HP/DDE Debugger User's Guide: HP 9000 Series 700/800 Computers > Chapter 8 Debugging in Special SituationsDebugging Optimized Code |
|
HP/DDE supports debugging of code compiled at optimization levels 2 and below. The following is a brief description of the compiler optimization options that are compatible with debugging:
For more information about optimization levels, consult your compiler documentation. Ordinarily, you first compile and debug your program without optimization. All or nearly all of the bugs in your program will show up in the unoptimized version. After eliminating all the bugs that you can find, turn on optimization (compile with -O). If the program behaves incorrectly, scan the source code for the most common kinds of bugs that appear for the first time in optimized code:
These kinds of problems, however, are often very difficult to find by examining the source code. If you cannot determine the reason for the program's misbehavior, you need to debug the optimized code. This section provides background information on the differences between optimized code and unoptimized code. For tutorial and task-oriented information on how to debug optimized code using the debugger, see the online help. Source-level debugging of unoptimized code is relatively easy because there is a simple correspondence between source-code statements and the assembly-code instructions into which they are translated. Each statement is translated into a contiguous series of instructions, which are executed in sequence. The source and object code are isomorphic: they have essentially the same form. Also, program variables are stored in memory and are therefore easy to access. Optimization destroys isomorphism. Optimization is a series of transformations performed on the object code in order to make the program run faster. An optimized program performs the same tasks and produces the same results that the source code specifies. However, the order in which these tasks are performed and the way in which they are performed can change drastically. In effect, optimization transforms a program into a different program. The executable program you are debugging is actually not the same program as the source program. In addition, program variables are stored in registers instead of memory and are therefore more difficult to access. The following sections describe these problems in detail. Figure 8-4 “Unoptimized Code: Statement-to-Instruction Mapping ” shows how source-code statements map to object-code instructions in unoptimized code. Every instruction in the object code corresponds to a single statement in the source code. And, in general, every statement in the source corresponds to a sequential group of instructions in the object code. (There are a few exceptions; a loop, for example, may be broken up into two groups of instructions, one at the beginning and one at the end of the loop.) This means that even though it is the object code that is being executed and not the source code, a view of the source code in the debugger can still give you an accurate view of what the object code is doing. For example, when you step from statement 1 to statement 2, instructions 1 through 5 are executed in order, and the current location is now instruction 6, corresponding to statement 2. Figure 8-5 “Optimized Code: Statement-to-Instruction Mapping ” shows the several things that happen to source-code statements in optimized code.
When you debug unoptimized code,
The reason for this is that in unoptimized code, when you step from one statement to the next in the source code, what the debugger actually does is to step from one group of assembly instructions to the next; but because the assembly instruction groups correspond exactly to the source statements, it looks as if it is the source code itself that is executing. When you debug optimized code, however, this correspondence breaks down:
In fact, it isn't really meaningful to talk of the current location in the source program with optimized code—only of the instructions that are actually executing. In unoptimized code, the values of all program variables are kept in memory. Every time the value of a variable changes, the new value is stored in memory. This means that at any time, the debugger can determine the current value of a variable. In optimized code, the values of variables are kept in registers instead of memory as much as possible, because register access is much faster than memory access. In addition, some variables may be eliminated and replaced by constants. Therefore, it is much more difficult for the debugger to track the current value associated with a variable. Sometimes, too, there may be multiple copies of a variable—in loop optimizations, for instance. In such cases it is often impossible to obtain a meaningful value for the variable. |