Giao diện
🔍 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 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_patternGenerate 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 directoryLoading 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 coreFirst 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
Navigate Frames
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 = 0x0Essential GDB Commands
Navigation
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 functionBreakpoints
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 allExecution 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 25Inspecting 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 instructionGDB 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) continueCommon 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 ProcessDataScenario 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 satisfiedScenario 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 valueGDB 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
endBest 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" │
│ │
└─────────────────────────────────────────────────────────────────────────┘