Skip to content

GDB: Debugging C, C++ and Rust Programs

DodaTech Updated 2026-06-22 7 min read

In this tutorial, you'll learn GDB debugging including breakpoints, watchpoints, backtrace, memory inspection, conditional breakpoints, and debugging multi-threaded programs in C, C++, and Rust.

Why GDB Matters

Printf debugging works for simple programs, but fails when bugs are subtle, rare, or involve complex state. GDB lets you pause execution, inspect variables, step through code line by line, and examine memory. It is the standard debugger on Unix systems and supports C, C++, Rust, and many other compiled languages.

By the end of this guide, you will debug programs with breakpoints, watchpoints, backtraces, and memory inspection using GDB.

What is GDB?

GDB (GNU Project Debugger) is a portable debugger that lets you see what is happening inside a program while it runs or what it was doing at the moment of a crash.

flowchart TD
  A[GDB Debugging] --> B[Start Program]
  A --> C[Set Breakpoints]
  A --> D[Step Through Code]
  A --> E[Inspect State]
  B --> F[Run / Continue]
  C --> G[Function Breakpoints]
  C --> H[Line Breakpoints]
  C --> I[Conditional Breakpoints]
  E --> J[Variables]
  E --> K[Memory]
  E --> L[Registers]
  E --> M[Stack Trace]

Compiling for Debugging

Programs must be compiled with debug symbols (-g flag).

# C
gcc -g -o program program.c

# C++
g++ -g -o program program.cpp

# Rust
rustc -g main.rs
# or
cargo build

Example C Program

// crash.c
#include <stdio.h>
#include <stdlib.h>

void cause_crash() {
  int *ptr = NULL;
  *ptr = 42;  // Segmentation fault
}

int main() {
  printf("About to crash...\n");
  cause_crash();
  printf("This never runs\n");
  return 0;
}
gcc -g -o crash crash.c
./crash
# Segmentation fault (core dumped)

Starting GDB

gdb ./crash

GDB Prompt

(gdb)

Running the Program

(gdb) run
Starting program: /home/user/crash

About to crash...

Program received signal SIGSEGV, Segmentation fault.
0x000055555555515e in cause_crash () at crash.c:6
6         *ptr = 42;  // Segmentation fault

GDB stopped at the exact line where the crash occurred.

Breakpoints

# Set breakpoint at a function
(gdb) break main
Breakpoint 1 at 0x1149: file crash.c, line 9.

# Set breakpoint at a line
(gdb) break crash.c:6
Breakpoint 2 at 0x1156: file crash.c, line 6.

# List breakpoints
(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000555555555149  in main at crash.c:9
2       breakpoint     keep y   0x0000555555555156  in cause_crash at crash.c:6

# Delete breakpoint
(gdb) delete 1

# Disable breakpoint
(gdb) disable 2

Conditional Breakpoints

#include <stdio.h>

int main() {
  for (int i = 0; i < 100; i++) {
    printf("i = %d\n", i);
  }
  return 0;
}
gcc -g -o loop loop.c
gdb ./loop
(gdb) break loop.c:5 if i == 50
(gdb) run

GDB stops only when i equals 50, saving you 50 manual iterations.

Expected Output

(gdb) run
Starting program: /home/user/loop
i = 0
...
i = 49

Breakpoint 1, main () at loop.c:5
5           printf("i = %d\n", i);
(gdb) print i
$1 = 50

Stepping Through Code

# Step into function
(gdb) step

# Step over (next line)
(gdb) next

# Continue until next breakpoint
(gdb) continue

# Step out of current function
(gdb) finish

# Execute next instruction in assembly
(gdb) stepi

Inspecting Variables

// vars.c
#include <stdio.h>

int main() {
  int a = 10;
  int b = 20;
  int c = a + b;
  char *name = "Alice";

  printf("Result: %d\n", c);
  printf("Name: %s\n", name);

  return 0;
}
gcc -g -o vars vars.c
gdb ./vars

Variable Inspection Commands

(gdb) break 9
(gdb) run

(gdb) print a
$1 = 10

(gdb) print b
$2 = 20

(gdb) print c
$3 = 30

(gdb) print name
$4 = 0x555555556004 "Alice"

(gdb) print &a
$5 = (int *) 0x7fffffffe4dc

(gdb) display c  # Auto-print every stop
1: c = 30

Examining Memory

(gdb) x/4xb &a   # Examine 4 bytes in hex at address of a
0x7fffffffe4dc: 0x0a    0x00    0x00    0x00

(gdb) x/s name   # Examine as string
0x555555556004: "Alice"

(gdb) x/16xw $rsp  # Examine stack memory

Backtrace

When a program crashes or stops at a breakpoint, backtrace shows the call stack.

(gdb) backtrace
#0  cause_crash () at crash.c:6
#1  0x0000555555555173 in main () at crash.c:11

(gdb) backtrace full  # With local variables
#0  cause_crash () at crash.c:6
No locals.
#1  0x0000555555555173 in main () at crash.c:10
No locals.

Frame Navigation

(gdb) frame 0  # Go to innermost frame
(gdb) frame 1  # Go to caller frame
(gdb) up       # Move up one frame
(gdb) down     # Move down one frame

Watchpoints

Watchpoints stop execution when a variable's value changes.

// watch.c
#include <stdio.h>

int main() {
  int counter = 0;
  for (int i = 0; i < 5; i++) {
    counter += i;
  }
  printf("Counter: %d\n", counter);
  return 0;
}
gdb ./watch
(gdb) break 6
(gdb) run
(gdb) watch counter
(gdb) continue

Expected Output

(gdb) continue
Continuing.

Hardware watchpoint 2: counter

Old value = 0
New value = 0
main () at watch.c:7
7         counter += i;

(gdb) continue
Continuing.

Hardware watchpoint 2: counter

Old value = 0
New value = 1
main () at watch.c:7
7         counter += i;

Debugging Rust Programs

// main.rs
fn divide(a: f64, b: f64) -> f64 {
    if b == 0.0 {
        panic!("Division by zero!");
    }
    a / b
}

fn main() {
    let x = 10.0;
    let y = 0.0;
    let result = divide(x, y);
    println!("Result: {}", result);
}
rustc -g main.rs -o divide
gdb ./divide

Rust-Specific Commands

(gdb) break 10  # Break at main function line
(gdb) run

(gdb) print x
$1 = 10

(gdb) print y
$2 = 0

(gdb) step
(gdb) step  # Step into divide function
(gdb) print a
$3 = 10
(gdb) print b
$4 = 0

Multi-Threaded Debugging

// threads.c
#include <pthread.h>
#include <stdio.h>

void *worker(void *arg) {
  int id = *(int *)arg;
  for (int i = 0; i < 3; i++) {
    printf("Thread %d: iteration %d\n", id, i);
  }
  return NULL;
}

int main() {
  pthread_t t1, t2;
  int id1 = 1, id2 = 2;
  pthread_create(&t1, NULL, worker, &id1);
  pthread_create(&t2, NULL, worker, &id2);
  pthread_join(t1, NULL);
  pthread_join(t2, NULL);
  return 0;
}
gcc -g -o threads threads.c -lpthread
gdb ./threads

Thread Commands

(gdb) break worker
(gdb) run

(gdb) info threads
  Id   Target Id         Frame
  1    Thread 0x7ffff7f... (process 1234) main ()
* 2    Thread 0x7ffff7f... worker (arg=0x7fffffffe4dc)
  3    Thread 0x7ffff7e... worker (arg=0x7fffffffe4e0)

(gdb) thread 2
[Switching to thread 2]
(gdb) print id  # Local variable in this thread
$1 = 1

Common Errors

Problem Cause Fix
No <a href="/compiler-design/symbol-table-management/">Symbol Table</a> is loaded Compiled without -g flag Recompile with gcc -g
Cannot find bounds of current function Optimized code Compile with -O0 to disable optimization
Breakpoint not inserted Shared library not loaded Use break after the library is loaded
SIGSEGV in libc Invalid pointer Check the return value of malloc
Thread breakpoint hits multiple times Breakpoint not thread-specific Use break worker thread 2

Practice Questions

1. What compiler flag is required for debugging symbols?

-g flag with GCC or Clang.

2. How do you set a breakpoint at a specific line number?

break filename.c:42.

3. What command shows the call stack?

backtrace (or bt).

4. How do you inspect the value of a variable?

print variable_name.

5. What is the difference between step and next in GDB?

step steps into function calls; next steps over them without entering.

Challenge

Write a C program with a memory leak or buffer overflow. Use GDB to identify the bug: set breakpoints at relevant functions, inspect memory, and trace the execution flow. Fix the bug and verify with GDB that the fix works.

Real-World Task

Use GDB to debug a multi-threaded C or C++ program that has a Race Condition. Set breakpoints in each thread, inspect shared variables at different execution points, and identify the interleaving that causes the bug. Apply a fix using mutexes and verify with GDB.

Can GDB debug Python programs?

No. GDB debugs compiled languages (C, C++, Rust, Fortran). For Python, use pdb or ipdb.

Does GDB work with Rust?

Yes. GDB supports Rust with pretty-printing for standard types. Compile with rustc -g or cargo build (debug mode is default).

What is the difference between a breakpoint and a watchpoint?

A breakpoint stops at a specific code location. A watchpoint stops when a variable's value changes.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro