Debugging & Tooling

the backtrace and inspecting frames

When something goes wrong deep inside a program, the most urgent question is 'how did we even get here?'. Imagine a chain of phone calls: the boss called a manager, who called a clerk, who called the front desk - and the front desk is now stuck. A backtrace is the written-down version of that whole chain. It lists, from the innermost function where the program currently is, back out through each caller, all the way to main - so you can read the exact path of calls that led to this moment.

Concretely, every function call pushes a stack frame onto the call stack - a small block holding that call's local variables, arguments, and the return address back to its caller. A backtrace simply walks that stack of frames from the top (where you are paused or crashed) down to the bottom (main), printing each one. In gdb you type 'bt' (backtrace) and see numbered frames: #0 the current function, #1 its caller, #2 that one's caller, and so on, each with the function name, its arguments, and the source line. You can then 'select' a frame to inspect it - 'frame 2' in gdb moves your view up to caller #2, and now 'print' shows that frame's local variables. This lets you ask 'what arguments did the caller pass?' and 'what were that function's locals when it made the call?', climbing the chain until you find where the values first went wrong.

Why it matters: a backtrace is usually the single most informative thing you get when a program crashes or stops - it instantly tells you the call path, which often points near the bug. The technique is to read it from the crash outward: the top frame is where the symptom appeared, but the actual mistake is frequently a frame or two down, where a caller passed a bad pointer or a wrong size. Honest caveats: optimized builds can inline functions so frames are missing or merged; a corrupted stack (say from a buffer overflow) can produce a garbled, nonsensical backtrace, which is itself a strong hint that memory was trashed; and without debug info you may see only addresses or '??' instead of names.

After a crash, gdb's bt prints: #0 parse (s=0x0) at parse.c:30 #1 load (path=...) at load.c:12 #2 main () at main.c:5. Frame #0 segfaulted because s is 0x0; but frame 1 reveals load() never checked the result of a failed open before passing it down - the real bug is one frame up.

bt shows the call chain; the crash is in #0 but the root cause is in #1.

The top frame is where the symptom surfaced, not necessarily where the bug lives - read downward to find the caller that supplied the bad value. A nonsensical or truncated backtrace often means the stack itself was corrupted, which is a clue, not a dead end.

Also called
backtracecall stack tracestack tracebt回溯呼叫堆疊