Intuition The one core idea
A running program is invisible because the CPU executes billions of instructions per second — far too fast for a human to watch. A debugger like GDB installs tiny traps that freeze the program at moments you choose, so you can look at its memory, variables, and the chain of function calls before letting it run on.
This page builds every word, symbol, and picture the main GDB note leans on — starting from "what even is a running program in memory?" Nothing below assumes you have seen assembly, hexadecimal, or a stack diagram before. Each block earns the next.
Before we can stop a program, we must picture what is there to stop.
Definition Machine code, instructions, and the instruction pointer
Machine code — your program, after compiling, is a long list of tiny numbered commands the CPU understands directly (add these two numbers, jump here, load that byte). Each command is called an instruction .
Instruction Pointer (IP) — a special number the CPU keeps that says "which instruction am I about to run next?" It is just an address (see below). After running one instruction, it moves to the next.
Picture: a conveyor belt of numbered boxes (instructions). A little pointer arrow (the IP) slides along, box by box, thousands of millions of times per second.
The topic needs this because a breakpoint is nothing more than making the IP stop moving at a box you chose. Everything else is detail.
Definition Byte, address, and memory
Byte — the smallest chunk of memory the machine names individually: a slot holding a number from 0 to 255.
Address — the house number of a byte. Memory is one enormous street of numbered houses; the address 0x4005a2 means "the byte living at house number 4005a2".
Picture: a very long row of mailboxes, each with a number painted on it. An address is that painted number; the byte is what's inside the box.
Intuition Why the topic keeps saying "address"
When GDB prints 0x... in process (p=0x0), that 0x... is an address — a house number on the memory street. It is showing you where in the code the CPU currently stands. You cannot read a crash report without knowing an address is just a location number.
Definition Hexadecimal — counting in sixteens
Hexadecimal ("hex") is a way of writing numbers using 16 symbols: 0 1 2 3 4 5 6 7 8 9 a b c d e f, where a=10, b=11, ... f=15. The prefix ==0x== announces "the following is hex, not ordinary decimal."
Why hex and not plain decimal? One byte holds a value 0–255. In hex that is exactly two digits (00 to ff) — clean and compact. So programmers name memory in hex because each pair of hex digits = one byte.
Picture: a car odometer, but each wheel has 16 positions instead of 10.
0xCC
The breakpoint byte the parent note mentions, 0xCC, means: hex digit C (=12) in the sixteens place, hex digit C (=12) in the ones place → 12 × 16 + 12 = 204 in ordinary numbers. It is one byte (value 204), the special "trap" instruction. You now know why it's written with 0x.
Question: What is 0x1f in ordinary decimal? 1 × 16 + 15 = 31 .
The parent note talks about both "one source line " (for step/next) and "one instruction " (for the trap). These are not the same, and confusing them causes real bugs in understanding.
Definition Source line vs. machine instruction
Source line — one line of the C code you wrote, e.g. result *= i;.
One source line usually compiles to several machine instructions (load result, load i, multiply, store back).
Picture: one sentence in your language (source line) translated into several short words in the machine's language (instructions).
Intuition Why this distinction matters for
step and next
When you type step, GDB does not run one instruction — it runs machine instructions until the source line number changes . That's why "one step" can be many CPU actions. step and next both work at the source-line level; the trap trick works at the instruction level. Same idea (stop the IP), two different rulers.
Debug symbols are an extra table the compiler writes when you pass ==-g==. It is a translation dictionary connecting the machine's world back to yours:
address 0x4005a2 ↔ source file bug.c, line 3
register/stack slot ↔ the name result
Picture: a bilingual dictionary sitting next to the machine code, so GDB can say "result" instead of "the 4 bytes at 0x7ffe...".
Intuition Why the topic insists on
-g
Without this dictionary, GDB only sees house numbers, never names. watch result fails because "result" is a word that no longer exists to the machine. See Compilation & -g debug symbols for how the compiler builds this table.
Common mistake "Compiling normally should be enough to debug."
Why it feels right: the variable names are right there in your .c file. The truth: the compiled binary throws names away unless -g keeps a copy in the symbol table. Fix: always gcc -g for a debug build.
Definition Optimization level
==-O0, -O2, -O3== tell the compiler how hard to rewrite your code for speed. -O0 = "don't rearrange, keep it literal." Higher = "delete, merge, reorder anything as long as the answer is the same."
Picture: -O0 is a word-for-word translation; -O2 is a clever paraphrase that may drop redundant sentences entirely.
-O0 for debugging
An optimizer may keep result only in a fast CPU register for a moment, or delete it once it proves the value is unused — then GDB honestly reports <optimized out>. -O0 keeps every variable in a stable, inspectable place. See Optimization levels -O0 -O2 -O3 .
The command bt (backtrace) prints "the chain of function calls." To read it you must first picture the stack .
Definition Stack, frame, and backtrace
When function A calls function B, the CPU sets aside a fresh block of memory for B's local variables. That block is a stack frame .
Frames pile up like a stack of plates: main at the bottom, then factorial on top. The newest call is on top.
Backtrace = reading the plates from top to bottom: "we're in factorial, which was called by main."
Picture: a stack of trays. Each tray holds one function's private scratch paper (its local variables). bt reads the labels down the stack.
bt is the first crash command
A crash usually happens because a caller handed a bad value. The crashing function is the top plate; the culprit is often the plate below. frame 1 lets you climb down one plate to inspect the caller's variables. The deeper mechanics live in Stack frames & calling conventions .
Question: If main calls factorial and it crashes, which frame is number 0? factorial — the top of the stack, where execution currently sits.
Definition Trap and signal
Trap — a deliberate "STOP!" the CPU raises when it hits a special instruction (like 0xCC). The CPU pauses and hands control to the operating system.
Signal — the OS's way of tapping a program (or GDB) on the shoulder. The parent note names two:
==SIGTRAP== — "a breakpoint/trap fired, come look." This is how GDB gets woken up.
==SIGSEGV== — "Segmentation fault: the program touched memory it doesn't own" (e.g. following a p=0x0 null pointer).
Picture: a tripwire (0xCC) that rings a specific bell (SIGTRAP) so GDB comes running. A SIGSEGV is a different bell — the alarm for illegal entry. See Signals — SIGSEGV SIGTRAP .
Intuition Putting it together: how a breakpoint really works
GDB swaps the original byte at your chosen address for 0xCC. When the IP reaches it, the trap fires, SIGTRAP wakes GDB, it restores the original byte and hands you the prompt. A breakpoint is just byte-swap + tripwire.
Definition Debug register
The CPU has ~4 special slots called DR0–DR3 (debug registers). You load a memory address into one, and the chip itself watches that spot and traps on any write — no software checking needed.
Picture: instead of you re-reading a mailbox after every single step (slow), you install a doorbell on that one mailbox. The chip rings it automatically when someone changes the contents.
Intuition Why this makes watchpoints cheap or dear
A hardware watchpoint = the doorbell (near-zero cost). A software watchpoint = re-checking the mailbox after every line because no doorbell was free — hence the huge slowdown the parent note derives. Details in CPU debug registers DR0–DR7 .
The parent's cost formula uses four letters. Here is each, from zero:
Intuition Reading the formula as a sentence
"Total slowdown = 1 (you always pay the original time) plus the extra trap time as a fraction of the original." When c is tiny (hardware doorbell), N c ≈ 0 and S ≈ 1 . When c is a full software trap, N c dwarfs T 0 and S explodes.
Question: In S = 1 + N c / T 0 , which symbol becomes ≈0 for a hardware watchpoint? c (per-line trap cost) → so S ≈ 1 .
Bytes and addresses (hex 0x)
Machine instructions and the IP
Source line vs instruction
Signals SIGTRAP and SIGSEGV
Names for variables and lines
Debug registers DR0 to DR3
Slowdown S = 1 + Nc over T0
Cover the right side and test yourself. If any line is fuzzy, reread its section above before tackling the parent note.
A byte holds values in what range, and how many hex digits show it? 0 to 255, exactly two hex digits (00–ff).
0x in front of a number means...the number is written in hexadecimal (base 16).
The instruction pointer (IP) tells the CPU... which instruction it will run next; a breakpoint stops it from advancing past a chosen point.
One C source line compiles to... usually several machine instructions.
step and next measure progress in units of...source lines (not single instructions).
-g gives GDB...a debug-symbol dictionary mapping addresses to variable names and line numbers.
-O0 matters because...it stops the optimizer from deleting/merging variables, keeping them inspectable.
A stack frame is... the private memory block holding one function call's local variables.
bt prints...the stack of frames — who called whom — top (current) to bottom (main).
0xCC (INT3) does what?raises a trap so the CPU stops and signals GDB (SIGTRAP).
SIGSEGV versus SIGTRAP?SIGSEGV = illegal memory access (crash); SIGTRAP = a breakpoint/trap fired.
Debug registers DR0–DR3 let the CPU... watch ~4 addresses in hardware and trap on write, with near-zero overhead.
In S = 1 + N c / T 0 , the letters mean... N =lines run, c =per-line trap cost, T 0 =normal time, S =slowdown factor.