The really low level implementation may vary a lot. A couple variants I have worked with: To halt execution temporarily, e.g. an explicitly declared breakpoint, or implicit by e.g. a 'continue to next line', the debugger looks up the address of the first instruction generated for that source line in the debug information. It copies and saves the first instruction, and inserts a special breakpoint instruction. Most modern CPUs provide a specific instruction, generating an internal interrupt, causing exeution of an interrupt handler provided by the debugger. Also, most modern CPUs have a mechanism for executing a single instruction and then cause a similar internal interrupt. And, the handlers are run at a low priority so that a higher priority interrupt, e.g. the clock, may preempt the debugger interrupt handler to let other processes have their CPU share. The debugger user dialog (e.g. to continue exectution, remove the breakpoint, display the current value of some variable etc.) takes place within this interrupt handler. When target execution is resumed, the debugger backs up the program counter to the start of the breakpoint instruction it has inserted, puts the saved "real" instruction into the code, sets the 'single instruction' flag to the CPU, and returns from the debug interrupt. The single instruction interrupt reinserts the breakpoint instruction, ready for the next time execution passes through this point in code. It resets the single instruction flag and returns from interrupt, and target execution continues until the next breakpoint. For 'run to next line', the debugger may find the first instruction of every relevant line in the code (usually limited to the current function and the continuation point upon return, but exception handlers may complicate this) and save all the instructions being overwritten. When any of the breakpoints are hit, the same restore-original / single step / reinsert breakpoint procedure is followed. The breakpoints remain until that scope is left, i.e. when the function is exited. The handler for that interrupt will restore all the original code, and then set breakpoints on all line starts in the new scope. Also if another function is called and another scope is entered, the debugger must set breakpoints in that scope. In the old days when memory was scarce, setting a huge number of line start breakpoints and saving information for each of them would break all memory limits. So when halted at one line, as the single current breakpoint, the debugger would