Skip to content

[中文版本](/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 unlimited

Check:

ulimit -a | grep core

Now 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
./crash

Output:

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 core

You’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) bt

Shows the backtrace, the call stack at the moment of crash.

#0  main () at crash.c:5

You can inspect variables:

(gdb) info locals
(gdb) print p

Output:

$1 = (int *) 0x0

Step 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 core

Inside 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:16

You 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 crash

The-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.%p

This example saves them in/tmp with program name and process ID:

/tmp/core.crash.1234

Step 9. Crash Analysis Workflow ​

Enable dumps:ulimit -c unlimited

Run program until it crashes

Locate core file

Analyze with gdb:

  • bt for stack trace
  • info locals for local vars
  • frame to inspect each stack frame
  • list to 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_fail

Output:

assert_fail: assert_fail.c:5: main: Assertion `x == 10' failed.
Aborted (core dumped)

Now inspect:

gdb ./assert_fail core
(gdb) bt

Output:

#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:5

You 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 ​

  1. Write a small program that dereferences a null pointer.
  2. Enable core dumps (ulimit -c unlimited).
  3. Crash it, then analyze the dump in gdb.
  4. Add one more function layer and check the call chain.
  5. Experiment withSIGFPE(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.

本站总访问 — 次