C++ Debugging Basics — GDB & LLDB: Breakpoints, Watchpoints,
이 글의 핵심
C++ debugging basics: GDB and LLDB breakpoints and watchpoints to narrow bugs quickly—limits of printf debugging and problem scenarios.
Introduction: limits of printf debugging
“I added 100 couts and still can’t find the bug”
You were chasing a segfault. You added dozens of std::cout statements but still could not pinpoint the cause. Output was buffered, so you never saw the exact crash location, and recompiling and rerunning cost a lot of time. A debugger lets you stop and inspect which line you are on, variable values, and the stack at that moment. Learning breakpoints (where to stop), watchpoints (stop when a variable changes), conditional stops, and backtraces (call stack) alone lets you narrow bugs much faster than printf debugging.
Analogy: printf debugging is like taking photos one frame at a time and guessing what happened; a debugger is like freezing time and inspecting the full state directly.
Environment: GDB (Linux/WSL: apt install gdb) or LLDB (macOS: xcode-select --install). Always build with -g.
After reading this article you will be able to:
- Use core GDB/LLDB commands in real debugging sessions.
- Set breakpoints and watchpoints appropriately.
- Inspect variables, memory, and the stack and trace root causes.
- Apply production-oriented debugging patterns.
Practical note: This article is based on real issues and fixes from large C++ projects. It includes practical pitfalls and tips that books and manuals often skip.
Table of contents
- Problem scenarios
- Starting the debugger
- Breakpoints — full guide
- Tracing memory with watchpoints
- Stepping
- Inspecting variables and memory
- GDB/LLDB complete examples
- Common errors and fixes
- Debugging best practices
- Production debugging patterns
1. Problem scenarios
Scenario 1: “It segfaults but I don’t know where”
"The program dies with Segmentation fault. The log shows nothing."
"I have no idea which function is at fault."
Situation: Crashes from out-of-bounds indices, null pointer dereference, etc. You sprinkled printf everywhere but output was buffered or the last log before the crash never printed, so you could not narrow the location.
What to do: Run under GDB/LLDB → on crash use backtrace → frame N to the right frame → print variables to find the cause.
Scenario 2: “Only the 500th iteration of 1000 fails”
"Usually it works; sometimes the result is wrong."
"Hard to reproduce, so printf is painful."
Situation: The bug only appears on a specific iteration or input. Printing all 1000 iterations is wasteful; conditional printf clutters the code.
What to do: Conditional breakpoint — e.g. break main.cpp:20 if i == 500 so you only stop when it matters.
Scenario 3: “A variable changes somewhere and I can’t find where”
"A variable I initialized later has a bogus value."
"I have no idea what overwrites it."
Situation: Memory corruption, buffer overruns, bad pointers change values unexpectedly. Hard to trace across hundreds of functions.
What to do: Watchpoint — watch variable_name stops when the value changes.
Scenario 4: “I’m stuck in an infinite loop”
"The program looks hung. I hit Ctrl+C but don’t know why."
Situation: Wrong while condition, missing i++, etc. Too much printf output hides the signal.
What to do: Interrupt with Ctrl+C → backtrace → print loop variables.
Scenario 5: “Deadlock in a multi-threaded program”
"Two threads seem to wait for each other."
"I don’t know which thread holds which mutex."
Situation: Inconsistent lock ordering causes deadlock; may not reproduce on a single thread.
What to do: Ctrl+C → thread apply all backtrace to see every thread’s stack.
Scenario 6: “It only crashes in production sometimes”
"Never on my dev machine; occasionally dies on the server."
"No core dump, so I can’t analyze."
Situation: Load- or data-dependent bugs; hard to reproduce; attaching a debugger in production is often impractical.
What to do: Enable core dumps → collect core on crash → analyze with GDB on the core file.
Choosing a tool by scenario
flowchart TD
A[Bug] --> B{Type?}
B -->|Crash| C[GDB/LLDB run → backtrace]
B -->|Conditional| D[Conditional breakpoint]
B -->|Variable corruption| E[Watchpoint]
B -->|Infinite loop| F[Ctrl+C → backtrace]
B -->|Deadlock| G[thread apply all bt]
B -->|Production| H[Core dump analysis]
2. Starting the debugger
Debug build (required)
-g embeds debug symbols (source lines, names). -O0 disables optimization so variables are less often “optimized out” and line mapping stays faithful.
# Debug info (-g), no optimization (-O0)
g++ -g -O0 main.cpp -o myapp
# With CMake
cmake -DCMAKE_BUILD_TYPE=Debug ..
make
Starting GDB
# Load the program
gdb ./myapp
# Run
(gdb) run
# Run with arguments
(gdb) run arg1 arg2
# Set env then run
(gdb) set env LD_LIBRARY_PATH=/path/to/libs
(gdb) run
# Quit
(gdb) quit
Note: PIE binaries may load at different addresses; only use ASLR-disabling options in dedicated dev setups for security reasons.
Starting LLDB (macOS)
# Load
lldb ./myapp
# Run
(lldb) run
# With arguments
(lldb) run arg1 arg2
# Quit
(lldb) quit
Debugging workflow
sequenceDiagram
participant Dev as Developer
participant GDB as GDB/LLDB
participant App as Target program
Dev->>GDB: gdb ./myapp
GDB->>App: Load (debug symbols)
Dev->>GDB: break main
Dev->>GDB: run
GDB->>App: Start execution
App->>GDB: Reach main() → stop
GDB->>Dev: Return prompt
Dev->>GDB: next / step / print
GDB->>Dev: Print results
Dev->>GDB: continue
GDB->>App: Run to next breakpoint
3. Breakpoints — full guide
What is a breakpoint?
A place where execution stops so you can inspect variables, stack, and memory.
How software breakpoints work (conceptually):
Software breakpoint:
1. Debugger sets a breakpoint:
(gdb) break main.cpp:20
Debugger steps:
a. Map source file:line → machine address
Debug symbol lookup:
main.cpp:20 → 0x400540
b. Save the original instruction at that address:
0x400540: mov rax, [rbp-8] # original
→ backup: saved_instruction[0x400540] = 0x48 8b 45 f8
c. Replace with int3 (1 byte):
0x400540: int3 # 0xCC
→ write 0xCC to memory
2. Program runs:
CPU hits 0x400540:
→ executes int3
→ SIGTRAP
→ kernel notifies the debugger
3. Debugger handles SIGTRAP:
a. Stop the program (pause PTRACE_CONT)
b. Restore original instruction:
0x400540: mov rax, [rbp-8]
c. Rewind PC by 1 byte:
RIP = 0x400540 (before int3)
d. Return to user prompt:
(gdb)
User types continue:
→ run original instruction once
→ reinstall int3 (reusable)
→ PTRACE_CONT to continue
ptrace usage:
Program start:
fork() → child process
↓
child: ptrace(PTRACE_TRACEME, 0, NULL, NULL)
→ "trace me"
exec(myapp)
↓
parent (GDB): ptrace(PTRACE_CONT, child_pid, ...)
→ controls child execution
Common ptrace ops:
- PTRACE_TRACEME: child asks to be traced
- PTRACE_PEEKTEXT: read memory (print)
- PTRACE_POKETEXT: write memory (insert int3)
- PTRACE_GETREGS: read registers
- PTRACE_SETREGS: write registers
- PTRACE_CONT: continue
- PTRACE_SINGLESTEP: single instruction (step)
Hardware breakpoint:
Uses CPU debug registers (x86: DR0–DR7):
(gdb) hbreak main.cpp:20
DR0 = 0x400540 # breakpoint address
DR7 = 0x00000001 # enable DR0
CPU accesses 0x400540:
→ hardware detects
→ Debug Exception
→ notify debugger
Pros:
- No code patch (no int3)
- Works in ROM / execute-only memory
- Limited to 4 (DR0–DR3)
Conditional breakpoint implementation:
(gdb) break main.cpp:20 if i == 500
Internally:
1. Unconditional breakpoint (int3)
2. On SIGTRAP:
a. Evaluate condition:
read i (PTRACE_PEEKDATA)
i == 500? → false
b. If false:
- execute original instruction
- reinstall int3
- PTRACE_CONT
c. If true:
- return to user prompt
→ condition checked many times (overhead)
→ slow if usually false
Performance comparison:
Software BP:
- Unlimited count
- Patches code (int3)
- Slightly slower (SIGTRAP handling)
Hardware BP:
- Max 4
- No code patch
- Fast (CPU)
Conditional BP:
- Repeated condition eval
- Can be very slow
Concrete example:
Original code:
int main() {
int x = 10; // 0x400540
int y = 20; // 0x400545
return 0;
}
Without debugger:
0x400540: mov dword ptr [rbp-4], 10
0x400545: mov dword ptr [rbp-8], 20
0x40054c: xor eax, eax
0x40054e: ret
After break main.cpp:2:
0x400540: int3 # 0xCC (was C7 45 FC 0A)
0x400545: mov dword ptr [rbp-8], 20
0x40054c: xor eax, eax
0x40054e: ret
Execution:
1. CPU hits 0x400540
2. int3 → SIGTRAP
3. kernel → GDB
4. GDB: stop
5. User: (gdb) prompt
6. continue
7. GDB: restore & run original
8. reinstall int3
9. continue
GDB breakpoint commands
# Break on function
(gdb) break main
(gdb) break processData
(gdb) b main # short
# Break on file:line
(gdb) break main.cpp:15
(gdb) b main.cpp:15
# Conditional (stop only when i == 50)
(gdb) break main.cpp:20 if i == 50
# Only when pointer is null
(gdb) break main.cpp:25 if ptr == nullptr
# List breakpoints
(gdb) info breakpoints
(gdb) i b
# Delete
(gdb) delete 1 # by number
(gdb) delete # all
(gdb) clear main.cpp:15 # by location
# Disable / enable
(gdb) disable 1
(gdb) enable 1
# Ignore first N hits
(gdb) ignore 1 99 # ignore breakpoint 1 ninety-nine times, then stop
LLDB breakpoint commands
# Function
(lldb) breakpoint set --name main
(lldb) b main # short
# File:line
(lldb) breakpoint set --file main.cpp --line 15
(lldb) b main.cpp:15
# Conditional
(lldb) breakpoint set --name processData --condition 'i == 50'
(lldb) breakpoint set -f main.cpp -l 20 -c 'ptr == nullptr'
# List
(lldb) breakpoint list
(lldb) br list
# Delete
(lldb) breakpoint delete 1
(lldb) breakpoint delete # all
# Disable / enable
(lldb) breakpoint disable 1
(lldb) breakpoint enable 1
Conditional breakpoint example
// conditional_bug.cpp — bug only on iteration 500 of 1000
for (int i = 0; i < 1000; ++i) {
process(i); // wrong behavior only when i == 500
}
# GDB: stop at main.cpp line 10 only when i == 500
(gdb) break main.cpp:10 if i == 500
(gdb) run
# LLDB
(lldb) breakpoint set -f main.cpp -l 10 -c 'i == 500'
(lldb) run
4. Tracing memory with watchpoints
What is a watchpoint?
Stops execution when a variable or memory address changes. Use it to answer “where does this value get overwritten?”
GDB watchpoints
# Stop on write
(gdb) watch variable_name
# Value pointed to by ptr changes
(gdb) watch *ptr
# Stop on read (rwatch)
(gdb) rwatch variable_name
# Stop on read or write (awatch)
(gdb) awatch variable_name
# Often: break main, run, then watch when in scope
(gdb) break main
(gdb) run
(gdb) watch arr[5] # arr[5] in scope now
(gdb) continue
LLDB watchpoints
# Stop on write
(lldb) watchpoint set variable variable_name
(lldb) w s v variable_name # short
# Expression (pointer deref)
(lldb) watchpoint set expression -- ptr
(lldb) watchpoint set expression -w write -- *(int*)0x7fff1234
# List
(lldb) watchpoint list
# Delete
(lldb) watchpoint delete 1
Watchpoint example: finding memory corruption
// memory_corruption.cpp
#include <iostream>
int global_counter = 0;
void suspiciousFunction() {
int buffer[10] = {0};
// Buffer overrun: writing buffer[10] may overwrite global_counter
for (int i = 0; i <= 10; ++i) {
buffer[i] = i; // i==10 is out of range!
}
}
int main() {
std::cout << "Before: " << global_counter << "\n";
suspiciousFunction();
std::cout << "After: " << global_counter << "\n"; // unexpected value
return 0;
}
# Find where global_counter changes with GDB
$ g++ -g -O0 -o corrupt memory_corruption.cpp
$ gdb ./corrupt
(gdb) break main
(gdb) run
(gdb) watch global_counter
(gdb) continue
# Stops when global_counter changes
Hardware watchpoint 2: global_counter
Old value = 0
New value = 10
suspiciousFunction () at memory_corruption.cpp:10
Everyday analogy: Think of memory as an apartment building. The stack is like an elevator—fast but limited. The heap is like a warehouse—roomy but slower to navigate. A pointer is the note that says “3rd floor, unit 302.”
5. Stepping
next vs step
| Action | GDB | LLDB | Behavior |
|---|---|---|---|
| Next line (step over calls) | next / n | next / n | Run current line, go to next; do not enter callee |
| Step into | step / s | step / s | Enter function when current line is a call |
| Until return | finish | finish | Run until current function returns |
| Continue | continue / c | continue / c | Run to next breakpoint |
GDB execution control
# Next line (don’t enter functions)
(gdb) next
(gdb) n
# Step into
(gdb) step
(gdb) s
# Run until current function returns
(gdb) finish
# Continue to next breakpoint
(gdb) continue
(gdb) c
# Next/step N times
(gdb) next 5
(gdb) step 3
# Show source around PC
(gdb) list
(gdb) l
LLDB execution control
(lldb) next
(lldb) n
(lldb) step
(lldb) s
(lldb) finish
(lldb) continue
(lldb) c
(lldb) list
(lldb) l
Stepping flow
flowchart TD
A[Current location] --> B{next vs step?}
B -->|next| C[Next source line]
B -->|step| D{Function call?}
D -->|Yes| E[Enter function]
D -->|No| C
C --> F[Wait for next command]
E --> F
F --> G{finish?}
G -->|Yes| H[Run until current function returns]
G -->|No| A
H --> F
6. Inspecting variables and memory
Printing variables
# GDB
(gdb) print x
(gdb) p x
# Dereference pointer
(gdb) print *ptr
# Ten array elements
(gdb) print arr[0]@10
# Struct
(gdb) print person
(gdb) print person.name
# STL vector (GDB 7.0+)
(gdb) print vec
(gdb) print vec.size()
# LLDB
(lldb) frame variable
(lldb) fr v
(lldb) frame variable x
(lldb) p x
(lldb) expression ptr->member
(lldb) expr vec.size()
Examining memory (x in GDB)
# GDB: examine memory
# Form: x/[count][format][size] address
(gdb) x/10x ptr # 10 words in hex (4 bytes each by default)
(gdb) x/10d ptr # 10 in decimal
(gdb) x/10s ptr # strings
(gdb) x/20xb ptr # 20 bytes, hex
# Formats: x hex, d decimal, s string, i instruction
# Sizes: b 1, h 2, w 4, g 8 bytes
Stack trace (backtrace)
# GDB
(gdb) backtrace
(gdb) bt
# Locals in all frames
(gdb) backtrace full
(gdb) bt full
# Select frame
(gdb) frame 2
(gdb) f 2
# Current frame info
(gdb) info frame
# Locals in current frame
(gdb) info locals
# Arguments
(gdb) info args
# LLDB
(lldb) thread backtrace
(lldb) bt
(lldb) frame select 2
(lldb) f 2
(lldb) frame variable
Stack frame picture
flowchart TB
subgraph stack["Call stack (bottom → top)"]
F0["main() — frame 2"]
F1["processData() — frame 1"]
F2["buggyFunction() — frame 0 (current)"]
end
F0 --> F1
F1 --> F2
subgraph vars["Frame 0 locals"]
V1["i = 10"]
V2["size = 10"]
V3["arr = 0x7fff..."]
end
7. GDB/LLDB complete examples
Example 1: Array out of bounds (segfault)
// buggy_array.cpp — g++ -g -O0 -o buggy buggy_array.cpp
#include <iostream>
void buggyFunction(int* arr, int size) {
for (int i = 0; i <= size; ++i) { // ❌ <= bug (i==size is OOB)
arr[i] = i * 2;
}
}
int main() {
int arr[10];
buggyFunction(arr, 10); // crash!
std::cout << "done\n";
return 0;
}
GDB session:
$ g++ -g -O0 -o buggy buggy_array.cpp
$ gdb ./buggy
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
(gdb) backtrace
#0 buggyFunction (arr=0x7fffffffe2a0, size=10) at buggy_array.cpp:5
#1 main () at buggy_array.cpp:12
(gdb) frame 0
(gdb) print i
$1 = 10 # ← bug: valid indices are 0–9
(gdb) print size
$2 = 10
(gdb) print arr[10]
Cannot access memory at address 0x...
Fix:
// ✅ use i < size
for (int i = 0; i < size; ++i) {
arr[i] = i * 2;
}
Example 2: Null pointer dereference
// null_ptr.cpp
struct Node { int value; Node* next; };
int sumList(Node* head) {
int sum = 0;
while (head != nullptr) {
sum += head->value; // crashes if head is null
head = head->next;
}
return sum;
}
(gdb) break sumList
(gdb) run
(gdb) next
(gdb) print head
$1 = (Node *) 0x0 # null pointer!
(gdb) backtrace full
Example 3: Infinite loop
// infinite_loop.cpp
void processData() {
int i = 0;
while (i < 100) {
process(i);
// missing i++!
}
}
# Interrupt with Ctrl+C
(gdb) run
^C
Program received signal SIGINT, Interrupt.
(gdb) backtrace
#0 processData () at main.cpp:5
(gdb) print i
$1 = 0 # never changes → find missing i++
Example 4: Conditional break (500th of 1000)
(gdb) break main.cpp:10 if i == 500
(gdb) run
# After stop
(gdb) print i
$1 = 500
(gdb) step
(gdb) backtrace
Example 5: Watchpoint for corruption
(gdb) break main
(gdb) run
(gdb) watch arr[5]
(gdb) continue
# When arr[5] changes
Hardware watchpoint 2: arr[5]
Old value = 0
New value = 999
someFunction () at memory_corruption.cpp:15
Example 6: STL vector out of range
// vector_bug.cpp
std::vector<int> vec = {1, 2, 3};
for (size_t i = 0; i <= vec.size(); ++i) { // ❌ <= bug
std::cout << vec[i] << "\n";
}
(gdb) run
Program received signal SIGSEGV
(gdb) print i
$1 = 3
(gdb) print vec.size()
$2 = 3
# vec[3] is out of range
Example 7: Multi-threaded deadlock
// deadlock.cpp
std::mutex m1, m2;
void thread1() { m1.lock(); /* ... */ m2.lock(); /* ... */ }
void thread2() { m2.lock(); /* ... */ m1.lock(); /* ... */ }
# After Ctrl+C
(gdb) thread apply all backtrace
Thread 1:
#0 __lll_lock_wait ()
#1 thread1 () at deadlock.cpp:10
Thread 2:
#0 __lll_lock_wait ()
#1 thread2 () at deadlock.cpp:20
# Each thread waits on the other’s mutex → deadlock
Example 8: Recursive stack overflow
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
int main() {
int result = factorial(-5); // ❌ negative → non-terminating recursion
return 0;
}
(gdb) run
Program received signal SIGSEGV
(gdb) backtrace
#0 factorial (n=-1048576) at stack_overflow.cpp:3
#1 factorial (n=-1048575) at stack_overflow.cpp:4
...
#1048575 factorial (n=-5) at stack_overflow.cpp:4
#1048576 main () at stack_overflow.cpp:8
# Negative n never satisfies termination → stack overflow
8. Common errors and fixes
“No symbol table” / “No debugging symbols found”
Cause: Built without -g.
Fix:
g++ -g -O0 main.cpp -o myapp
file myapp
# Look for "not stripped" or debug_info
“Cannot access memory at address 0x0”
Cause: Null pointer dereference.
Fix:
(gdb) backtrace
(gdb) frame 0
(gdb) info args
(gdb) print ptr # check for 0x0
“optimized out”
Cause: -O2/-O3 removed or register-allocated the variable.
Fix:
g++ -g -O0 main.cpp -o myapp
# Or volatile for one variable (last resort)
volatile int debug_var = value;
“Program received signal SIGSEGV” — unclear cause
Fix:
(gdb) backtrace full
(gdb) break [crashing function]
(gdb) run
(gdb) next # step and watch variables
GDB exits immediately after run
Cause: Normal exit, or crash in a child process.
Fix:
(gdb) set follow-fork-mode child
(gdb) break fork
(gdb) run
(gdb) continue
LLDB “variable not found”
Cause: Optimization or out of scope.
Fix:
(lldb) frame variable
(lldb) expr *(int*)0x7fff...
Hardware watchpoint limit
Cause: x86 often allows only four hardware watchpoints.
Fix: GDB may fall back to software watchpoints (slower, unlimited). Delete unused watchpoints if needed.
(gdb) delete 2
9. Debugging best practices
1. Always -g -O0 for debug builds
g++ -g -O0 main.cpp -o myapp
With optimization on, variables disappear, line numbers drift, and stepping misleads.
2. On crash, backtrace first
(gdb) run
# After SIGSEGV
(gdb) backtrace full
(gdb) info locals
(gdb) info args
3. Save time with conditional breakpoints
(gdb) break main.cpp:20 if i == 500
4. Watchpoints for mystery mutations
(gdb) watch suspicious_var
(gdb) continue
5. Use .gdbinit
# ~/.gdbinit or project/.gdbinit
set pagination off
set print pretty on
set print array on
6. Log the session
(gdb) set logging file debug.log
(gdb) set logging on
(gdb) run
# ... debug ...
(gdb) set logging off
Checklist
- [ ] Built with -g -O0?
- [ ] backtrace to locate crash
- [ ] frame N to select frame
- [ ] info locals, info args
- [ ] Conditional breakpoint for rare cases
- [ ] Watchpoint for memory changes
- [ ] thread apply all bt for multi-threaded issues
10. Production debugging patterns
Analyzing core dumps
Enable core dumps so crashes leave a file you can open later.
# Linux
ulimit -c unlimited
echo /tmp/core.%e.%p | sudo tee /proc/sys/kernel/core_pattern
# After crash
$ gdb ./myapp /tmp/core.myapp.12345
(gdb) backtrace
(gdb) backtrace full
Remote debugging (gdbserver)
# Target machine
gdbserver :1234 ./myapp
# Dev machine
gdb ./myapp
(gdb) target remote 192.168.1.100:1234
(gdb) continue
Split debug symbols
Keep debug info in a separate file for release binaries.
objcopy --only-keep-debug myapp myapp.debug
strip -g myapp
objcopy --add-gnu-debuglink=myapp.debug myapp
# Later
gdb -s myapp.debug -e myapp -c core.12345
Production flow
flowchart TD
A[Production crash] --> B{Have matching debug build?}
B -->|Yes| C[Collect core dump]
B -->|No| D[Reproduce elsewhere]
C --> E[gdbserver or local GDB]
D --> E
E --> F[Analyze backtrace]
F --> G[Find root cause]
G --> H[Fix and deploy]
Notes:
- The binary that produced the core must match the symbols you load.
-O2builds may show wrong or missing locals → RelWithDebInfo is a good compromise.- gdbserver pauses the process—use during low traffic.
Practical workflow
- Reproduce with minimal input.
- Isolate with breakpoints.
- Hypothesize from variable values.
- Verify after the fix.
- Document cause and resolution.
Command reference
GDB cheat sheet
run, r # run
run arg1 arg2 # with args
kill # kill inferior
break, b # breakpoint
info breakpoints # list
delete 1 # delete
next, n # next line
step, s # step in
finish # finish function
continue, c # continue
print, p # print
ptype # type
backtrace, bt # stack
info locals
info args
watch var
list, l # source
quit, q
GDB vs LLDB
| Feature | GDB | LLDB |
|---|---|---|
| Run | run | run |
| Break | break main | breakpoint set -n main |
| Conditional | break f.c:10 if i==5 | breakpoint set -f f.c -l 10 -c 'i==5' |
| Next | next | next |
| Step | step | step |
print x | frame variable x | |
| Stack | backtrace | bt |
| Watch | watch var | watchpoint set variable var |
| Quit | quit | quit |
Summary
| Tool | Platform | Notes |
|---|---|---|
| GDB | Linux | GNU debugger; gdbserver for remote |
| LLDB | Mac/Linux | LLVM debugger; fast |
| Visual Studio | Windows | GUI debugger |
Principles:
- Prefer a debugger over printf.
- Use breakpoints and conditional breakpoints.
- Use watchpoints for memory issues.
- Use the stack trace to locate cause.
- In production: core dumps + gdbserver.
Next: [C++ In Practice #16-2] Sanitizers: tools that find memory bugs automatically
FAQ
Q. When do I use this in practice?
A. Whenever printf is too slow or misleading: GDB/LLDB breakpoints, watchpoints, variable inspection, and stack traces to find bugs quickly—from scenarios above through production patterns. Use the examples and guides in this article.
Q. What should I read first?
A. Follow previous post links at the bottom of each article. See the C++ series index for the full sequence.
Q. Where can I go deeper?
A. cppreference and official library docs. Use the reference links at the end of articles as well.
Previous: [C++ In Practice #15-3] Compile-time optimization
Related posts
- C++ Sanitizers — ASan/TSan for memory bugs and data races
- C++ logging and assertions — intermittent production crashes
- C++ Segmentation fault — core dumps
Keywords
C++, debugging, GDB, LLDB, breakpoint, watchpoint, debugger, bugfix, segfault — searches like these should surface this article.
See also
- C++ GDB/LLDB — find in minutes what 100 couts could not
- C++ logging and assertions
- C++ GDB basics — breakpoints and watchpoints
- C++ Sanitizers
- C++ LLDB basics — macOS and breakpoints
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ GDB/LLDB | cout 100개 찍어도 못 찾은 버그, 디버거로 5분 만에 해결
- C++ GDB 기초 완벽 가이드 | 브레이크포인트·워치포인트
- C++ LLDB 기초 완벽 가이드 | macOS·브레이크포인트
이 글에서 다루는 키워드 (관련 검색어)
C++, Debugging, GDB, LLDB, Breakpoint, Watchpoint, Debugger 등으로 검색하시면 이 글이 도움이 됩니다.