views:

287

answers:

5

After spending a little time wondering why my app was running a particular scenario very slowly with the debugger attached, I discovered that this was due to having a conditional breakpoint (whose condition was never being met). This seems reasonable, as the CPU would signal the breakpoint and VS would need to evaluate the condition before allowing execution to continue. These transitions must be costly.

I assume that a breakpoint in a code path that is not executed has no runtime impact.

So my question is twofold:

  1. Are there any resources that can quantify the cost associated with conditional breakpoints, and if so is there anything one can do to reduce their runtime evaluation cost?
  2. Is there any cost associated with a 'disabled' breakpoint? By disabled I mean that VS displays the breakpoint marker in the gutter with a hollow circle.

Of course if anything I've mentioned above doesn't make sense, then please point me in the right direction.

A: 

I read somwhere that there is hardware support for these breakpoints, so that using fewer than x conditional breakpoints of a certain kind is essentially free, but above that, it needs to use more software. (OTOH, that was for native apps, not sure about these newfangled JIT things.)

Disabled breakpoints should take affect things at all, they just occopy some memory and GUI resources in IDE.

Marcus Lindblom
A: 
  1. Try putting the breakpoint in your code to test the performance. E.G.

    Stopwatch st = new Stopwatch(); st.Start(); if(my condition) { st.Stop(); Debugger.Break(); }

    No, not exactly the same but close enough.

  2. No - a disabled breakpoint is not present in the executing program. It is just stored in the VS metadata for your convenience.

RichAmberale
+5  A: 

It's hard to quantify the cost of a conditional breakpoint. The expression in a conditional breakpoint is evaluated using the exact same semantics as if you had typed it into the watch or immediate window. Expressions of this nature are not actually executed in the client program but instead handled by the language specific expression evaluator. It's not really possible to profile these types of evaluations in a meaningful way.

However I can list a few things that are known to be slower in a debug window eval

  • Function Calls: They are the slowest thing you can do because the function call requires the debuggee process to be restarted so that the func eval can occur in the process
  • String comparison: Under the hood these go back to func evals

As for disabled breakpoints, no they do not affect the running of the application.

JaredPar
Evaluating a conditional breakpoint incurs the cost of both the debugger interrupt as well as the evaluation of whatever expression is used for the condition. One way to reducing the cost (if it is causing difficulty in debugging an application) is to modify the source code to test the condition using a regular if() followed by a null statement (;) and then place a non-conditional breakpoint there. This isn't always possible (and obviously requires access to the source), but is a way of reducing the cost of the conditional evaluation.
LBushkin
@LBushkin, the cost of a conditional breakpoint is unfortunately much more complex than that. Expressions typed in the debugger are evaluated in a mixed style where part occurs on the debuggee and part in the debugger (sometimes only in the debugger process). It's really hard to summarize the dance that occurs in a SO post. But yes you are correct about optimizing by using an if() block. I do that when a complex conditional is needed.
JaredPar
Thanks for your clear explanation.
Drew Noakes
A: 

I have also noticed that conditional breakpoints are expensive and came to the same conclusion as you. I can't imagine any reason that a disabled breakpoint would cause any slowdown since I would expect it to be an editor only thing, a shortcut for turning on a breakpoint should you need it.

What I do when I am in a situation like yours is make an assert macro. (you can use the assert macro that visual studio provides, but I dont like it). Have your macro check the condition you want and then call DebugBreak if it fails. In the realease, or non-checked build of your application, have assert evaluate to nothing, so your code is not impacted.

A simple assert macro can look like:

#ifdef _DEBUG
#define assert(x)\
do{\
  if(!(x)){DebugBreak();}\
}while(0)
#else
#define assert(x)
#endif

and call it like:

assert(pValue != NULL && "A bad parameter was passed to the function");

You can put more code in the failure before DebugBreak (like printing out the condition that failed with #x, and/or the line/file number with __FILE__ and __LINE__ so you can double click on the message). You can write messages to the debug log with OutputDebugString and even check to see if a debugger is attached with IsDebuggerPresent to tailor your assert even more. I also like to use the && string format to give a bit more information on the particular assert.

There are a few things to be careful of when using assert. First, do not put any code that MUST be run in the macro, since it will be stripped in a non debug build. For the same reasons, don't put in code that has side effects. Also, you do not want to call DebugBreak() when a debugger is not attached because it essentially throws an exception, which if not caught, will terminate the application.

Dolphin
+1  A: 

One thing to watch out for (which I learned the hard way) is to make sure you put == when comparing a variable to a value and not the single =

The breakpoint editor doesn't warn you but when the breakpoint is evaluated, the variable is altered! Took me a while to debug my code with that one!

Also, if I really need the conditional breakpoint to bebug code; I add the condition to the the code, then add something like string stop = "here"; and put a normal breakpoint there - I find the code runs faster then.

JLWarlow
On your last point -- that only works if you can edit the code, and in the case of a hard-to-reproduce bug, if you're happy to restart the process.
Drew Noakes