Writing minidumps with PostSharp aspects

In the comments for one of my previous posts, Writing an automatic debugger in 15 minutes, a reader suggested creating minidumps with PostSharp aspects. Although I proved (by the very scientific method of guesstimation – see Performance impact of running under MDbgEngine) that running under a debugger introduces only a 25% performance penalty, it is reasonable to try to avoid it altogether. Today’s post will be an investigation into using PostSharp as a means of injecting minidump creation code into exception handlers.

What we’re trying to achieve

Let’s recap the most important point about minidumps: they can’t be created in the usual catch-all exception handlers of ASP.NET (in Global.asax or an HttpModule), WinForms (Application.ThreadException) or the general AppDomain.UnhandledException, because when they execute, the stack frames of calls leading up to the exception (along with variable values) are already all lost. The only information available is a stack trace in the Exception object, but’s that’s a normal runtime feature (used by Elmah, for example) that doesn’t provide any value for minidumps. Therefore, we need a way to insert minidump creation code as close to exception throw instructions as possible.

There are at least a few ways to achieve this:

    • attach a debugger and respond to exception thrown events – covered in previous posts, this method has the additional benefit of applying to exceptions originating in external code, including the .NET Framework itself;
    • insert minidump calls before every throw instruction of your code – I think I don’t need to tell you how big of a maintenance nightmare that would be;
    • insert minidump calls into every catch handler of your code – there will probably be less of those than throw instructions, bit it’s still a very weak solution, and it will cost you some context information (stack frames lost between the actual throw and executing catch;
    • use PostSharp or a different aspect framework to inject code automatically – in this post I’ll show you where and how calls can be injected with PostSharp, to create the information-rich minidumps possible.

PostSharp and exceptions

Intercepting exceptions is the first part of the PostSharp tutorial: PostSharp Principles: Day 1 – OnExceptionAspect. It introduces the OnExceptionAspect, which we could extend the following way to generate minidumps:

  1. [Serializable]
  2. class ClrDumpAspect : OnExceptionAspect
  3. {
  4.     public override void OnException(MethodExecutionArgs args)
  5.     {
  6.         ClrDump.Dump();
  7.         base.OnException(args);
  8.     }
  9. }

There is a convenient way of attaching this aspect to all methods in an assembly, described in PostSharp Principles: Day 3 – Applying Aspects with Multicasting Part 2:

  1. [assembly: ClrDumpAspectDemo.ClrDumpAspect]

Here’s what an OnExceptionAspect does:

The OnException method is called when an unhandled exception occurs in the target method. PostSharp will wrap the target method in a try/catch where the code we provided will be placed in the catch.

Indeed, PostSharp wraps each and every method in an exception handler:

Decompiling with ILSpy shows what the Main method is rewritten to:

  1. private static void Main(string[] args)
  2. {
  3.     try
  4.     {
  5.         Program.Foo(1);
  6.     }
  7.     catch (Exception arg_0C_0)
  8.     {
  9.         MethodExecutionArgs args2 = new MethodExecutionArgs(null, null);
  10.         Program.<>z__Aspects.a2.OnException(args2);
  11.         throw;
  12.     }
  13. }

Personally, I don’t like the fact that every method is now wrapped in a try-catch, but since wisemen say that it doesn’t have a negative performance impact (one proof of that is here), we could go along with it. But there a different problem. Take a look at this method (also after rewriting by PostSharp):

  1. private static void Throw(int i)
  2. {
  3.     try
  4.     {
  5.         if (i == 1)
  6.         {
  7.             int j = i + 2;
  8.             if (j == 3)
  9.             {
  10.                 throw new Exception();
  11.             }
  12.         }
  13.         if (i == 2)
  14.         {
  15.             throw new Exception();
  16.         }
  17.     }
  18.     catch (Exception arg_38_0)
  19.     {
  20.         MethodExecutionArgs args = new MethodExecutionArgs(null, null);
  21.         Program.<>z__Aspects.a5.OnException(args);
  22.         throw;
  23.     }
  24. }

This code exhibits the same problem that I described earlier, when talking about generic catch-all exception handlers, although on a smaller scale – when the exception aspect is run, variables from local blocks are lost – in this case j (which is serves as an extremely hard to calculate value, which we simply must have minidumped). Here is a screenshot form a post-mortem debugging session opened from a minidump generated by the throw instruction on line 10:

Intercepting the throw instruction

Ideally, we could insert minidump code right before every throw instruction. The simplest way to do this is to always use custom exception types and invoke ClrDump.Dump in their constructors. Below is a similar post-mortem debugging session, this time with a minidump created in an exception constructor:

A variation of this would be to apply an OnMethodBoundaryAspect aspect to constructors of all exceptions defined in your assemblies. Using system exceptions, however, would require MSIL rewriting – to insert aspect code right before the throw instructions. As far as I know, there is no out-of-the-box support for this in PostSharp. Also, as far as I can tell, PostSharp authors are going to great lengths to discourage writing custom aspects like the one we need for minidumps. I get why the PostSharp SDK is unsupported and undocumented. But it would be great if there was at least a null aspect sample to show how to configure PostSharp to load a plugin. With a fair knowledge of MSIL and a class browser, the rest is doable.

I would love to hear from someone with bigger experience in using PostSharp!

Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: