The challenge of debugging optimized code
Debugging optimized code has always been a challenging task because it could make the program state not available to debuggers. Optimization can change the sequence of operations, add or remove code, change variable data locations, and perform other transformations that make it difficult to associate the generated code with the original source statements. For example, if a variable is used only in computing another expression, the compiler can chose to generate the value of the final expression directly, without explicitly generating the variable value at any point. This can inhibit debug usability because a variable that a programmer is expecting to see is no longer available in the optimized program.
Current debug support in IBM XL C/C++ and XL Fortran
IBM® XL C/C++ and XL Fortran provide a set of mechanisms for debug support:
- Compiler option –g generates debug information for use by a symbolic debugger. Although it provides full debug support at no-opt, it doesn't really work well with the optimized code. As we described in the previous section, the program state is not captured due to optimization.
- Compiler option –qkeepparm targets the procedure parameters in particular. It ensures that the initial values of procedure parameters are available to the debugger even during optimization. This has a small impact on execution performance. The -qkeepparm option then provides access to the values of incoming parameters to debuggers by simply preserving those values on the stack
- Compiler option -qoptdebug creates a modified source code that matches the optimized code after some high-level transformations have been done. This pseudo code maps more closely to the instructions and values of an optimized program than the original source code. When a program compiled with this option is loaded into a debugger, you will be debugging the pseudo code rather than your original source code.
Enhancements for debugging optimized code
Debug option –g is enhanced to control the debugging support and to make debugging optimized code easier, specifically with –O2. When the –O2 optimization level is in effect the debug capability is completely supported. When an optimization level is higher than –O2 is in effect the debug capability is limited. The debug option –g has levels that range from 0 to 9.
- -g0: No debug information is generated. No program state is preserved (best performance).
- -g1: Generates debug information about line numbers and source file names. No program state is preserved.
- -g2: Generates debug information about types, line numbers, source file names and view-only debug information about variables. No program state is guaranteed when the code is optimized.
- -g3, -g4: Same behavior as –g2 plus procedure parameter values are available to the debugger at the beginning of each procedure.
- -g5, -g6, -g7: Same behavior as –g3 and –g4 plus program state is available to the debugger at the following selected language constructs at –O2.
- Before and after loops
- Before and after conditional branching statements
- Before and after function calls as well as the first executable statement of a function.
- -g8: Same behavior as –g3 and –g4 plus program state is available to the debugger at beginning of every executable statement at –O2.
- -g9: Same behavior as –g8 plus users can modify a variable in the debugger at –O2. Being able to modify a variable in the debugger is also the default behavior of debugging no-opt code (best debug support).
The debug support for optimized code is enhanced at –g5 and above, where the program state is made available at –O2. For example, when debugging the following test case at –g8:
T1 = X * Y - 1; T2 = X * Y * 3; Q = T1 + T2;
At –O2 without the debug support, the compiler might generate the following code, where GRX and GRY are registers:
GRX = X * Y; GRY = GRX << 2; Q = GRY – 1;
Because the program never calculates the value of T1 and T2, the debugger might not display the correct values of T1 and T2.
At –O2 –g8, the compiler generates code like this:
GRX = X * Y; GRY = GRX << 2; T1 = GRY – 1; T2 – GRY – GRX; Q = GRY – 1;
The program stores the current values of T1 and T2 back to the memory so they are available to the debugger.
Debugging an inlined function
Function inlining is one of the common optimization techniques. It also causes much trouble for debugging such a function. The debugger does not even recognize that it is an inlined function, because only the line number entries are generated. Formal parameters and local variables of the inlined function might not be available to the debuggers after the inlining.
As an improvement of debugging support, the compiler makes the inlined function scope explicit so that users can step through the function body. Users can also examine the formal parameters and the local variables of the inlined function. However, currently, users are not able to "stop in function" and get debuggers to find any inlined instances for the inlined functions.
Even though the enhancement of the debug ability negatively impacts the performance (because preserving the program state eliminates some optimization opportunities), the compilers still generate optimized code at –O2 that is fully debuggable. As we measured at –g8 –O2, we can get in average 80% of the –O2 execution time at a 14% increase of the compile time. Of course, the degradation of the execution and the compile time are heavily dependent on the applications.
To limit the performance impact, you can also try with different –g levels. For example, because loops are where optimization techniques are heavily applied, –g5 is designed to preserve only the program state before and after computational loops so that the debug information does not interfere with the optimization algorithms applied to the loop body.
The support of debugging optimized code has been improved. It now allows you to scale between debug ability and the performance. Currently, only optimization done at level –O2 is with full debug support. We are continuing work on the improvement of debug support for the optimization done for higher optimization levels.
- Find out more about IBM XL Fortran for AIX and Linux:
- Find out more about XL C/C++ for AIX and Linux:
- Visit the Rational software area on developerWorks for technical resources and best practices for Rational Software Delivery Platform products.
- Subscribe to the developerWorks weekly email newsletter, and choose the topics to follow.
- Stay current with developerWorks technical events and webcasts focused on a variety of IBM products and IT industry topics.
- Attend a free developerWorks Live! briefing to get up-to-speed quickly on IBM products and tools, as well as IT industry trends.
- Watch developerWorks on-demand demos, ranging from product installation and setup demos for beginners to advanced functionality for experienced developers.
Get products and technologies
- Get the free trial download for XL Fortran for IBM AIX.
- Get the free trial download for XL Fortran for Linux.
- Get the free trial download for XL C/C++ for AIX.
- Get the free trial download for XL C/C++ for Linux.
- Try XL C/C++ for IBM AIX in the Enterprise Modernization Sandbox.
- Try XL Fortran for IBM AIX in the Enterprise Modernization Sandbox
- Download a free trial version of Rational software.
- Evaluate other IBM software in the way that suits you best: Download it for a trial, try it online, use it in a cloud environment, or spend a few hours in the SOA Sandbox learning how to implement service-oriented architecture efficiently.
- Get connected. Join the Rational Fortran Café community. Join the Rational software forums to ask questions and participate in discussions.
- Get connected. Join the Rational C/C++ Café community.
- Ask and answer questions and increase your expertise when you get involved in the Rational forums, cafés, and wikis.
- Join the Rational community to share your Rational software expertise and get connected with your peers.
- Rate or review Rational software. It's quick and easy.