views:

567

answers:

6

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?

+1  A: 

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.

Daniel A. White
+13  A: 

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.

Mason Wheeler
I figured as much lol but you have said it better.
Daniel A. White
Mason: good summary; watch out though that it is easy to leave some of these constructions in the code. So I suggest that it is done with some remarks (I use something with @@@) or macro's which are guaranteed not to end up in an actual product.
Adriaan
This is tagged Delphi, which doesn't have preprocessor macros or @@@ remarks. One simple way to make sure you don't leave any in is to grep your codebase for "int 3" before you build a release version.
Mason Wheeler
instead of adding assembly language, I usually add a statement of "volatile int e = 9;". This gives me a statement to put a breakpoint on. Also add a #pragma (if Delphi allows) to warn you that there's debug code.
Robert
You could do something like that. The equivalent to your #pragma would be something like: {$MESSAGE HINT Debug code here.}
Mason Wheeler
This is a bad answer! You don't want to risk leaving an errant int 3 in the program. Look at Pax's answer--a harmless line that you set a breakpoint on. This won't cause trouble if it ends up in production code.
Loren Pechtel
Which is why I suggested 2 different ways to keep that from happening. Here's a third: Put the line inside an {$IFDEF DEBUG} block.
Mason Wheeler
A: 

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.

Timo Geusch
It's probably not the conditional check itself, it's the context switches and the overhead of stopping and restarting the process. You don't notice that with a normal breakpoint since you land in the debugger anyway, but on a conditional where it keeps going it becomes very apparent very quickly.
Mason Wheeler
+5  A: 

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.

paxdiablo
+1 In this case the op probably could use something simple like if I = 600 then write; and put a breakpoint on the write clause.
Lieven
+1 for the Colossal Caves reference. :D
Mason Wheeler
+2  A: 

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.

François
And it will also give [Pascal Warning] Dist.dpr(72): W1002 Symbol 'DebugHook' is specific to a platform - making them easy to find before check in.
Gerry
+3  A: 

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@

Nat