CLR SPY and Customer Debug Probes: The Object Not Kept Alive and Buffer Overrun Probes (A Quiz)

The Object Not Kept Alive and Buffer Overrun probes are unlike any other CDPs, because they do not output any messages to report bugs in your code.  Instead, they change general CLR behavior with the goal of forcing non-deterministic bugs that can be almost impossible to reproduce into bugs that will happen every time you run your application.  As a developer or tester, that's obviously a good thing.  The lack of a specific probe message might make such bugs somewhat harder to diagnose, but once you understand the probes' behavior, you can learn to be successful with them.

The Object Not Kept Alive probe forces a garbage collection (then waits at most a second for pending finalizers) immediately before every transition from managed to unmanaged code.  This is done to surface problems that could occur if a managed object happens to be collected during the small time window when transitioning to unmanaged code.  I find this to be one of the most useful of all the CDPs.

The Buffer Overrun probe forces a garbage collection (then waits at most a second for pending finalizers) immediately after every transition from managed to unmanaged code.  The intent of this is to catch buffer overruns, which unmanaged code has the power to cause.  (Managed code does too -- even verifiable managed code if it uses methods of the Marshal class, which require unmanaged code permission.)  If unmanaged code writes into memory that it's not supposed to, it could potentially corrupt the GC heap.  The next time the garbage collector compresses the heap (as part of a collection), it's likely to run into an access violation.  So by forcing a collection after every call into unmanaged code, the corruption can hopefully be noticed at the source of the problem.

Due to the large number of collections forced by these probes, they will significantly slow down most applications.

Now consider the following harmless-looking C# code that uses the Microsoft.Win32.RegistryKey class:

  // This code has a bug!

  public static void Main ()

  {

    // Open the key for read-only access

    RegistryKey key = Registry.LocalMachine.OpenSubKey(

      "Software\\Microsoft\\.NETFramework");

    Console.WriteLine("Install root: " +

      key.GetValue("InstallRoot"));

  }

Most likely, this code will run fine if you don't enable any probes.  If you enable the Object Not Kept Alive debug probe and compile this code with debugging information ("csc /debug ObjectNotKeptAlive.cs" from a command prompt, or the Debug solution configuration in Visual Studio .NET), you'll still get the output that you'd expect when running the program.  For example:

  Install root: c:\windows\microsoft.net\framework

But if you compile it without debugging information ("csc ObjectNotKeptAlive.cs" from a command prompt, or the Release solution configuration in Visual Studio .NET) with the Object Not Kept Alive probe still enabled, you get surprising output:

  Install root:

Weird, huh?

Do you see any bugs in the code?  Who can tell me why this happens with the probe enabled?  Why does it only happen in the debug configuration?  My last quiz was answered pretty quickly, so please contribute any of your thoughts!