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 removedAnonymous
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=3592Anonymous
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