Improving Analysis with C# 6 Exception Filters

C# 6 recently added support for exception filters, which enable a few helpful scenarios. In this post, I’ll demonstrate how they can be used to improve debugging and crash dump analysis.

C# 5 Implementation

Consider the following code where we call an API that throws an exception. Normally, we swallow the exception and continue. However, if the status is UnknownError, we log a message before rethrowing the exception.


using System;
using System.Net;
class Program
{
    static void Main(string[] args)
    {
        try
        {
            var webClient = new WebClient();
            Console.WriteLine(webClient.DownloadString("abc"));
        }
        catch (WebException e)
        {
            if (e.Status == WebExceptionStatus.UnknownError)
            {
                Console.WriteLine(e.Message);
                throw;
            }
        }
    }
}

While this code is functionally correct, we lose some of the original context when the exception is rethrown. When the exception crashes the program, the state of the webClient is no longer available. You can see from the callstack window that the debugger is stopped at the throw statement on line 18.


unwound_callstack

C# 6 Exception Filter Implementation

We can address these shortcomings with an exception filter. The new syntax involves adding a when clause. If the filter evaluates to true, the exception is handled, and the catch block executes as before. Otherwise, the exception is unhandled, leaving the callstack at the original failure.


static void Main(string[] args)
{
    try
    {
        var webClient = new WebClient();
        Console.WriteLine(webClient.DownloadString("abc"));
    }
    catch (WebException e) when (HandleException(e))
    {
    }
}
private static bool HandleException(WebException e)
{
    if (e.Status == WebExceptionStatus.UnknownError)
    {
        Console.WriteLine(e.Message);
        return false;
    }
    return true;
}

Now we know the original context of the failure since we’re still in the try block. We can examine webClient in the locals window.


original_callstack

This process works for both a live debugging session and opening a crash dump. In a complex application, examining the state of the original crash context before the stack is unwound can provide valuable clues about the root cause of a failure.