Thursday, 30 May 2013

How to debug silent crashes in .Net

Here’s a quick note to my future self, and any other interested parties in the present on how to diagnose a particularly tricky kind of unhandled exception. I’m talking about those ninja exceptions which manage to evade any kind of unhandled exception logging you’ve rigged up around your .Net applications.

I was debugging just such a problem today. A customer would double click the application, and nothing would happen. Our application is configured to log any unhandled exceptions to a log file. But no log file was being created. There was, however, an entry written to the application event log: it told me that a TypeInitializationException had been thrown, and even gave me a stack trace. But it told me nothing about why the type had failed to initialize.

To complicate matters, this was one of those heisenbugs where, though I could replicate it under normal circumstances, it would go away if I attached a debugger to the process. What I needed was a crash dump – a snapshot of the entire state of the application at the moment the exception happened.

DebugDiag was my first port of call. That’s a nifty tool from Microsoft which can be configured to create dumps from your application under particular circumstances,  including when it crashes. Handily, it will also analyse the dump file, and help you work out why the application crashed. Inexplicably, DebugDiag failed to capture any dumps for my application.

So I turned to Windows Error Reporting. From Windows Vista SP1 onwards you can tweak some flags in the registry, and have windows automatically capture dumps for you – assuming you’re using .Net 4.0.

Capturing dump files with Windows Error Reporting

All you need to do is set create a key at HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\[Your Application Exe Name]. In that key, create a string value called DumpFolder, and set it to the folder where you want dumps to be written. Then create a DWORD value called DumpType with a value of 2.

image

Now get those ninjas to crash your app.

You should see a *.dmp file appearing in the folder you chose. Double click it – and you’ll see some Visual Studio magic, introduced in VS 2010. You can debug a dump file almost as if it were a live application. When you see the Minidump File Summary screen, you just need to click Debug with Mixed.

image

Unmasking the ninja

So what was this ninja exception that was evading my logging routines? It was an exception being thrown by my exception handling code!

The original exception was a ConfigurationErrorsException, thrown by the configuration API when loading my application’s user settings from a corrupt user.config file. My exception handling code looked like this:

private void ReportUnhandledException(Exception exception)
{
    try
    {
        Tracing.TraceUnhandledError(exception);

        ReportExceptionToUser(exception);
    }
    catch (Exception fatalException)
    {
        Tracing.TraceUnhandledError(fatalException);
    }
}

The problem was caused by a static constructor in the Tracing class. We’re using the System.Diagnostics TraceSource api to log our exceptions, and the constructor was setting all of that up. The TraceSource API, it turns out, also tries to load the application configuration, so that was throwing an exception.

The solution? Use Environment.FailFast instead of trying to log the secondary exception. That simply writes a message to the event log then bails out, with no chance of causing mayhem by raising further exceptions.

private void ReportUnhandledException(Exception exception)
{
    try
    {
        Tracing.TraceUnhandledError(exception);

        ReportExceptionToUser(exception);
    }
    catch (Exception fatalException)
    {
        Environment.FailFast("An error occured whilst reporting an error.", exception);
    }
}

3 comments:

Dennis said...

Good information

Web Designer in Bangalore

Ladislau Radu Nagy said...

Very cool. Thanks.

Quentin Calvez said...

This is exactly what I was looking for, and saved me a lot of precious time trying to figure out what was happening with my app. Thank you very much ;) !

Post a Comment