DoStackSnapshot Tidbit #1: Exception Filters


Believe it or not, my last (rather large) post on stack walking actually left out several miscellaneous details about using DoStackSnapshot.  I’ll be posting those details separately.  We’ll start off with some light reading on exception filters.  No deadlocks this time, I promise.


For those of you diehard C# fans, you might be unaware of the existence of exception filters in managed code. While VB.NET makes them explicitly available to the programmer, C# does not. Filters are important to understand when you call DoStackSnapshot, as your results might look a little weird if you don’t know how to interpret them.


First, a little background. For the full deal, check out the MSDN Library topic on VB.NET’s try/catch/finally statements. But here’s an appetizer. In VB.NET you can do this:


Function Negative() As Boolean
    Return False
End Function

Function Positive() As Boolean
    Return True
End Function

Sub Thrower
    Throw New Exception
End Sub

Sub Main()
    Try
        Thrower()
    Catch ex As Exception When Negative()
        MsgBox(“Negative”)
    Catch ex As Exception When Positive()
        MsgBox(“Positive”)
    End Try
End Sub


The filters are the things that come after “When”. We all know that, when an exception is thrown, its type must match the type specified in a Catch clause in order for that Catch clause to be executed. “When” is a way to further restrict whether a Catch clause will be executed. Now, not only must the exception’s type match, but also the When clause must evaluate to True for that Catch clause to be chosen. In the example above, when we run, we’ll skip the first Catch clause (because its filter returned False), and execute the second, thus showing a message box with “Positive” in it.

The thing you need to realize about DoStackSnapshot’s behavior (indeed, CLR in general) is that the execution of a When clause is really a separate function call. In the above example, imagine we take a stack snapshot while inside Positive(). Our managed-only stack trace, as reported by DoStackSnapshot, would then look like this (stack grows up):

Positive
Main
Thrower

Main

It’s that highlighted Main that seems odd at first. While the exception is thrown inside Thrower(), the CLR needs to execute the filter clauses to figure out which Catch wins.  These filter executions are actually function calls.  Since filter clauses don’t have their own names, we just use the name of the function containing the filter clause for stack reporting purposes.  Thus, the highlighted Main above is the execution of a filter clause located inside Main (in this case, “When Positive()“).  When each filter clause completes, we “return” back to Thrower() to continue our search for the filter that returns True.  Since this is how the call stack is built up, that’s what DoStackSnapshot will report.


Comments (12)

  1. BlackTigerX says:

    "For those of you diehard C# fans, you might be unaware of the existence of exception filters in managed code. While VB.NET makes them explicitly available to the programmer, C# does not"

    so are they available at all in C#?

  2. davbr says:

    Nope. One who enjoys being gross and hacky can kind of simulate them to a small extent by having lots of different exception types. Imagine your original throw can throw one of the types based on a condition. Your catch clauses can catch each type, and even rethrow based on a condition in that scope, etc. But this can get expensive and difficult to read, so I wouldn’t recommend it.

  3. davbr says:

    Chris Brumme’s incredibly detailed blog contains a post that theorizes about another way to achieve this using anonymous methods (which are introduced here: http://msdn.microsoft.com/msdnmag/issues/04/05/C20/). Go to Chris’s post at http://blogs.msdn.com/cbrumme/archive/2003/10/01/51524.aspx and search in your browser for "DoTryCatch". I don’t know if anyone’s actually tried this yet, but you might enjoy exploring it.

  4. Andrei Faber says:

    Reading this article, I arrived at an idea. Is it possible to modify stack, for example substitute return point, or even add a faked-up stack frame? Such methods are used in native apps sometimes, but I’m not sure if this is possible under CLR.

  5. davbr says:

    Hi, Andrei, sounds like a fun idea, but modifying the stack frame, such as substituting in your own return addresses, is not advised with managed apps.  Various parts of the runtime need to walk the stack explicitly (to support garbage collection, security checks, exception handling, debugging, etc.).  If you change the stack, these subsystems will get confused, and the app will fail in all sorts of ways.  If your goal is to get your own custom profiler code to run I recommend either using the enter/leave/tailcall hooks, or IL rewriting to get your own code in there.  Modifying the stack will not work.

  6. Andrei Faber says:

    Hello David; I think this is a good idea. BTW, what will be result of rewriting code, when application is under debug, and/or rewrite occured in the method which is currently executing?

  7. davbr says:

    If your profiler rewrites IL via SetILFunctionBody(), a debugger should work fine, but you’ll need to call SetILInstrumentedCodeMap() to inform the CLR how your rewritten IL maps to the original IL.  That’s the only extra piece of information the debugger needs to be able to go from native code offset (of the JITted, rewritten IL) to the original language source code (C#, VB.NET, etc.).

    Note that if your rewritten IL is so drastically different from the original IL that a simple mapping is impossible, then of course the debugging experience will be confusing.

    Also note that there’s a general limitation in that, if profiling is on, then a debugger is unable to use the Edit and Continue feature, regardless of whether you’re rewriting IL or not.

    Finally, note that it is impossible to rewrite IL of a function currently executing, because the profiling API only allows you to change the IL at JIT time (specifically, call SetILFunctionBody() from within your JITCompilationStarted() callback).  So the IL is always rewritten before the first managed thread gets to execute it.

  8. Andrei Faber says:

    Thanks, David. And another question – is it possible to rewrite method several times by using SetFunctionReJIT method?

  9. davbr says:

    SetFunctionReJIT has been deprecated in 2.0.  The only way to rewrite IL in 2.0 is to do so before the function is JITted, which gives you only one opportunity per function.

  10. Omer Raviv says:

    Hi, David!

    In this oddity case of Exception Filters, can I expect that the information reported in DoStackSnapshot will match 1-by-1 my shadow stack, assuming I'm constructing it in the standart way, by pushing and popping in my ELT hooks? In other words, will I receive an Enter and Leave callback for that 2nd Main call?

    Thanks,

    – Omer

  11. davbr says:

    Hi, Omer.  I'm pretty sure you won't get a separate enter/leave probe call for the 2nd Main call (though feel free to try it out and report back if I'm wrong).  While maintaining a shadow stack, you'll have to adjust it via the various Exception* callbacks.  In particular, in this case the ExceptionSearchFilterEnter and ExceptionSearchFilterLeave callbacks will be of use.

    Thanks,

    Dave