Skip to main content

Short-list of useful gdb commands

  • break main.c:12 - set a breakpoint on line 12 of file main.c (you can set a breakpoint in any c or h file)
 (gdb) break <function>
 (gdb) break <linenumber>
 (gdb) break <filename:function>
 (gdb) break <filename:linenumber>
# To delete a breakpoint, use one of the following:
 (gdb) delete <breakpoint number>
  • run - runs your program until a breakpoint or crash is encountered. You can also pause your program by pressing Contrl-C (useful for finding infinite loops). When one of these is encountered, you will be able to inspect the state of your program with any of the following commands.

  • run args sets the arguments of your program (i.e., arguments the main function).

  • continue - continue running your program until another breakpoint is encountered.

  • next - execute one line of the current function and then stop

  • step - execute a single line of code and then stops. Think of step as meaning "step into". If you are about to execute the line of code printf("hello world\n"); next will execute the entire printf function and stop at the line after printf in the file you are looking at. step will try to step into the printf function and excute the first line of code it finds there. If you only "step" through your code, you may find yourself deep inside a library very quickly. And chances are the bug you are looking for is not in the library! Also, if your library was not compiled with debug information, execution proceeds until control reaches a function that does have debugging information, which may result in unpredicatble behavior. Please see this online reference or one like it for full details.

  • backtrace - displays the call stack for your program.

  • frame 4 - moves to a specific frame on the call stack.

  • print x - prints the value of expression or variable x.

  • quit - close GDB.

After starting GDB, the first thing you'll want to do is place a breakpoint. Type break segfault to place a breakpoint at the beginning of the segfault() function. Then type run to start your program. Soon you will find that execution comes grinding to a halt. Type backtrace to see the call stack. You can now use print to view the values of variables. (Go ahead and type print a, which prints a pointer, which prints a particular field in the struct that a points to, and print *a, which dereferences the pointer and prints the entire structure it points to.) Pay special attention to the contents of the arraylist. You can continue execution by typing continue. Once you have a theory of what might be going wrong, you'll want to run your program again. This time before doing so, set a different breakpoint in the code where you think the issue might be. Again inspect the program state to see if you've tracked down the problem. If not, try somewhere else in the program and repeat.

  • Examining memory. Examining memory and registers You can use the command x to examine the contents of a memory address. For example, to display instructions at address 0x8048422, you can call:
   (gdb) x/4i 0x8048422
   0x8048422 : mov $0x0,%eax
   0x8048427 : leave
   0x8048428 : ret
   0x8048429 : push %ebp

Here, /4i is an optional parameter, indicating that GDB should display 4 lines of machine instruction. Other formats are available, such as x/d for decimal, x/x for hex, and x/s for strings. Use the & operator to find the address of variable mystr in the function main. Now, use the x/s operator and format command to diplay the contents of this address as a null-terminated string.

Useful gdb options

  • Load .gdbinit file to avoid repeating work. Sometimes you made tiny modification and need to rerun GDB, all breakpoints should be same. If you manually set all these breakpoints again and again, it’s not only time-wasting but also tiresome.

  • The format of condition breakpoint is:

breakpoint WHERE if CONDITION

The breakpoint is triggered only when a particular condition is satisfied.

GDB - Init File

Often it is tedious to debug complex data structures. Debugging a linked list often requires printing a node by dereferencing the pointer to its struct, then accessing the next node and doing the same to it. This can become tedious and messy, resulting in commands like: :

    (gdb) print *(curr)
    (gdb) print *(curr->next)
    (gdb) print *(curr->next->next)
    ...

However, a command to print out a linked list can be created and placed inside the .gdbinit file.

The user initialization file contains commands that are executed upon the startup of GDB. It is located in your home directory under the path:

    ~/.gdbinit

Create/edit this file and add the following line to this file: :

    set auto-load safe-path /

This allows our programs to use a current directory initialization file, i.e. you can create a .gdbinit for each project you're debugging.

Current Dicetory Initialization File

The current directory initialization file contains commands to be executed upon the startup of GDB within the current directory.

> \~/\<file\_path\>/.gdbinit

Create/edit this file to contain any startup behaviour and user defined commands. Use either standard gdb commands or the GDB scripting syntax (which is described below).

.gdbinit files can be used to always execute commands when you run GDB in a particular directory. For example, if you always want to break at a certain function (for those doing OS, this might be the panic() function) then you can place the following in the file: :

    break <function_name>

Define a command

You can create a user defined command in the .gdbinit file: :

    define <command>
        <code>
    end

Document a command:

You can document a command in the .gdbinit file: :

    document <command>
        <information about the command>
    end

This information appears when the help feature is used: :

    (gdb) help <command>
    <information about the command>

Parameters

When a user defined command is called in GBD, arguments can be passed in: :

    (gdb) <command> <arg0> <arg1> <arg2> ...

The number of arguments, and the arguments themselves can be referenced inside the command definition using the following variables: :

    $argc
    $arg0
    $arg1
    $arg2
    ...

Convenience Variables

Convenience variables are used to store variables as your script runs. They are specified by putting a '$' in front of a name (note there are some reserved names, such as $1): :

    $<variable_name>

Note, gdb scripting uses one flat namespace - which makes recursion difficult. To use recursion you must use unique names for each of the variables in each stack frame. This can be generated using a name base, plus the value of another variable, separated by an underscore: :

    $<name_base>_$<other_variable>

Setting a variable

You can set the value of a variable using the set command: :

    set $<variable_name> = <value_or_expression>

If statements

If statements can be used to conditionally execute GDB commands/ scripts: :


    if <condition>
        <code>
    else
        <code>
    end

While Loops

While loops can be used to repeat a section of code for as long as a condition is true: :

    while <condition>
        <code>
    end

Printing

Printing in GDB scripting is very similar to printing in c: :

    printf "<format string>", <arg0>, <arg1>, ...

Using a User Defined Command

User defined commands are used in the same way as non-user defined commands (such as print). Within gdb, the command name is specified followed by any arguments the command definition requires :

(gdb) <command> <arg0> <arg1> <arg2> ...

Break on a given function every time

Students will want to always break on panic() so that when there is an error, the program halts and gdb can be used. There are several things that are required in the .gdbinit file for OS, one of which being a break on panic.

Place the following in the ~/.gdbinit file. This lets GDB use the commands in ./~gdbinit. :

    set auto-load safe-path /

Place the following in GDB in the ./~gdbinit file. This breaks when the panic function is called. :

    break panic

Having these two files is effectively like running break panic when GDB is first started. GDB can then be used as normal, and when panic() is called, the execution is automatically stopped.

Debugging Linked lists

Learning Outcome

  • Able to add basic commands to a .gdbinit file as default commands (e.g. breakpoints) to avoid entering them every debug session.

  • Able to write advanced user-defined GDB commands, and use them to debug more complex data structures including linked lists and binary trees.

There are two main ways to debug code containing linked lists in GDB. You can manually go through and print each node in the linked list (which can become tedious), or you can write a script to do it for you. In this example, we will learn how to write a GDB script to traverse the linked list given in linked_list.c.

//Makes a linked list of length 7 and prints it out
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>


struct node {
    int data;
    struct node *next;
};

struct node *create_node(int data);
struct node *create_list(int length);
void print_list(struct node *list);

int main(void){
    struct node *list1 = create_list(7);
    print_list(list1);

    return 0;
}

struct node *create_node(int data){
    struct node *new = malloc(sizeof(struct node));
    assert(new != NULL);
    new->data = data;
    new->next = NULL;
    return new;
}

struct node *create_list(int length) {

    struct node *head = NULL;
    if (length > 0) {
        head = create_node(0);
        int i = 1;
        struct node *curr = head;
        while (i < length) {
            curr->next = create_node(i);
            curr = curr->next;
            i++;
        }
    }
    return head;
}

void print_list(struct node *list){
    struct node *curr = list;

    while (curr != NULL) {
        printf("%d->", curr->data);
        curr = curr->next;
    }
    printf("X\n");
}

Place this in $PWD/.gdbinit`. Same folder as where you placed the linked_list.c

# Gdb init
define p_generic_list
  set var $n = $arg0
  while $n
    print *($n)
    set var $n = $n->next
  end
end

document p_generic_list
        p_generic_list LIST_HEAD_POINTER
        Print all the fields of the nodes in the linked list pointed to by LIST_HEAD_POINTER. Assumes there is a next field in the struct.
end



define indentby
    printf "\n"
    set $i_$arg0 = $arg0
    while $i_$arg0 > 10
        set $i_$arg0 = $i_$arg0 - 1
        printf "%c", ' '
    end
end

Place the following in the ~/.gdbinit file (~/: Home directory):

    set auto-load safe-path /

Place the following script to print out a linked list in GDB in the $REPO/.gdbinit file:

.gdbinit.txt

This script is the same as a C program to print out a linked list, excluding a few key syntax differences.

We want our script to traverse the list given in the first argument to the command ($arg0). So we create a convenience variable to store our current pointer and set it to $arg0: :

set var $n = $arg0

We might want to change our node structure later on, so we create a command that will print out every field in the node, regardless of what it is. We do this by using the gdb command print on the dereferenced nodes (we could use printf, but we would have to individually specify each field). :

print *($n)

The only part of the linked list node that we assume will always be present is the next pointer, which we use to move through our program in the line : :

set var $n = $n->next

When our p_generic_list command is run in gdb, it prints out all of the nodes until it reaches a NULL: :

    $ gcc -g -o linked_list linked_list.c
    $ gdb -q ./linked_list
    (gdb) br 18
    Breakpoint 1 at 0x40061c: file linked_list.c, line 18.
    (gdb) r
    Starting program: /mnt/d/code/debugging/modules/linked_list
    Breakpoint 1, main () at linked_list.c:18
    18          print_list(list1);
    (gdb) p_generic_list list1
    $1 = {data = 0, next = 0x602030}
    $2 = {data = 1, next = 0x602050}
    $3 = {data = 2, next = 0x602070}
    $4 = {data = 3, next = 0x602090}
    $5 = {data = 4, next = 0x6020b0}
    $6 = {data = 5, next = 0x6020d0}
    $7 = {data = 6, next = 0x0}
    (gdb)

Acknowledgements

This page has been modified by Arrvindh Shriraman and Liz Whiller.