How to break in at the call site that invokes the break point


I think everyone at some point in time wants to embed a break point in there code, whether it be for debugging purposes, path tracing, or detecting edge conditions that have not yet been tested. When I hit a break point, I would prefer that the debugger break in at the call frame which needs the break point and not another function which implements the break point. I have seen two different patterns over the years.


The first pattern is to call DbgBreakPoint(). This works well enough. It is portable across different processor architectures so you don’t have to worry about new platforms, but it has one major problem. When you break into the debugger, you are in the wrong call site! You end up in the middle of DbgBreakPoint() itself and not the caller. You can use the gu command, but that requires typing ;).


The second pattern is to create a #define for some inline assembly, something akin to:

#define TRAP __asm { int 3 }

This satisfies my requirement that the point of execution when we stop is in the function of interest, but this solution is platform specific. You would need enough knowledge per supported platform for this to work and that platform must support inline assembly (which x64 does not for our compilers). You could compromise and #define TRAP to DbgBreakPoint() for those platforms, but then you have a satisfactory solution on a subset of platforms.


Enter the __debugbreak() intrinsic that I just learned about this weekend from the OSR WinDBG mailing list (courtesy of Pavel A.) Yes, it is a Microsoft specific extension, but generating a break point is inheritly platform specific already. __debugbreak() is the best of both patterns. You get platform independence and when you break in to the debugger, you are sitting at the right call frame and not inside a system routine. That rocks in my book!


PS: I don’t know which version of the compiler this intrinsic was introduced, but it is used in the Server 2003 SP1 DDK, so I know it has been implemented for awhile.


PPS:  I never call __debugbreak() in production code, and IMHO, neither should you.  To control this in a DDK build environment, I do the following

#if DBG
#define TRAP() __debugbreak()
#else // DBG
#define TRAP()
#endif // DBG

Comments (8)

  1. strik says:

    I hope it is not only __debugbreak() which you do not call in production code, but also your macro TRAP() and DebugBreak(), which do not belong in production code.

    Or are there legitimate reasons to add them in production code?

  2. doronh says:

    look at how TRAP() is defined.  It only does something if DBG is defined (e.g. a chk build).  If DBG is not defined or is zero, TRAP() evaluates to a no-op, so it still exists in the code .c(pp) file, but does not exist in the generated binary.

    Embedding an int3 or DbgBreakPoint in a release driver can have disastrous affects.  If that code is executed and a kernel debugger is not attached, the machine bug checks due to an unhandled exception.

  3. Skywing says:

    __debugbreak was added in CL 13 (VC7), IIRC.

  4. doronh says:

    thx skywing, that is good info to have

    d

  5. Ishai says:

    Since __debugbreak()  is a compiler intrinsic it does not prevent optimizations like inline __asm {int 3} does.  As a result, if you have two instances of  if (something failed) { TRAP(); return STATUS_SOME_ERROR; } the compiler tends to generate a single breakpoint for both calls which is probably not what you wanted (at least not what I wanted when I moved to __debugbreak())

  6. doronh says:

    Ishai, I don’t see this as a problem.  Why?  Because  my TRAP()s are not on in an optimized free build.  My debug builds, where TRAP() is turned on, are not optimized so the problem does not show up.    A chk optimized build is possible, but I rarely find them useful.

    If you really wanted the TRAP()s to not be folded, I think you could do something like this (I haven’t tested this on an optimized build to see if it really works though)

    #define TRAP()              
    __pragma optimize("", off)  
    __debugbreak()             
    __pragma optimize("", on)
    

    d