Debugging & Tooling

stepping through a program

Once a debugger has paused your program at a breakpoint, you need a way to nudge it forward a little at a time and watch what happens - like advancing a recipe one instruction at a time, checking the bowl after each. Stepping is exactly that controlled forward motion. Rather than letting the program run free again, you move it one source line (or one function call) at a time, inspecting variables between each move, so you can watch the bug develop in slow motion.

There are four moves you reach for constantly, and the difference between two of them is the classic beginner trap. 'step' (gdb: step) runs the next source line, and if that line calls a function, it goes INTO that function so you can watch it run. 'next' (gdb: next) also runs the next line, but if that line calls a function, it runs the whole call to completion and stops on the following line - it steps OVER the call. So step dives in; next skips past. 'finish' (gdb: finish) says 'I am inside a function, just run the rest of it and stop when it returns', handy when you stepped into something boring. 'continue' (gdb: continue) abandons stepping entirely and lets the program run at full speed until the next breakpoint or the end. lldb uses the same words: step, next, finish, continue.

Why it matters: stepping is how you turn a static guess into a live observation of cause and effect - you literally watch which branch is taken, which value changes, where execution actually goes versus where you assumed it went. The discipline is to step OVER (next) the functions you trust and step INTO (step) only the ones you suspect, or you will waste your life single-stepping through library code. And remember the honest limit: stepping moves at human speed, so it cannot reproduce timing-dependent bugs like races, and on optimized builds the source lines may be reordered or merged so stepping jumps around confusingly.

Stopped at a call result = compute(data); in gdb: next runs the whole compute() call and stops on the line after, treating it as a black box. step instead enters compute() so you can watch it line by line. finish (once inside) runs compute() to its return and shows the returned value.

next steps over a call; step goes into it; finish runs the current function to its return.

The classic mix-up is step versus next: step descends into every called function (including library calls you do not care about), while next runs them to completion in one move. Use next by default and step only into code you actually suspect.

Also called
single-steppingstep / next / continue / finish逐步執行逐行執行