Partager via


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 FalseEnd FunctionFunction Positive() As Boolean    Return TrueEnd FunctionSub Thrower    Throw New ExceptionEnd SubSub Main()    Try        Thrower()    Catch ex As Exception When Negative()        MsgBox("Negative")    Catch ex As Exception When Positive()        MsgBox("Positive")    End TryEnd 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):

PositiveMainThrower
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

  • Anonymous
    October 13, 2005
    "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#?

  • Anonymous
    October 13, 2005
    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.

  • Anonymous
    October 17, 2005
    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.

  • Anonymous
    November 28, 2006
    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.

  • Anonymous
    November 29, 2006
    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.

  • Anonymous
    November 29, 2006
    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?

  • Anonymous
    November 30, 2006
    The comment has been removed

  • Anonymous
    December 01, 2006
    Thanks, David. And another question - is it possible to rewrite method several times by using SetFunctionReJIT method?

  • Anonymous
    December 04, 2006
    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.

  • Anonymous
    June 17, 2009
    PingBack from http://pooltoysite.info/story.php?id=3592

  • Anonymous
    July 05, 2011
    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
  • Anonymous
    July 06, 2011
    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