A debugger shows the line that crashed
A debugger such as gdb runs your program under its control. You can stop it, look at its variables and see which line crashed it. Reading a crash this way is faster than adding printf lines and guessing.
A program prints Segmentation fault and nothing else. gdb tells you the line, the function and the call that led there.
By the end of this lesson
Note
I can find the line where a program crashes with a debugger.
Key words
| Word | What it means | Example |
|---|---|---|
| Debugger | a tool that runs a program under your control | gdb |
| Debug symbols | extra information that lets a debugger name lines and variables | gcc -g |
| Signal | a message the system sends to stop or warn a process | SIGSEGV |
| Segmentation fault | a crash from using memory the program may not use | |
| Backtrace | the list of function calls that led to a point | |
| Breakpoint | a place where the debugger stops the program |
The cells students fill in are left blank in this preview.
A segmentation fault is a bad memory access
A segmentation fault happens when a program reads or writes memory it is not allowed to use. The system stops it with the signal SIGSEGV.
The usual cause is a pointer that holds NULL, or an address that is wrong. The program below does exactly that, by writing through a NULL pointer.
A function that writes through NULL
#include <stdio.h>
void fill(int *slot) {
*slot = 5; // crashes when slot is NULL
}
int main(void) {
int *p = NULL;
fill(p);
printf("done\n"); // never reached
return 0;
}
Write the crashing program to a file
printf '%s\n' '#include <stdio.h>' 'void fill(int *slot) {' ' *slot = 5;' '}' 'int main(void) {' ' int *p = NULL;' ' fill(p);' ' printf("done\n");' ' return 0;' '}' > crash.cShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Build it with debug symbols
gcc -g -Wall -o crash crash.cShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run it and print the exit status
bash -c './crash; echo "status $?"'Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A backtrace lists the calls to the crash
gdb can run the program and stop it when the signal arrives. The command bt, short for backtrace, then lists every function that was running, newest first.
Frame #0 is the line that crashed. Frame #1 is the function that called it, and so on out to main. The lines also show each argument's value.
How to read what gdb prints
| What you see | What it tells you |
|---|---|
| Program received signal SIGSEGV | The kind of crash |
| fill (slot=0x0) | The function, and its argument slot is NULL |
| at crash.c:3 | The file and the line number |
| #1 main () at crash.c:7 | The caller: main made the call on line 7 |
The cells students fill in are left blank in this preview.
Run it in gdb and print the backtrace
gdb -batch -ex run -ex bt ./crashShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Stop in fill and print its argument
gdb -batch -ex "break fill" -ex run -ex "print slot" ./crashShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people think the line gdb shows is the line with the bug. It is the line where the program fell over. Here the crash is in fill, but main passed the NULL, so look at frame 1 as well.
A breakpoint stops the program where you choose
A crash is not the only time to look. A breakpoint stops the program before a chosen line runs. Then print shows a variable, and continue runs on to the next stop.
The table shows what a loop that adds ten times a number looks like when stopped at its body each time.
Worked example: stops inside a loop
| Stop | i | total, before the line runs |
|---|---|---|
| 1 | 1 | 0 |
| 2 | 2 | 10 |
| 3 | 3 | 30 |
| 4 | 4 | 60 |
The cells students fill in are left blank in this preview.
Write a loop program to a file
printf '%s\n' '#include <stdio.h>' 'int main(void) {' ' int total = 0;' ' for (int i = 1; i <= 4; i++) {' ' total = total + i * 10;' ' }' ' printf("total %d\n", total);' ' return 0;' '}' > sum.cShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Build the loop with debug symbols
gcc -g -Wall -o sum sum.cShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Stop twice in the loop and print
gdb -batch -ex "break 5" -ex run -ex "print total" -ex continue -ex "print total" -ex "print i" ./sumShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.