Skip to content

Expert · Debugging and tools · topic 51 of 60 · 30 min

Debugging with gdb

Explanation

gdb (the GNU debugger) runs your program under control: set breakpoints, step line by line, print variables, and see the call stack. Compile with -g -O0 first. After a crash such as a segmentation fault, run the program in gdb and type bt (backtrace) to see exactly which line and which chain of calls caused it.

Prepare the program

bash
gcc -std=c17 -Wall -Wextra -g -O0 bill.c -o bill   # -g: debug info, -O0: no optimisation
gdb ./bill
-O0 keeps the machine code close to your source, so stepping and printing behave as expected.

The essential commands

CommandShortDoes
break main / break bill.c:42bStop at a function or a line
run [args]rStart the program (with arguments)
nextnRun the current line, stepping OVER function calls
stepsRun the current line, stepping INTO function calls
continuecRun until the next breakpoint
finishfinRun until the current function returns
print exprpShow a value: p total, p *order, p marks[3], p arr[0]@5
info localsi loShow all local variables
backtracebtShow the call stack
frame N / up / downfMove between stack frames
watch exprStop when a variable's value changes
listlShow source around the current line
quitqExit gdb

Finding a segfault

output
$ ./bill
Segmentation fault (core dumped)

$ gdb ./bill
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0x0000555555555189 in total_amount (items=0x0, n=3) at bill.c:8
8           sum += items[i].price;
(gdb) bt
#0  total_amount (items=0x0, n=3) at bill.c:8
#1  0x00005555555551d4 in print_bill (cart=0x7fffffffe010) at bill.c:21
#2  0x0000555555555210 in main () at bill.c:30
(gdb) frame 1
(gdb) print cart->items
$1 = (Item *) 0x0
The crash is at line 8 because items is NULL; frame 1 shows the caller passed cart->items, which was never set.

Watching a variable

output
(gdb) break main
(gdb) run
(gdb) watch balance            # stop whenever balance changes
(gdb) continue
Hardware watchpoint 2: balance
Old value = 5000
New value = -200               # found the line that made it negative

Core dumps

A core dump is a snapshot of the program's memory at the moment it crashed. With core dumps enabled (ulimit -c unlimited in the shell; many Linux systems collect them with systemd-coredump and coredumpctl), you can run gdb ./bill core and type bt to investigate a crash that happened earlier, even on a test server.

Careful: Debugging optimised code (-O2) is confusing: lines run out of order, and variables show "<optimized out>". Reproduce the bug with -O0 -g when you can. If the bug only appears with optimisation, suspect undefined behaviour and run with -fsanitize=undefined.
Think of it like this: gdb is like the slow-motion replay with a pause button in a cricket broadcast: you can stop the match at any ball (breakpoint), replay frame by frame (next/step), zoom in on any player (print), and ask how the batsman got there (backtrace).
Tip: Learn the TUI mode (gdb -tui, or press Ctrl+X then A) to see the source and your position side by side. VS Code and CLion use gdb underneath with a graphical interface.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Debugging with gdb · 1/6

gdb

In this lesson you will debug C programs with gdb.

Prefer a video by a teacher? Find free videos on “gdb tutorial debugging c segmentation fault backtrace breakpoints” on YouTube ↗

In real life: A crash in a payroll batch

A payroll batch job crashed only for one branch's data file. Running it under gdb with that file and typing bt showed the crash inside parse_employee called from load_branch; printing the line buffer revealed an empty department field, which made strchr return NULL that the code then dereferenced. A NULL check and a test for empty fields fixed it.

bash
gdb --args ./payroll branch_17.csv
(gdb) run
(gdb) bt
(gdb) frame 1
(gdb) print line

Practice (0/3 done)

Task 1 · Question

Which gdb command shows the chain of function calls at a crash?

Task 2 · Question

You want to go INTO the function called on the current line. Which command?

Task 3 · Practice

Write a program with a deliberate NULL-pointer crash three calls deep (main → load → parse). Compile with -g -O0, reproduce the crash in gdb, and use bt, frame, print and info locals to find the cause. Then set a breakpoint before the crash, step through, and use watch on a variable. Write down each command and what it showed.

Quick quiz

Question 1

1.Which flag adds debug information for gdb?

Question 2

2.What does finish do?

Question 3

3.What does print arr[0]@5 show?

Question 4

4.Why debug with -O0 rather than -O2?

Read more (free)

Before this, learn

This topic builds on these. If something here feels new, open them first.

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Which flag adds debug information for gdb?

Answer: -g

Line numbers and variable names.

What does finish do?

Answer: Runs until the current function returns

Then shows the return value.

What does print arr[0]@5 show?

Answer: Five elements starting at arr[0]

@ prints an array slice.

Why debug with -O0 rather than -O2?

Answer: Optimised code reorders lines and removes variables, which is confusing to step through

Variables show <optimized out>.

What does "Step Into" do in a debugger?Related

Answer: Goes inside the method called on the current line

Step Into enters the called method.

From Reading errors and debugging

Why print debug output to stderr rather than stdout?Related

Answer: stderr is unbuffered, so lines appear even if the program crashes right after

Buffered stdout may be lost on a crash.

From A method for debugging

You are paused on the line total = addGst(price); and want to see what happens inside addGst. Which button?Related

Answer: Step into

Step into goes inside the function called on this line. Step over would run it all and stop on the next line.

From Debugging like a pro: DevTools and VS Code

Which sanitizer finds use-after-free bugs?Related

Answer: AddressSanitizer

AddressSanitizer checks memory accesses.

From Building real projects: CMake, warnings, debuggers and sanitizers

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 4 more topics in “Debugging and tools” before its exam: Finding memory bugs with valgrind, Sanitizers and warnings, assert, logging and testing C code, A method for debugging.
  2. Module 11 exam: Debugging and tools · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Embedded C, system calls and careers. 60 topics left in the course before the final exam.

← Previous: Hash tables