Debugger.Break()


System.Diagnostics.Debugger.Break() is a BCL method that causes a program to issue a User Breakpoint when run under the debugger.

This translates to a Break() debug event on ICorDebugManagedCallback. (Not to be confused with Breakpoint(), which corresponds to actual breakpoints. Yeah, we could have given them better names…)

The debugger will break at the callsite of the Break(), and not actually inside the call. This is convenient because it means that the caller is on top of the stack, which is what you’d intuitively expect. It also means that Debugger.Break() behaves almost identical to if you had manually set a breakpoint at the location and hit it.

There’s also an IL ‘break’ opcode that does the same thing as Debugger.Break(). You can view Debugger.Break() as functional access to the break opcode. In both cases, it ends up as a call into the CLR engine which then does a few things:
– if no debugger is attached, it may cause a managed JIT-attach (similar to Debugger.Launch).
– if a debugger is attached, it will essentially do a step-out of the CLR engine’s frames to get back to the call-site. This essentially gets the mscorwks frames off the stack.
– once that step out completes, it sends the user breakpoint event to the debugger.

 

Usages:

This can be useful in a variety of ways.

  1. This is the managed counterpart to kernel32!DebugBreak().  (See more comparison of managed vs. native debug methods)
  2. VB’s ‘stop’ statement is implemented via Debugger.Break().  (JScript’s ‘debugger’ statement could be implemented on this too).
  3. It’s useful for assert dialogs or error checks.
  4. Strongly preferred way for instant break-into-the-debugger over exceptions.  Exceptions may modify your program’s actual control flow. Debugger.Break() won’t. It just notifies the debugger but has no other side-effects.

 

Quick demo:

Run this VB code:

Module Module1

    Sub Main()

        Console.WriteLine("Hi!")
        Stop
        Console.WriteLine("done")
    End Sub

End Module

And you stop in the debugger at the ‘stop’ statement. In the callstack you see:

>    ConsoleApplication2.exe!ConsoleApplication2.Module1.Main() Line 6 + 0x5 bytes    Basic
     [External Code]   

If you view the code in Reflector as C#, you see the Stop statement is Debugger.Break():

[STAThread]
public static void Main()
{
    Console.WriteLine("Hi!");
    Debugger.Break();
    Console.WriteLine("done");
}
Comments (3)

  1. terrajobst says:

    Hi Mike – can you read my mind? Yesterday I thought Debugger.Break() would make a great article in your blog 🙂

    One additional thing worth pointing out is that Debugger.Break() is great in scenarios where your program is actually started by someone else. For example, at the weekend I wrote a subversion hook (http://svnbook.red-bean.com/en/1.0/svn-book.html#svn-ch-5-sect-2.1) to integrate our bug tracker with version control. For those who don’t know how SVN hooks work: you write a small console application that does some work and in case of an error write the output to the Console.Error stream. This allows you to implement pre-commit checks, email notifications and stuff like that. However, debugging such an application is not that easy. Your app will only run a few seconds (so Debug -> Attach will only work if can click as fast as Lucky Luke). Also, you cannot start your app explicitly by an external application (which you could do to debug COM add-ins for example). So the only way to debug such an application in an easy way is to use Debugger.Break(). That way I could just check-in some code, let SVN start my hook app and – voilà – the JIT-attach feature kindly asks for the debugger.

    Another interesting scenario is conditional breakpoints. Although I like VS conditional breakpoints a lot (in particular the new tracepoint feature of VS 2005) using some piece of code in conjunction with Debugger.Break() is sometimes much easier. Beside the simplicity the performance is also much better for complex conditions (or condition that need to be evaluated many times).

    Best regards,

    Immo

  2. There's a good reason that methods on a "System.Diagnostics.Debugger" class are still compiled