When I'm debugging something that goes wrong inside a loop, say on the 600th iteration, it can be a pain to have to break for every one. So I tried setting a conditional breakpoint, to only break if I = 600. That works, but now it takes almost a full minute to reach that point, where before it was almost instantaneous. What's going on, and is there any way to fix it?
Conditional breakpoints in any debugger (I'm just surmising here) require the process to flip back and forth every time between your program and the debugger every time the breakpoint is hit. This process is time consuming but I do not think there is anything you can do.
When you hit a breakpoint, Windows stops the process and notifies the debugger. It has to switch contexts, evaluate the condition, decide that no, you don't want to be notified about it, restart the process and switch back. That can take a lot of processor cycles. If you're doing it in a tight loop, it'll take a couple orders of magnitude more processor cycles than one iteration of the loop takes.
If you're willing to mess with your code a little, there's a way to do conditional breakpoints without incurring all this overhead.
if <condition here> then
asm int 3 end;
This is a simple assembly instruction that manually sends a breakpoint notification to the OS. Now you can evaluate your condition inside the program, without switching contexts. Just make sure to take it out again when you're done with it. If an int 3 goes off inside a program that's not connected to a debugger, it'll raise an exception.
Normally condition breakpoints work by inserting the appropriate break instruction into the code and then checking for the conditions you have specified. It'll check at every iteration and it might well be that the way in which the check is implemented is responsible for the delay as it's unlikely that the debugger compiles and inserts the complete check and breakpoint code into the existing code.
A way that you might be able to accelerate this is if you put the condition followed by an op with no side effect into the code directly and break on that op. Just remember to remove the condition and the op when you're done.
It slows it down because every time you reach that point, it has to check your condition.
What I tend to do is to temporarily create another variable like this (in C but should be doable in Delphi).
int xyzzynum = 600;
while (true) {
doSomething();
if (--xyzzynum == 0)
xyzzynum = xyzzynum;
}
then I put a non-conditional breakpoint on the "xyzzynum = xyzzynum;"
line.
The program runs at full speed until it's been through the loop 600 times, because the debugger is just doing a normal breakpoint interrupt rather than checking conditions every time.
You can make the condition as complicated as you want.
Mason's explanations are quite good.
His code could be made a bit more secure by testing that you run under the debugger:
if (DebugHook <> 0) and <your specific condition here> then
asm int 3 end;
This will not do anything when the application is running normally and will stop if it's running under the debugger (whether launched from the IDE or attached to the debugger).
And with boolean shortcut <your specific condition here>
won't even be evaluated if you're not under the debugger.
Further to Mason's answer, you could make the int 3 assember only be compiled in if the program is built with the debug conditional defined:
{$ifdef debug}
{$message warn 'debug breakpoint present in code'}
if <condition here> then
asm int 3 end;
{$endif}
So, when you are debugging in the ide, you have the debug conditional in the project options. When you build the final product for your customers (with your build script?), you wouldn't include that symbol, so it wont get compiled in.
I also included the $message compiler directive, so you will see a warning when you compile letting you know that the code is still there. If you do that everywhere you use int 3, you will then have a nice list of places which you can double click on to take you straight to the offending code.
N@