I think I will not be mistaken if I write that every programmer has ever used a debugger, debugged the program step by step, installed breakpoint, etc. However, some programmers do not like to debug. Others adore. And most simply use the debugger without thinking about love and hate, because this is just another handy tool for work.
For many programmers, debuggers are a black box. They know how to handle it, but do not know how it works. I'm not saying that this is bad - in most cases you can easily debug the program without knowing the debugger device.
And for those who want to look inside the black box, I wrote this short article.
Here I will talk about one of the most mysterious (at least for me) debugger capabilities - about working with breakpoints. I will try to tell it as simply as possible and without unnecessary details. However, I am writing this article for those readers who already know what breakpoints are and know how to use them.
Have you ever wondered what happens when you press Insert Breakpoint? Or how the debugger can add breakpoints to the code on the fly and delete them? How do breakpoint conditions work? Whether multiple set breakpoint slows down the program?
First, a little theory:
Surely many of you have already encountered or heard about the assembly instruction
int 3 (
_asm int 3 in C ++). This instruction is the basis of the entire breakpoint system.
When the processor encounters this instruction, it initiates a debugging exception and sends it to the operating system interrupt handler. And the operating system converts it into a software exception and initiates it in the place of the executable file in which this
int 3 was encountered. The type of this exception is well known - it is STATUS_BREAKPOINT (0x80000003).
If a debugger is not attached to the process that triggered this exception, this process will drop. And you will see messages like these:
This is possible if you are running a debug build or have left
int 3 in your release build in your code.
')
If the process worked under the debugger (and usually STATUS_BREAKPOINT exceptions are used by the debugger), then this exception is passed to the debugger and then the debugger processes it.
Also at the processor level, break-points are supported that trigger on accessing the memory area (read, write, or code execution can be caught). There can be no more than four such breakpoints. They work in a different way and are global for the system as a whole, and not just for the application being debugged. I will not dwell on their work in detail, but those who wish can
google about the registers DR0-DR7 .
Now practice:
When you set breakpoint in the debugger, the following happens:
The debugger translates the memory area where you set the breakpoint to read-write mode (by default, the memory area with read only executable code) so that you can change the code there on the fly. Then the debugger replaces the assembler instruction in the place where you set the breakpoint with
int 3 , and remembers this replaced instruction in its memory.
At the same time, whenever you look at the assembler code in the debugger, it replaces these
int 3 with real instructions, but in fact there is
int 3 hidden from your eyes.
Also, the debugger always replaces all
int 3 back with real instructions when the process stops at breakpoint and control is transferred to the user.
When a breakpoint under the debugger is triggered, then:
The debugger replaces all
int 3 (not only the one that worked, but all) with the correct instructions that he remembered when inserting these
int 3 , and shows the breakpoint to the user already with normal code. Then, when the user starts the application to run further, the debugger again inserts
int 3 in all the right places.
If a breakpoint with a condition, then when it is triggered, the debugger first checks the condition and only when the condition is met does all the work, as with a normal breakpoint. If the condition did not work, the debugger executes the stored command, replaced by
int 3 , and then continues execution with the instruction after
int 3 .
When you remove a breakpoint, then:
The debugger simply replaces the inserted
int 3 back with the desired command and forgets about this breakpoint.
Conclusions that can be drawn from the foregoing:
Do the breakpoints brake if there are a lot of them?
No, if they are unconditional. A simple breakpoint does not require additional debugger resources or a process to be debugged - it is just an
int 3 instruction. Even if there are a million of them, but they do not work, then this will not slow down the execution of the program for a millisecond.
But if you set a breakpoint with the condition, then even one can slow down the program several times if it is in an often called place, since in each such breakpoint an interrupt will be triggered, then an exception will be raised, passed to the debugger, which will check the condition, replace the commands and return execution back to the breakpoint location.
Breakpoints for memory also do not slow down, because they are supported at the processor level.
It should also be noted that when you start the application under the debugger, then at the time of launch it will replace
int 3 with all instructions in the breakpoint points. And, if there are a lot of them, it may take a long time (for example, MS Visual Studio 2005 debugger when installing a breakpoint in a large project (C ++) in a frequently used template function, for example std :: vector :: operator [], sometimes hangs for a minute) .
PS:
Everything I wrote here does not apply to breakpoint in interpreted programming languages - I don’t know exactly how they work there. There the interpreter is responsible for everything and the breakpoint work scheme in it can be any theoretically. The same applies to code running under virtual machines.
PS2:
I specifically wrote all the terms (breakpoint, debugger, crash, etc.) in Russian so as not to mix the English and Russian text and to make it easier to read. I also specifically called the breakpoint breakpoint, since It seems to me more correct.
Test: 193784457