CpS 450 Language Translation Systems

Code Generation - Debugging

In addition to debugging the low-level assembly, compilers can include debugging directives in your generated assembly code that will allow users to use gdb to step through a source program and view the values of variables from the perspective of the source code.

To enable source-level debugging, you must:

  • Emit a boilerplate set of debugging directives at the top of the program that tells the debugger where to find the source file
  • Emit a debugging directive for each Dream method
  • Emit a debugging directive for each Dream statement
  • Emit a simple debugging directive for each global variable in your program (optional)

The details are below. After doing this, assemble your compiler’s output using gcc without the -g switch, and the debugging information will be placed in the executable for the debugger to use.

To get started, have a look at files in examples/codegen/debugdemo:

  • demo.dream - a source Dream program
  • demo_sourcedebug.s - an assembly file generated from it that includes STABS-format debugging directives

See the makedemo.sh script for commands to assemble and link.

Debugging Header Info

Consider:

        .file   "demo.dream"
        .stabs  "demo.dream",100,0,0,.Ltext0
        .text
.Ltext0:
        .stabs  "int:t(0,1)=r(0,1);-2147483648;2147483647;",128,0,0,0

This “debugging header” ties the demo.s assembly file back to “demo.dream” source file. Your compiler should emit these lines at the top of your generated assembly file, substituting the name of the source .dream file in place of “demo.dream”.

Variable Debug Info

Look at this declaration of the global x variable:

.comm   x,4,4
.stabs  "x:G(0,1)",32,0,0,0

The .comm directive is followed by a .stabs directive designating that x is a global int-type variable. By emitting this line, you give the debugger the information it needs to be able to show you the value of the variable when you hover the mouse pointer over the variable in the source code, or you add a watch on it, etc.

Note that you must not prefix your variable names with an underscore if you want nice integration with your debugger.

When you get around to implementing local variables, the debugging directive is a little different. Try compiling a C program with -gstabs -S flags and looking at the assembly output to see how to get debugger support for local variables.

Method Debug Info

For each method in your Dream program, emit a debug directive, like this:

# Line 5: start()
.global main
        .stabs  "main:F",36,0,0,main
main:

Insert the debug directives in between the .global and the label for the function. This example shows how to do it for the start() method (which, in Phase 4, corresponds to the main() function).

Statement Debug Info

Consider the following line in the original source file:

x := 5

Here’s the generated assembly code, including the debug directive:

# Line 7: x := 5
        .stabn 68,0,7,.line7-main
.line7:
        pushl   $5
        ...

For each statement in your Dream source program, emit the following:

  • A comment with the source text (this is not necessary for the debugger, but it is necessary for humans looking at the generated assembler, and a requirement for your compiler):
    # Line 7: x := 5
    
  • A .stabn directive, of this form:
    .stabn 68,0,<line#>,.line<line#>-main
    

    This directive tells the debugger which assembler instructions go with which source line. Example:

    .stabn 68,0,7,.line7-main
    

    Note: If you’re emitting code for some function other than main, you must use that function’s name instead of main. For example, if you’re emitting code for a method Point_setX, you would emit a line like this:

    .stabn 68,0,7,.line7-Point_setX
    
  • A label for the line:
    .line7:
    
  • The assembly code for the statement

Debugging Dream with gdb

Now that you’ve done this work, invoke gcc without the -g switch:

gcc -m32 -g -c stdlib.c
gcc -m32 -c demo.s stdlib.o -o demo

Then start gdb:

gdb demo

See also