Run-time exception checking


One of our partners asked us how a .NET program can tell what the currently active “try” blocks are on the stack.  This seemed like a dubious thing to want to do, but regardless a colleague of mine whipped up some sample code that uses the StackTrace class and reflection to do this.  We were talking about the possible uses of this, and most of them seemed pretty evil.  Changing program behaviour based on dynamic program inspection can lead to programs that are hard to reason about and brittle due to violating abstraction boundaries and implicit coupling.  Not to mention the fact that in optimized builds, some data may be missing from the StackTrace due to in-lining or other optimizations.


However, in-process inspection mechanisms like the StackTrace class can be incredibly useful for diagnostics purposes.  Perhaps in certain situations, it would be valuable to be able to write assertions like “If I throw a FooException, it should be handled by somebody”. As you probably know, there is a lot of debate about the value of checked exceptions, but I’m not going to discuss that here, see the Artima interviews with Anders Hejlsberg and James Gosling for a start.  We all know that validating your assumptions with assertions is a critical part of writing quality software, especially for large systems.  So if you’re making some assumptions about exception behaviour, it seems logical to want to check those assumptions at run-time with assertions.  Assertions can also be useful for making you aware of changes in behaviour that might have an impact on an area you didn’t anticipate.  You could imagine more complex checks like “The only catch handler for System.Exception is the one I know about in the top loop of my application” (nothing is more annoying then having some library code swallow your exceptions).  Of course checking these sorts of things statically (with tools like FxCop) is generally preferable to run-time checks, but our managed static analysis tools are still quite primitive, and it is very difficult to do complete static analysis in the presence of dynamic control flow mechanisms like reflection and delegates.


Anyway, here is some sample code that shows how you could write such assertions (I apologize for the lack of syntax highlighting – this new community server software doesn’t properly paste formatted text).  I’m still not convinced this is necessarily a good idea, but it seems to have some intriguing possibilities nonetheless. 


// Get a list of all the try clauses that are active on the stack that would
// catch exceptions of the specified type.
static public List<ExceptionHandlingClause> GetActiveTryClauses(Type exType)
{
  List<ExceptionHandlingClause> activeTryClauses =
    new List<ExceptionHandlingClause>();


  StackTrace stackTrace = new StackTrace();
  StackFrame[] frames = stackTrace.GetFrames();


  // Start at 1 to skip the GetActiveTryClauses frame.
  for (int i = 1; i < frames.Length; i++)
  {
    StackFrame frame = frames[i];
    MethodBase method = frame.GetMethod();
    MethodBody body = method.GetMethodBody();
 
    // Only consider methods that have an IL body.
    if (body != null)
    {
      IList<ExceptionHandlingClause> ehClauses = body.ExceptionHandlingClauses;
      foreach (ExceptionHandlingClause ehClause in ehClauses)
      {
        if (ehClause.Flags == ExceptionHandlingClauseOptions.Clause)
        {
          // Only consider clauses which are active on the current stack
          int offsetInFrame = frame.GetILOffset();
          int tryStartOffset = ehClause.TryOffset;
          int tryEndOffset = tryStartOffset + ehClause.TryLength;
 
          if ((offsetInFrame >= tryStartOffset) && (offsetInFrame < tryEndOffset))
          {
            // If desired, only collect clauses that would catch the specified type
            if (exType == null || ehClause.CatchType.IsAssignableFrom(exType))
            {
              activeTryClauses.Add(ehClause);
            }
          }
        }
      }
    }
  }
 
  return activeTryClauses;
}
 
public static bool IsCaught(System.Type type)
{
  List<ExceptionHandlingClause> handlers = GetActiveTryClauses(type);
  return (handlers.Count > 0);
}


This allows you to write simple checks like the following:


// Ensure someone will handle any IO failure here
Debug.Assert(TryClauseInfo.IsCaught(typeof(System.IO.IOException)));


// Ensure no-one is catching all exceptions
Debug.Assert(!TryClauseInfo.IsCaught(typeof(System.Exception)));


Or you could even write more complex checks based on the ExceptionHandlingClause information. 


What do you think?  Are there situations in your applications where you could get value out of using assertions like this? Can you think of any other uses for this code that wouldn’t be totally evil?

Comments (4)

  1. toub says:

    This is an interesting idea, but couldn’t it yield false positives if user exception filters are involved? A filter could narrow the exceptions caught in a particular catch block such that even if the exception type matches, it still might not be caught, i.e. "Catch e As IOException When False". Still, an interesting use of reflection.

  2. heaths says:

    re: "I apologize for the lack of syntax highlighting – this new community server software doesn’t properly paste formatted text"

    And it probably never will. The VS editor is a rich-text control that only fills the clipboard with RTF and Text. What many bloggers do is paste this into Word (which favors RTF) and then re-copy the text into Community Server. Word uses many clipboard formats – including HTML – which the editable content portion of CS will favor.

  3. rmbyers says:

    Thanks for your comment Stephen. Yes this wouldn’t work properly with filters at at all. Although rather than get false positives as you suggest, it would ignore the filters entirely (so you’d miss potentially interesting handlers). In VB, when you say "Catch e as Exception" it emits a catch handler just like C# does, but when you add a "When" clause, it switches completely to a filter which doesn’t explicitly track an exception type but instead runs some arbitrary filter code which includes the type check ("isinst" instruction). In my sample code, the test for ExceptionHandlingClauseOptions.Clause skips all filters. If I also permitted "Filter" there, I’d then get an InvalidOperationException when I called ehClause.CatchType because filters don’t have an explicit catch type. If I really wanted to try and be clever, I could probably copy the IL for the filter into a Reflection.Emit method and invoke it on some exception instance I provide in order to test the filter at run-time.

    Heath, thanks for the details! I had tried copying from VS into Word and then into CS, and it appeared to work properly until I actually went to preview the entry when all the formatting disappeared. When displayed in the edit window, the formatting looked fine. I didn’t experiment with it much more, but others have told me copying from word in general now doesn’t work well. Have you had any success with this?

  4. Barry Kelly says:

    It’s a bit of an understatement to say:

    "it is very difficult to do complete static analysis in the presence of dynamic control flow mechanisms like reflection and delegates."

    In fact, it’s provably impossible!