How do breakpoints work in C++ code?

2019-01-16 10:16发布

问题:

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:

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.



回答2:

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".



回答3:

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.



回答4:

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.