views:

234

answers:

4

How do breakpoints work in C++ code? Are they special instructions inserted in between some assembler instructions when the code is compiled? Or is there something else in place? Also, how are stepping-through-the-code implemented? The same way as breakpoints...?

+1  A: 

According to this blog entry on technochakra.com you are correct:

Software breakpoints work by inserting a special instruction in the program being debugged. This special instruction on the Intel platform is “int 3″. When executed it calls the debugger’s exception handler.

I'm not sure how stepping into or over the next instruction is implemented though. However, the article goes on to add:

For practical reasons, it is unwise to ask for a recompilation whenever a breakpoint is added or deleted. Debuggers change the loaded image of the executable in memory and insert the “int 3″ instruction at runtime.

However, this would only be used for the "run to current line option".

ChrisF
+2  A: 

Single stepping is implemented at (assembler) code level not at C++ level. The debugger knows how to map the C++ code lines to code addresses.

There are different implementations. There are CPUs that support debugging with breakpoint registers. When the execution reaches the address in the breakpoint register, the CPU executes a breakpoint exception.

A different approach is to patch the code for the time of execution with a special instruction, at best a one-byte instruction. At x86 systems that usually int 3.

The first approach allows breakpoints in ROM, the second allows more breakpoints at the same time.

harper
+9  A: 

This is heavly depend on the CPU and debugger.

For example, one of the possible solution on x86 CPU:

  • Insert one-byte INT3 instruction on the required place
  • Wait until breakpoint exception hits
  • Compare exception address to the list of breakpoint to determine which one
  • Do breakpoint actions
  • Replace INT3 with original byte and switch the debugged process into trace mode (step-by-step execution of CPU instructions)
  • Continue debugged process
  • Immediately you catch trace exception - the instruction was executed
  • Put INT3 back

Watchpoints can be implemented in the similar way, but instead of INT3 you put the memory page where watched variable is into read only, or into no access mode, and wait for segmentation exception.

Stepping through assembly can also be done by using trace mode. Stepping through source lines can also be done by placing breakpoints onto next instructions, based on debug data.

Also some CPU has hardware breakpoint support, when you just load address into some register.

Xeor
I don't understand the "replace INT3 with original byte" part... Why do we need to do this?
gablin
After resuming from breakpoint, you need to execute the CPU instruction where you stopped, but you corrupted it - replaced the first byte with INT3 opcode. So you need to restore it, let the instruction be processed by CPU, and then put INT3 back - so the next time this instruction is executed, you get break again
Xeor
Oh, I see. So _that's_ how the debugger "injects" the breakpoints in the code. Thanks!
gablin
A: 

AFAIK all debuggers (for whatever compiled language) that allow an unlimited number of breakpoints use a variant of replacing the instruction to be breakpointed with a special value (as described above) and keeping a list of places where these values have been placed.

When the processor tries to execute one of these special values, an exception is raised, the debugger catches it and checks if the address of the exception is on its list of breakpoints. If it is, the debugger is invoked and the user is given an opportunity to interact. If it is NOT, then the exception is due to something that was in the program from the outset and the debugger lets the exception 'pass' to whatever error handler might be there.

Note also, that debugging self-modifying code can fail precisely because the debugger momentarily modifies the code itself. (Of course, nobody would ever write self-modifying, now would they? >;-)

For these reasons, it is important that the debugger be given the opportunity to remove all the breakpoints it sets before terminating the debugging session.

smirkingman
" it is important that the debugger be given the opportunity to remove all the breakpoints it sets before terminating the debugging session." Nah -- the debugger only modifies the image in memory, not original file on disk.
Jerry Coffin
Indeed, but if you end the debugger and let the program continue, it must remove the in-memory breakpoints before letting the program go
smirkingman