Fundamentals 20 min read

Comprehensive Guide to Using GDB for Debugging C/C++ Programs

This article provides a thorough introduction to the GNU Debugger (GDB), covering its core capabilities, startup methods, compilation requirements, a wide range of debugging techniques—including breakpoints, memory inspection, disassembly, reverse execution—and detailed guidance for multithreaded debugging on Linux systems.

Deepin Linux
Deepin Linux
Deepin Linux
Comprehensive Guide to Using GDB for Debugging C/C++ Programs

In the complex world of software development, efficient debugging tools are essential; this guide explores the powerful GNU Debugger (GDB) and how it helps developers inspect program execution, locate bugs, and optimize performance.

1. What GDB Does

GDB performs four main tasks: setting variables or conditions before program start, pausing execution at specified locations or conditions, examining program state when it stops, and modifying variables or conditions during execution to test bug fixes.

2. Starting GDB

There are two primary ways to launch GDB:

Direct start: gdb (then use file or exec-file to load the target program), gdb test.out , or gdb test.out core for core analysis.

Dynamic attachment: gdb test.out pid attaches to a running process identified by its PID (obtainable via ps aux ).

Compile the program with gcc -g to embed debugging symbols, ensuring GDB can locate source files (e.g., gcc -g main.c -o test.out ).

3. GDB Debugging Techniques

Conditional breakpoints : Use break 666 if testsize==100123123 to stop only when a condition is met.

Breakpoint commands : Attach scripts or actions to breakpoints for advanced debugging.

Memory dump : The x command (e.g., x/16xw 0x7FFFFFFFE0F8 ) displays memory in various formats; format specifiers include x , d , u , o , t , i , c , and f . Unit size specifiers are b (byte), h (half‑word), w (word), g (giant‑word).

Inline disassembly : disassemble or disas main shows the assembly corresponding to source functions.

Reverse debugging : Commands like reverse-continue (rc) and reverse-step (rs) let you run the program backward to re‑examine previous states (requires GDB 7.0+).

4. Debugging Workflow

Compile with debugging symbols : gcc -g main.c -o main.out .

Start debugging : gdb main.out , then run (optionally with arguments). To attach to a running process, use gdb -p PID or gdb attach PID .

Common commands :

list – view source code around the current line.

break – set breakpoints (by line, function, file:line, or condition).

next ( n ) – step over a line.

step ( s ) – step into a line.

continue ( c ) – resume execution.

print / display – show variable values (formats: /x , /d , /u , etc.).

watch , rwatch , awatch – monitor variable changes.

Segmentation fault debugging : Generate a core file with ulimit -c unlimited , then load it via gdb program core . Use backtrace , frame , and print to locate the fault.

5. Additional GDB Features

Parameter commands : info registers , info stack , info args , info locals , info function , info breakpoints , info watchpoints , info line .

Memory examination : x/<n/f/u> <addr> with format and unit specifiers as described above.

Source listing : list , list 12 , list function_name .

Stack frame commands : info frame , up , down , info locals .

6. Multithreaded Debugging with GDB

Compile multithreaded programs with gcc -g -pthread -o multithread multithread.c . Example program:

#include <stdio.h>
#include <pthread.h>
#define NUM_THREADS 5
void * thread_func(void * thread_id) {
    long tid = (long)thread_id;
    printf("Hello World! It's me, thread #%ld!", tid);
    pthread_exit(NULL);
}
int main() {
    pthread_t threads[NUM_THREADS];
    int rc; long t;
    for (t = 0; t < NUM_THREADS; t++) {
        printf("In main: creating thread %ld", t);
        rc = pthread_create(&threads[t], NULL, thread_func, (void *)t);
        if (rc) { printf("ERROR; return code from pthread_create() is %d", rc); return -1; }
    }
    pthread_exit(NULL);
}

Key multithread commands:

info threads – list all threads.

thread THREAD_ID – switch context.

break function_name – set breakpoints in synchronization functions.

thread apply all bt – show backtraces for all threads.

set scheduler-locking on|step|off – control which threads run during step operations.

Common issues such as deadlocks can be diagnosed by pausing the program ( Ctrl‑C ), inspecting thread states with info threads , and examining stack traces with bt . Use tools like valgrind for memory‑related problems.

debuggingLinuxMultithreadingcommand-lineC programmingGDB
Deepin Linux
Written by

Deepin Linux

Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.