Advanced GDB Debugging Techniques

Installing GDB

sudo apt-get install gdb

Common Debugging Commands

Command Name Command Abbreviation Command Description
run r Run the program
continue c Continue running the program
break b Set a breakpoint
tbreak tb Set a temporary breakpoint
delete del Delete a breakpoint
enable \ Enable a breakpoint
disable \ Disable a breakpoint
info \ View current breakpoints/threads and other debugging information
next n Run to the next line, step over
step s Step into the function if there is a function call
until u Run until a specified line is reached
backtrace bt View the call stack
frame f Switch to the specified stack frame of the current call thread
print p Print the value of a variable
list l View the source code
ptype \ View the variable type
thread \ Switch to the specified thread
finish fi End the current function call and return to the previous function
return \ End the current function call and return a specified value, returning to the previous function
jump j Jump to a specified line or address
disassemble dis View the assembly code
set args \ Set command line arguments
show args \ View the set command line arguments
watch \ Monitor whether the value of a variable or memory address changes
display \ Automatically output and monitor the variable or memory address when the program is interrupted
dir \ Redirect the location of source files
quit q Exit debugging

Example

main.cpp:

#include <cstdio>
#include <iostream>
#include <typeinfo>

namespace MathUtils {
    int add(int a, int b) 
    {
        std::cout << "add(int, int) called" << std::endl;
        std::cout << "a = " << a << ", b = " << b << std::endl;
        return a + b;
    }
    
template <typename T>
    T max(T a, T b) 
    {
        std::cout << "max " << typeid(T).name() << " called" << std::endl;
        return a > b ? a : b;
    }
    
    // Explicit template instantiation
    template int max<int>(int, int);
    template double max<double>(double, double);

    class Calculator {
    public:
        explicit Calculator(double init_value) : current_value(init_value) {}
        void add(double value) 
        {
            std::cout << "Calculator::add(double) called" << std::endl;
            current_value += value;
        }
        
        private:
            double current_value;
    };
}

using namespace MathUtils;

int main() 
{
    int a = 5;
    int b = 3;
    add(a, b);

    a = 10;
    b = 20;
    max(a, b);

    double c = 3.14;
    double d = 2.71;
    max(c, d);

    Calculator calc(10.0);
    calc.add(5.0);

    int val = 0;
    for(; val < 10; val++)
    {
        std::cout << val << std::endl;
    }

    return 0;
}

Compile:

g++ main.cpp -g -o main

Debug:

gdb main

Breakpoints

Notify GDB to pause execution at specific locations in the program.

Line Breakpoints

  • • break line
  • • break file:line

Function Breakpoints

  • • break function
  • • break file:functionSetting breakpoints in C++:
  • • break namespace::function // Namespace function
  • • break class::function // Class method
  • • break function(T, T) // Function template, specific type must be specified

Address Breakpoints

Set a breakpoint at a specified virtual address, used when the program does not have debugging information

  • • break *address

Conditional Breakpoints

Pause execution when specific conditions are met, basic syntax:

  • • break line if [condition expression]
  • • break function if [condition expression]Valid C condition statements can be used for conditional breakpoint expressions, which must evaluate to a boolean value:
  • • Equality, logical, and inequality operators (<, <=, >, >=, ==, !=, &&, ||, etc.), for example:break 18 if string==”hello” && i < 0
  • • Bitwise and shift operators (&, |, ^, <<, >>, etc.), for example:break 18 if (x & 0x1) == 1
  • • Arithmetic operators (+, -, *, /, etc.), for example:break 18 if (x + y) > 10
  • • Function calls, the function must be linked in the program:
    • • Personal function: break 18 if is_zero(x)
    • • Library function: break 18 if strlen(string) == 0
  • • Parentheses can be used to control precedence, for example:break 18 if (x > 0) && (y < 0)

Normal breakpoint conditions can be converted to conditional breakpoints, syntax format:condition [breakpoint number] [condition expression]Assuming there is a breakpoint 3

  • • Add condition, i==3:cond 3 i == 3
  • • Delete condition:cond 3

Example:

(gdb) break 42                   # Set line breakpoint
Breakpoint 1 at 0x400a2a: file main.cpp, line 42.
(gdb) break main.cpp:43          # Set file line breakpoint
Breakpoint 2 at 0x400a31: file main.cpp, line 43.
(gdb) break main                 # Set function breakpoint, same for other ordinary functions
Breakpoint 3 at 0x400a1b: file main.cpp, line 41.
(gdb) break MathUtils::add       # Set namespace function breakpoint
Breakpoint 4 at 0x4009a4: file main.cpp, line 8.
(gdb) break MathUtils::Calculator::add  # Set class method breakpoint
Breakpoint 5 at 0x400cad: file main.cpp, line 29.
(gdb) break MathUtils::max<int>(int, int) # Set function template breakpoint
Breakpoint 6 at 0x400bb2: file main.cpp, line 16.
(gdb) break 69 if val==5         # Set conditional breakpoint
No line 69 in the current file.
Make breakpoint pending on future shared library load? (y or [n]) N
(gdb) break 60 if val==5  
Breakpoint 7 at 0x400ae4: file main.cpp, line 60.
(gdb) info break                 # View breakpoint information
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400a2a in main() 
                                                   at main.cpp:42
2       breakpoint     keep y   0x0000000000400a31 in main() 
                                                   at main.cpp:43
3       breakpoint     keep y   0x0000000000400a1b in main() 
                                                   at main.cpp:41
4       breakpoint     keep y   0x00000000004009a4 in MathUtils::add(int, int) at main.cpp:8
5       breakpoint     keep y   0x0000000000400cad in MathUtils::Calculator::add(double) at main.cpp:29
6       breakpoint     keep y   0x0000000000400bb2 in MathUtils::max<int>(int, int) at main.cpp:16
7       breakpoint     keep y   0x0000000000400ae4 in main() 
                                                   at main.cpp:60
        stop only if val==5
(gdb) run
Starting program: /home/alientek/Desktop/test/gdb/main 

Breakpoint 3, main () at main.cpp:41  # Breakpoint 3 hit
41    {
(gdb) c
Continuing.

Breakpoint 1, main () at main.cpp:42  # Breakpoint 1 hit
42        int a = 5;
(gdb) c
Continuing.

Breakpoint 2, main () at main.cpp:43   # Breakpoint 2 hit
43        int b = 3;
(gdb) c
Continuing.

Breakpoint 4, MathUtils::add (a=5, b=3) at main.cpp:8   # Breakpoint 4 hit
8            std::cout << "add(int, int) called" << std::endl;
(gdb) c
Continuing.
add(int, int) called
a = 5, b = 3

Breakpoint 6, MathUtils::max<int> (a=10, b=20) at main.cpp:16   # Breakpoint 6 hit
16            std::cout << "max " << typeid(T).name() << " called" << std::endl;
(gdb) c
Continuing.
max i called
max d called

Breakpoint 5, MathUtils::Calculator::add (this=0x7fffffffdca0, value=5)
    at main.cpp:29
29                std::cout << "Calculator::add(double) called" << std::endl;
(gdb) c
Continuing.
Calculator::add(double) called
0
1
2
3
4

Breakpoint 7, main () at main.cpp:60    #  Breakpoint 7 hit
60            std::cout << val << std::endl;
(gdb) c
Continuing.
5
6
7
8
9
[Inferior 1 (process 6956) exited normally] # Program exited normally

Breakpoint Commands

Execute a series of commands at breakpoints

  • • commands [breakpoint number][command1][command2]end

Set a breakpoint at line 50 in main.cpp:

(gdb) break main.cpp:44
Breakpoint 8 at 0x400a38: file main.cpp, line 44.
(gdb) info break
Num     Type           Disp Enb Address            What
8       breakpoint     keep y   0x0000000000400a38 in main() 

Example 1, automatically print variable values:

(gdb) commands 8
Type commands for breakpoint(s) 8, one per line.
End with a line saying just "end".
>printf "a = %d, b = %d\n", a, b
>end
(gdb) run
Starting program: /home/alientek/Desktop/test/gdb/main 

Breakpoint 8, main () at main.cpp:44
44        add(a, b);
a = 5, b = 3

Example 2, modify the value of variable a:

(gdb) commands 8
Type commands for breakpoint(s) 8, one per line.
End with a line saying just "end".
>set variable a = 0
>continue
>end
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/alientek/Desktop/test/gdb/main 

Breakpoint 8, main () at main.cpp:44
44        add(a, b);
add(int, int) called
a = 0, b = 3 # The value of a has been modified

Check the breakpoint information, you can see that breakpoint 8 contains the commands added above:

(gdb) info break
Num     Type           Disp Enb Address            What
8       breakpoint     keep y   0x0000000000400a38 in main() 
                                                   at main.cpp:44
        breakpoint already hit 1 time
        set variable a = 0
        continue

Example 3, print the call stack:

(gdb) delete 8              # Delete breakpoint 8
(gdb) info break
No breakpoints or watchpoints.
(gdb) break main.cpp:46     # Set a breakpoint at line 46 in main.cpp, breakpoint number 9
Breakpoint 9 at 0x400a47: file main.cpp, line 46.
(gdb) commands 9            # Add commands for breakpoint 9
Type commands for breakpoint(s) 9, one per line.
End with a line saying just "end".
>silent                     # Silent mode, do not print breakpoint information
>backtrace                  # Print the call stack
>info locals                # Print local variables
>end                        
(gdb) run                   # Run the program
Starting program: /home/alientek/Desktop/test/gdb/main 
add(int, int) called
a = 5, b = 3
#0  main () at main.cpp:46    # Call stack
a = 5                         # Local variable    
b = 3                         # Local variable
c = 2.0733524115605412e-317   # Local variable
d = 6.9533558074002266e-310   # Local variable
calc = {current_value = 2.07389786003355e-317}  # Local variable
val = 0                       # Local variable

Example 4, use GDB’s define command to create a macro to print the values of variables a and b:

(gdb) clear main.cpp:46         # Delete breakpoint at line 46 in main.cpp, breakpoint number 9
Deleted breakpoint 9
(gdb) break main.cpp:44         # Set a breakpoint at line 44 in main.cpp
Breakpoint 1 at 0x400abc: file main.cpp, line 51.
(gdb) info break                # View breakpoint information
Num     Type           Disp Enb Address            What
10      breakpoint     keep y   0x0000000000400a38 in main() 
                                                   at main.cpp:44
(gdb) define printf_a_b         # Define macro
Type commands for definition of "printf_a_b".
End with a line saying just "end".
>printf$arg0, $arg1, $arg2     # Equivalent to printf "formatted string", variable a, variable b, 
                                # here $arg0, $arg1, $arg2 are macro parameters, supporting up to 10 parameters
>end
(gdb) command 10                 # Add commands for breakpoint 10
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>silent
>printf_a_b "breakpoint1 a = %d, b = %d\n" a b # Use macro to print the values of variables a and b
>end
(gdb) run
Starting program: /home/alientek/Desktop/test/gdb/main 
breakpoint1 a = 5, b = 3                     # Output values of a and b

Deleting Breakpoints

In the previous examples, there have been multiple commands to delete breakpoints, summarized as follows:

  • • delete # Delete all breakpoints
  • • delete [breakpoint number] # Delete specified breakpoint
  • • clear [location] # Delete breakpoint at specified location, location can be: line number, function name, filename:line number, filename:function name, address
  • • disable [breakpoint number] # Disable specified breakpoint
  • • enable [breakpoint number] # Enable specified breakpoint

Resuming Execution

continue

continue abbreviated as c, has been attempted in the above examples.This command allows GDB to resume program execution until the next breakpoint is triggered or the program ends.

step and next for single-step debugging

step abbreviated as s, allows GDB to enter the function and execute line by line, entering the function if there is a function call.next abbreviated as n, allows GDB to execute line by line, not entering the function if there is a function call, stopping at the first statement after the function call.step example, pause at line 44, and enter the add function, executing line by line:

(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400a38 in main() 
                                                   at main.cpp:44
(gdb) run
Starting program: /home/alientek/Desktop/test/gdb/main 

Breakpoint 1, main () at main.cpp:44
44        add(a, b);
(gdb) s
MathUtils::add (a=5, b=3) at main.cpp:8
8            std::cout << "add(int, int) called" << std::endl;
(gdb) s
add(int, int) called
9            std::cout << "a = " << a << ", b = " << b << std::endl;
(gdb) s
a = 5, b = 3
10            return a + b;
(gdb) s
11        }
(gdb) s
main () at main.cpp:46
46        a = 10;

next example, pause at line 44, treating the add function as a single line of code, executing line by line:

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/alientek/Desktop/test/gdb/main 

Breakpoint 1, main () at main.cpp:44
44        add(a, b);
(gdb) n
add(int, int) called
a = 5, b = 3
46        a = 10;

finish

finish abbreviated as fin, allows GDB to execute the current function and return to the place where the function was called.Example:

(gdb) break main.cpp:44                             # Set a breakpoint at line 44 in main.cpp
Breakpoint 1 at 0x400a38: file main.cpp, line 44.
(gdb) r
Starting program: /home/alientek/Desktop/test/gdb/main 

Breakpoint 1, main () at main.cpp:44                 # Pause at line 44
44        add(a, b);
(gdb) step                                          # Step into the add function
MathUtils::add (a=5, b=3) at main.cpp:8
8            std::cout << "add(int, int) called" << std::endl;
(gdb) finish                                        # Execute finish command, complete the add function, and return to the main function
Run till exit from #0  MathUtils::add (a=5, b=3) at main.cpp:8
add(int, int) called
a = 5, b = 3
main () at main.cpp:46
46        a = 10;
Value returned is $1 = 8

until

until abbreviated as u, is usually used to exit loops or skip the remaining part of the current function.Example:

(gdb) break main.cpp:57       # Set a breakpoint at line 57 in main.cpp
Breakpoint 1 at 0x400ad7: file main.cpp, line 57.
(gdb) run
Starting program: /home/alientek/Desktop/test/gdb/main 
add(int, int) called
a = 5, b = 3
max i called
max d called
Calculator::add(double) called

Breakpoint 1, main () at main.cpp:57     # Pause at line 57
57        int val = 0;
(gdb) next                               # Execute line by line
58        for(; val < 10; val++)
(gdb) next
60            std::cout << val << std::endl;
(gdb) next
0
58        for(; val < 10; val++)
(gdb) print val                          # Print the value of variable val
$1 = 0
(gdb) until                              # Execute until command
1
2
3
4
5
6
7
8
9
63        return 0;                     # Reached the next line of source code outside the loop
(gdb) next
64    }(gdb) 
__libc_start_main (main=0x400a13 <main()>, argc=1, argv=0x7fffffffdda8, init=<optimized out>, 
    fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdd98) at ../csu/libc-start.c:325
325    ../csu/libc-start.c: No such file or directory.
(gdb) 
[Inferior 1 (process 6689) exited normally]  # Program exited normally 

Note that when using until, if there are breakpoints inside the loop, it cannot exit the loop.

References

  • 【The Art of Software Debugging (Matt Lavoie et al.)】. Translated by Zhang Yun.
  • I hope you enjoyed this article. You can follow the public account for more programming knowledge to be shared in the future.

Leave a Comment