Nothing is simple

There is no such thing as a "simple" feature. The example of-the-day is Debugger.Launch, the API that will launch a managed debugger with special command line arguments that will direct it to automatically attach it to the calling app (see here for details). We call this a "just-in-time (JIT) attach".

The basis pseudo code would be:
            if (Debugger.IsAttached) return;

Sounds easy enough, right? 

Once you weed through all the issues like security ("am I allowed to launch a debugger"), policy ("which debugger do I launch?"), etc in the LaunchTheDebugger() phase, you still have a giant surface area for design holes and bugs:

  1. Launch() needs to block waiting for the debugger to attach. How do you do this in a thread-safe, race-free manner?  Does it wait with a timeout or infinite?

  2. What happens if Launch() is waiting for the newly-spawned debugger process to attach and another debugger attaches instead? This could easily happen if the spawned process has some wait dialog, and the user decides to manually attach a specific debugger instead.

  3. What if the spawned process decides that it doesn't want to debug after all? Can it abort?

  4. What if the spawned process is just a proxy that wants to launch the real process? For example, VS registers a jit-debugger process that just lets you choose the real debugger you want (eg, VS2003 or Vs2005).    What if the spawned process starts the real debugger process, but exits before the real debugger attaches? (If the abort from #3 is based off the lifetime of the spawned process, then this would be broken).

  5. What if multiple threads call Debugger.Launch at the same time? Do they each spawn their own dialogs and try to initiate their own jit-attach? That would be ugly, so you probably want them to coordinate with each other.

  6. What if a thread calls Debugger.Launch multiple times in a row. Does it pop-up a request each time? The requests could be an hour apart, or they could be a millisecond apart.

  7. What if you do a jit-attach, detach, and the re-attach?   Better make sure that whatever state Debugger.Launch keeps around for the jit-attach is properly cleaned up by-detach.

  8. What if you've got multiple threads calling Debugger.Launch, and Thread 1 triggers a jit-attach and that debugger immediately detaches, and this all happens before Thread 2 hits the wait alluded to in question #1?   Naively, Thread 2 may have originally noticed that Thread 1 was going to handle the jit-attach, not have realized the debugger already detached, and then may hang forever waiting for a debugger to attach.

You could probably rattle off a dozen more interesting multi-threaded races. There are so many moving parts: multiple-threads trying to share 1 jit-attach request, threads having to block waiting for the attach, jit-attach aborting, jit-attach succeeding but detaching immediately, another debugger trying to attach during this window, etc...

I just rewrote the mechanism for managed jit-attach. I foolishly thought it would be simple, and then quickly realized how much surface area there was. Nothing is easy!

Comments (2)

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

Skip to main content