Appearance
[中文版本](/posts/c教程/zh-CN/08-调试测试与 profiling/078-Crash Analysis and Core Dumps)
Even with careful coding and testing, programs crash. In C, a crash is your system’s way of saying “you touched something you shouldn’t have.” The good news is you can analyze crashes scientifically using core dumps, snapshots of your program’s memory at the moment of failure.
Learning how to read them is an essential skill for every systems programmer.
Step 1. What Is a Core Dump?
A core dump is a file that captures your program’s state (stack, registers, memory) at the time it crashed. You can inspect it later using a debugger like gdb to see what went wrong.
Common crash signals that generate core dumps:
- SIGSEGV – invalid memory access (segmentation fault)
- SIGABRT – failed assertion
- SIGFPE – arithmetic error (like division by zero)
- SIGILL – illegal instruction
Step 2. Enabling Core Dumps
By default, modern systems limit or disable them. Enable them using:
ulimit -c unlimitedCheck:
ulimit -a | grep coreNow when your program crashes, a file namedcore orcore. will appear.
Step 3. A Crashing Example
Let’s make a simple crash:
#include <stdio.h>
int main(void) {
int *p = NULL;
*p = 42; // crash: dereferencing null pointer
return 0;
}Compile and run:
gcc crash.c -g -o crash
./crashOutput:
Segmentation fault (core dumped)Acore file is now created in your directory.
Step 4. Opening the Core Dump in gdb
Use gdb to inspect what happened:
gdb ./crash coreYou’ll see:
Core was generated by `./crash'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000000000401136 in main () at crash.c:5
5 *p = 42;This tells you the exact line where the program crashed.
Step 5. Inspecting Variables and Stack
Within gdb:
(gdb) btShows the backtrace, the call stack at the moment of crash.
#0 main () at crash.c:5You can inspect variables:
(gdb) info locals
(gdb) print pOutput:
$1 = (int *) 0x0Step 6. A More Complex Example
#include <stdio.h>
void f3(int *p) {
*p = 7; // likely crash here
}
void f2(int *p) {
f3(p);
}
void f1(void) {
int *x = NULL;
f2(x);
}
int main(void) {
f1();
}Run it, crash it, then:
gdb ./crash coreInside gdb:
(gdb) bt
#0 f3 (p=0x0) at crash.c:4
#1 f2 (p=0x0) at crash.c:8
#2 f1 () at crash.c:12
#3 main () at crash.c:16You can see the entire call chain that led to the null dereference.
Step 7. Debugging Optimized Builds
When you compile with-O2 or-O3, the compiler may inline or reorder code, making debugging harder. For debugging, always use:
gcc -g -O0 crash.c -o crashThe-g flag keeps symbol information (file names, line numbers). Without it, gdb can’t tell you much beyond addresses.
Step 8. Controlling Core Dump Location
Change where dumps are stored:
sudo sysctl -w kernel.core_pattern=/tmp/core.%e.%pThis example saves them in/tmp with program name and process ID:
/tmp/core.crash.1234Step 9. Crash Analysis Workflow
Enable dumps:ulimit -c unlimited
Run program until it crashes
Locate core file
Analyze with gdb:
btfor stack traceinfo localsfor local varsframeto inspect each stack framelistto see nearby code
This gives you a full picture of what happened just before failure.
Step 10. Tiny Code: Assertion Failure Analysis
#include <assert.h>
#include <stdio.h>
int main(void) {
int x = 5;
assert(x == 10); // fails
printf("Done\n");
}Compile and run:
gcc -g assert_fail.c -o assert_fail
ulimit -c unlimited
./assert_failOutput:
assert_fail: assert_fail.c:5: main: Assertion `x == 10' failed.
Aborted (core dumped)Now inspect:
gdb ./assert_fail core
(gdb) btOutput:
#0 0x00007f0a9a4b9187 in raise () from /lib64/libc.so.6
#1 0x00007f0a9a4c53e8 in abort () from /lib64/libc.so.6
#2 0x00007f0a9a4b0246 in __assert_fail_base () from /lib64/libc.so.6
#3 0x00007f0a9a4b02f2 in __assert_fail () from /lib64/libc.so.6
#4 0x0000000000401136 in main () at assert_fail.c:5You can trace exactly how the assertion caused the abort signal.
Why It Matters
Crash analysis turns chaos into clarity. Instead of guessing, you can:
- See which line caused the crash.
- Inspect all variables at that point.
- Reproduce and fix the bug quickly.
Every serious C programmer must master this, it’s how systems engineers debug everything from user tools to kernels.
Try It Yourself
- Write a small program that dereferences a null pointer.
- Enable core dumps (
ulimit -c unlimited). - Crash it, then analyze the dump in gdb.
- Add one more function layer and check the call chain.
- Experiment with
SIGFPE(divide by zero) and see how the core dump differs.
Next, you’ll build a code review checklist for C projects, habits and principles that help prevent these crashes before they ever happen.
