Skip to content

🔍 GDB — The Debugger Post-mortem

"Never restart a crashed server without saving the core dump."

Core dump = frozen crime scene. GDB = forensic analysis tool.

Core Dumps — The Crash Autopsy (@[/debug])

What is a Core Dump?

┌─────────────────────────────────────────────────────────────────────────┐
│                    CORE DUMP CONCEPT                                     │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   When a program crashes (SIGSEGV, SIGABRT, etc.):                      │
│                                                                         │
│   ┌─────────────────┐          ┌─────────────────────┐                 │
│   │  Crashed        │   ────►  │    Core Dump        │                 │
│   │  Process        │          │    (core.12345)     │                 │
│   │  (Memory State) │          │                     │                 │
│   └─────────────────┘          │  • Register values  │                 │
│          ↓                     │  • Stack frames     │                 │
│      Dead (exit)               │  • Heap data        │                 │
│                                │  • Loaded libraries │                 │
│                                └─────────────────────┘                 │
│                                         │                               │
│                                         ▼                               │
│                                ┌─────────────────┐                     │
│                                │      GDB        │                     │
│                                │  Load dump      │                     │
│                                │  Run 'bt'       │                     │
│                                │  See crash site │                     │
│                                └─────────────────┘                     │
│                                                                         │
│   → Core dump = "frozen moment of death"                                │
│   → Can analyze hours/days later                                        │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Enable Core Dumps

bash
# Check current limit
ulimit -c

# Enable core dumps (unlimited size)
ulimit -c unlimited

# Make permanent: add to /etc/security/limits.conf
*               soft    core            unlimited
*               hard    core            unlimited

# Configure core dump location
echo "/tmp/cores/core.%e.%p" | sudo tee /proc/sys/kernel/core_pattern

Generate Test Core Dump

cpp
// crash.cpp
#include <cstdlib>

void InnerFunction(int* ptr) {
    *ptr = 42;  // SIGSEGV if ptr is null!
}

void MiddleFunction() {
    int* null_ptr = nullptr;
    InnerFunction(null_ptr);
}

void OuterFunction() {
    MiddleFunction();
}

int main() {
    OuterFunction();
    return 0;
}
bash
# Compile with debug symbols
g++ -g -o crash crash.cpp

# Run (will crash)
./crash
# Segmentation fault (core dumped)

# Find core dump
ls -la /tmp/cores/ # or current directory

Loading Core Dump in GDB

bash
# Load program + core dump
gdb ./crash /tmp/cores/core.crash.12345

# Or if core is in current directory
gdb ./crash core

First Command: Backtrace

gdb
(gdb) bt
#0  0x0000000000401149 in InnerFunction (ptr=0x0) at crash.cpp:5
#1  0x0000000000401168 in MiddleFunction () at crash.cpp:10
#2  0x0000000000401178 in OuterFunction () at crash.cpp:14
#3  0x0000000000401188 in main () at crash.cpp:18

💡 READING BACKTRACE

  • #0 = Where crash happened (InnerFunction, line 5)
  • ptr=0x0 = The pointer was null!
  • Stack shows call chain: main → Outer → Middle → Inner
gdb
(gdb) frame 1
#1  0x0000000000401168 in MiddleFunction () at crash.cpp:10
10	    InnerFunction(null_ptr);

(gdb) print null_ptr
$1 = (int *) 0x0

(gdb) info locals
null_ptr = 0x0

Essential GDB Commands

gdb
# Backtrace (call stack)
bt              # Short form
backtrace       # Full form
bt full         # With local variables

# Move between frames
frame 2         # Go to frame #2
up              # Go one frame up (caller)
down            # Go one frame down (callee)

# List source code
list            # Show current location
list 20         # Show around line 20
list function   # Show function

Breakpoints

gdb
# Set breakpoint
break main                    # At function
break crash.cpp:10            # At line
break MyClass::MyMethod       # At method

# Conditional breakpoint
break 10 if x > 100

# List breakpoints
info breakpoints

# Delete breakpoint
delete 1        # Delete breakpoint #1
delete          # Delete all

Execution Control

gdb
# Start/Continue
run             # Start program
run arg1 arg2   # With arguments
continue        # Continue after breakpoint (c)

# Step through code
next            # Step over (don't enter functions) (n)
step            # Step into (enter functions) (s)
finish          # Run until current function returns
until 25        # Run until line 25

Inspecting Data

gdb
# Print values
print x                    # Print variable
print *ptr                 # Dereference pointer
print array[5]             # Array element
print obj.member           # Object member
print/x value              # Print as hex
print/t value              # Print as binary

# Watch (stop when value changes!)
watch x                    # Stop when x changes
watch x if x > 100         # Conditional watch
rwatch x                   # Stop when x is read
awatch x                   # Stop on read or write

# Memory examination
x/10x ptr                  # 10 hex words at ptr
x/s str                    # Print string
x/i $pc                    # Current instruction

GDB TUI Mode — Visual Debugging

bash
# Start GDB with TUI
gdb -tui ./myapp

# Or enable during session
(gdb) tui enable
(gdb) layout src    # Source code view
(gdb) layout asm    # Assembly view
(gdb) layout split  # Both source and assembly
(gdb) layout regs   # Registers view
┌─────────────────────────────────────────────────────────────────────────┐
│                    GDB TUI INTERFACE                                     │
├─────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │   1  #include <iostream>                                            │ │
│ │   2                                                                 │ │
│ │   3  void InnerFunction(int* ptr) {                                 │ │
│ │B+ 4      *ptr = 42;  ◄──── breakpoint                               │ │
│ │ > 5  }                ◄──── current line                            │ │
│ │   6                                                                 │ │
│ │   7  void MiddleFunction() {                                        │ │
│ │   8      int* null_ptr = nullptr;                                   │ │
│ │   9      InnerFunction(null_ptr);                                   │ │
│ │  10  }                                                              │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ (gdb) print ptr                                                         │
│ $1 = (int *) 0x0                                                        │
│ (gdb) _                                                                 │
└─────────────────────────────────────────────────────────────────────────┘

Remote Debugging

Server (Crashed Machine)

bash
# Start gdbserver on port 1234
gdbserver :1234 ./myapp

# Or attach to running process
gdbserver :1234 --attach $(pidof myapp)

Client (Your Machine)

gdb
# Connect to remote
(gdb) target remote 192.168.1.100:1234

# Debug as usual
(gdb) bt
(gdb) break main
(gdb) continue

Common Debugging Scenarios

Scenario 1: Null Pointer Crash

gdb
(gdb) bt
#0  0x... in ProcessData (data=0x0) at process.cpp:42

# Frame 0 shows data=0x0 (null!)
# Go up to see where null came from
(gdb) up
#1  0x... in HandleRequest (req=0x7fff...) at handler.cpp:15
(gdb) print req.data
$1 = (Data *) 0x0

# req.data was null when passed to ProcessData

Scenario 2: Infinite Loop

gdb
# Attach to running process
gdb -p $(pidof stuck_program)

(gdb) bt
#0  0x... in SearchLoop () at search.cpp:89

(gdb) print iteration_count
$1 = 99999999  # Still incrementing!

(gdb) print found
$2 = false     # Never becomes true

# Found the bug: search condition never satisfied

Scenario 3: Data Corruption

gdb
# Use watchpoint to find WHO corrupts the data
(gdb) watch my_important_variable

(gdb) continue
Hardware watchpoint 1: my_important_variable
Old value = 42
New value = 0
0x... in SuspiciousFunction () at suspicious.cpp:55

# Caught! SuspiciousFunction changed the value

GDB Init File (.gdbinit)

bash
# ~/.gdbinit
set print pretty on
set pagination off
set confirm off

# Pretty print STL containers
python
import sys
sys.path.insert(0, '/usr/share/gcc/python')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end

# Custom commands
define pv
  print *(std::vector<$arg0>*)$arg1
end

Best Practices

┌─────────────────────────────────────────────────────────────────────────┐
│                    GDB BEST PRACTICES                                    │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   ✅ DO                                                                  │
│   ─────                                                                 │
│   • Always compile with -g for debug symbols                            │
│   • Enable core dumps in production (ulimit -c unlimited)               │
│   • Save core dumps! They're evidence.                                  │
│   • Use watchpoints to find corruption                                  │
│   • Learn TUI mode for faster debugging                                 │
│                                                                         │
│   ❌ DON'T                                                               │
│   ───────                                                               │
│   • Don't restart crashed servers without core dump                     │
│   • Don't debug -O3 optimized code (variables optimized out)            │
│   • Don't ignore compiler warnings (often predict bugs)                 │
│                                                                         │
│   🔧 PRODUCTION SETUP                                                   │
│   ───────────────────                                                   │
│   # In systemd service                                                  │
│   LimitCORE=infinity                                                    │
│   Environment="MALLOC_CHECK_=3"                                         │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Bước tiếp theo

📊 Logging → — spdlog, Structured Logging, Request Tracing