CSE 374, Lecture 11: gdb

The stack

We talked about the layout of memory and the address space in an earlier lecture, but today we talked in more detail about the stack, which is where information about the currently running program/function and its associated variables. So if we have a function main() that calls a function foo() and then foo() calls bar(), our stack might look something like this:

    |        ...         |
    |--------------------|
    |                    |
    | variables and info |
    |     about bar      |
    |                    |
    |--------------------| --
    |                    |   |
    | variables and info |   |- "stack frame"
    |     about foo      |   |     for foo
    |                    |   |
    |--------------------| --
    |                    |
    | variables and info |
    |     about main     |
    |                    |
     --------------------
     "bottom" of the stack

For each function that is called, a new "frame" is pushed onto the stack to store information about the execution of that function. When the function returns, the frame is popped from the stack, resuming execution at the function below it (which called it).

Digging deeper, let's take a look at what exactly is stored in a stack frame:

     --------------------
    |                    |
    |       local        |
    |     variables      |
    |                    |
    |--------------------|
    | prev frame pointer |
    |--------------------|
    |   return address   |
    |--------------------|
    |                    |
    | function arguments |
    |                    |
     --------------------

We learned about the structure of the stack because we need to understand the stack in order to start debugging.

gdb

The goal of today is to learn some basic debugging survival skills. No matter how skilled you get at programming, your programs will inevitably have bugs and crash unexpectedly, so learning how to identify and fix bugs is very important. There are a few techniques that we mentioned for debugging crashes or problems in your code:

GDB is part of standard Linux tool chain. It supports several languages, including C, and we'll be using it on the command line as we run our C programs.

An example

I introduced a program called reverse.c, which would prompt the user for input, reverse the input string, and print it back to the user. But it crashes! Let's use GDB to debug the program. See the demo script linked on the course website for a log of the gdb commands we used. However, a summary is as follows.

Also linked in the "Links" section of the course website is a "gdb reference card", a very excellent resource for all things gdb if you ever forget a command.

    gdb reverse                # start gdb with the program reverse

    (gdb) break foo            # sets a breakpoint at the function foo - after you
                               # run the program, gdb will stop when it reaches foo

    (gdb) run                  # runs the program
    (gdb) backtrace            # prints out the functions that are on the stack
    (gdb) bt                   #   (abbreviation for backtrace)

    (gdb) up                   # examine the calling function's frame on the stack
    (gdb) down                 # examine the stack frame at the next lowest address

    (gdb) list                 # print the code that is currently executing
    (gdb) print s              # print the value of the variable s
    (gdb) p s                  #   (abbreviation for print)

    (gdb) info locals          # prints out all the local variables and their values
    (gdb) info args            # prints out all the arguments to the current function
                               # and their values

    (gdb) step                 # if the program is stopped, execute the next line of code
    (gdb) s                    #   (abbreviation for step)

Other key things to note about using gdb:

GDB tricks: