Originally posted by: rauls
Those of us who develop C++ applications for a living, often hit a dilemma when analyzing functional errors. Should I debug my application using a symbolic debugger, or should I just add prints and traces to the code to understand its behavior?
Too often developers choose the latter, slowing down the application development cycle. Not only must they recompile parts of the application to pick up these changes, but they need to know ahead of time what data to examine, which is often hard to predict in advance. This effectively creates a long cycle of instrument-compile-test iterations that is not very productive. Also, in some cases tracing is not an effective solution, for example for long running programs that might generate large unmanageable traces. In some cases the traces themselves may interfere with the behavior of the application, for example when the developer is dealing with memory corruption or access to uninitialized storage.
Is there a better way?
One of the common reasons for developers to forego symbolic debugging is that their regular application build uses compiler optimization, which effectively hides much of the program state to the debugger. Trying to debug an application where breakpoints don't work consistently and user variables can't be easily and reliably examined creates enough frustration to steer the developer towards the tracing approach.
What if there was a new way to tackle this problem? A way to allow compiler optimization to occur while still providing an effective experience through the symbolic debugger? Such solutions have been explored in the past but have never been able to achieve a good balance between runtime performance and debug experience. That is, until the new XL C/C++ v12.1 compiler just released by IBM for the Power-based AIX and Linux on Power platforms.
These compilers provide a mechanism to allow effective application debugging through a symbolic debugger with minimal impact on the runtime performance of the application. This gives developers the option of debugging the application from their production builds without the need to recompile. It allows developers to run the program at almost the same speed as a non-debug-enabled binary. The way this mechanism works is that the visibility of the user variables to the symbolic debugger is modeled in the same way as any other access to these variables, causing optimizations to maintain the values of all user variables in the program that can be examined through the debugger.
This allows the programmer to set breakpoints and step through the program with a similar behavior as if it had been unoptimized. The advanced optimization of the XL C/C++ compilers combined with the out-of-order execution of the POWER7 microprocessor allows the program to run at close to the same speed as the traditionally non-debug-enabled program. ]Also, the balance between runtime performance and debug capability can be finely controlled by the developer. Options are provided to fine-tune this balance based on the needs of the development team. This control is provided with the -gXXX option, where XXX is a number from 0 to 9 representing the “level” of debugging support being targeted. In the absence of optimization, all these levels provide full debug support, but in combination with optimization options, the levels provide specific guarantees for the debug experience. Some of the highlight levels are:
-g9: Ensures program variables can be examined and modified at any point of the program. Modifications to the program variables in the debugger are fully supported and will be reflected on the behavior of the application. This option may have a significant runtime performance cost in some situations.
-g8: Ensures program variables can be examined at any point of the program. Modifications to the program variables in the debugger are not supported, and may be either ignored or may cause the program to behave erratically. This is the recommended option for providing a good balance of runtime performance and debug capability
-g2: Provides visibility of some program variables at some points of the program. This has minimal impact into the runtime performance of the application but it provides an incomplete debug experience, similar to what is available with competitive compilers.
For many application development teams, compiling with options -O2 -g8 may provide the best balance of runtime performance and debug experience. This combination may be suitable for a single build that can be used both in a production environment and during the application development process. For applications that are to be shipped to third parties, it is possible on AIX to split the debug information into a separate file, through the -bstabsplit linker option. That allows the development team to reserve the capability to debug the binary being shipped, while preventing the end user from peeking into the internals of the application.
This is only one of the major features being introduced into the XL C/C++ compilers recently, but I’m very confident that it will become an essential component of the development process for many products. You can read more about the XL C/C++ compiler at http://www.ibm.com/software/awdtools/xlcpp/aix/, including download links to the product trials.
Please add a comment below if you have had a chance to try this feature, if you have questions about how it behaves, or if you would like to let IBM know what you think of this feature.