Abstract
The article describes how to use the .NET StackTrace
class and its frames to navigate through the source code during Exception handling.
The class used to extract info from the StackTrace
is hosted inside a DLL assembly and ready to use inside any .NET 3.5 or higher Solution.
Introduction
First of all, what is a stack trace? During the execution of a program, it's usual to have functions or methods which call other functions and/or methods;
these, in turn, call other functions and/or methods so that we have a chain of calls. StackTrace
is a structure reporting this sequence of calls in the right order they occurred.
As stated by the MSDN: "Stack trace information will be most informative with Debug build configurations. By default, Debug builds include debug symbols,
while Release builds do not. The debug symbols contain most of the file, method name, line number, and column information used in constructing
StackFrame
and StackTrace
objects."
Now the question is how to use these information? Very often, during debugging or in short cycle development, I struggle with Exceptions threw by very deep code;
in cases like that, what I can do is: navigate from call to call diving into the code until the source of the thrown exception is reached... another possibility is read
the call stack and see the line of code that threw the exception. The class StackTrace
lists the sequence of function/method calls, so the main idea
is use this list of calls to make some sort of automation for navigating through the source code.
Using the code
The core is the .NET StackTrace
class. If you create an instance of the class in any point of your code (see the snippet of code below),
you can inspect the chain of calls that bring you to that point of your code!
using System.Diagnostics;
. . .
StackTrace stacktrace = new StackTrace();
StackFrame[] frames = stacktrace.GetFrames();
string stackName = string.Empty;
foreach (StackFrame sf in frames){
System.Reflection.MethodBase method = sf.GetMethod();
stackName += (method.Name+"|");
}
From the picture, you can see the chain: The Main
method called _nExecuteAssembly
, which called RunUsersAssembly
, and so on.
This kind of information becomes really useful when debugging because when your code or the framework code throws an Exception,
the Exception contains lots of information that you can use to navigate inside the source code, formally building some sort of automation facility.
This is what is done by the class StackTraceDumperForm
. To let the StackTraceDumperForm
help you during your debugging session,
you must collaborate doing three things:
- don't destroy the stack trace in your code (which is recommended)
- inside the catch part of a
try
-catch
, use the Exception to build a StackTrace
instance and pass it to the StackTraceDumperForm
ctor - let run only one instance of Visual Studio, so that
StackTraceDumperForm
shall be able to hook it, find, and highlight the line causing the Exception
Let's see the steps one by one.
- Don't destroy the stack trace
This is good:
try {
. . .
}
catch( Exception exx ) {
Debug.WriteLine( exx.Message );
if( null != exx.InnerException )
Debug.WriteLine( exx.InnerException.Message );
throw;
}
Because every Exception which is been thrown is caught, is inspected to show some useful information, and is re-thrown without destroying the stack of calling functions.
On the other hand, this is bad:
try {
. . .
}
catch( Exception exx ) {
throw new Exception( "The exception message is" + exx.Message );
}
Sure, you display the same message of the original exception but every time you create a new Exception, the stack of calling functions is reset and begins from
the point where the Exception is created! For the same reason, this is also bad:
try {
. . .
}
catch( Exception exx ) {
throw new Exception("The exception is", exx );
}
And, actually, this is also a bad thing to do:
try {
. . .
}
catch( Exception exx ) {
throw exx;
}
You can download the demo project and inspect the cases, you can also use StackTraceDumperForm
to visualize how the stack is cut:
You can see how the stack trace displays the guilty function (the last one: DdoItTooEarly
method) because the stack is preserved by our code!
The guilty function is CallAFailureFuncton1()
because the exception is created inside the catch
block of this function.
Again, the guilty function is CallAFailureFuncton2()
because the exception is created inside the catch
block of this function.
Finally the guilty function is CallAFailureFuncton3()
because even though we re-throw the original exception, writing "throw exx;"
instead of "throw;"
really destroys the stack of calling functions!
- Inside the catch part of a try-catch, use the Exception to build a StackTrace instance and pass it to the StackTraceDumperForm ctor
The StackTraceDumperForm
needs an instance of System.Diagnostics.StackTrace
to work, not an Exception. The instance of the Exception
class
contains a StackTrace
; so you can extract the StackTrace
from the exception and pass it to the ctor! The snippet of code that I usually use is:
try {
. . .
}
catch( Exception exx ) {
Debug.WriteLine( exx.Message );
if( null != exx.InnerException )
Debug.WriteLine( exx.InnerException.Message );
#if DEBUG
Dictionary<string, string> debugInfos = new Dictionary<string, string>();
debugInfos.Add( "Method Name", "MyMethodName" );
var trace = new System.Diagnostics.StackTrace( ex, true );
var frm = new StackTraceDumperForm.StackTraceDumperForm( debugInfos, ex.Message, trace );
frm.ShowDialog();
#endif
}
The DEBUG macro just avoid that inside production code, StackTraceDumperForm
stops the code execution!
- Let run only one instance of Visual Studio
Do you see the Visual Studio 2010 logo inside the StackTraceDumperForm
? On the left side, there is the string: "Go to line..." This information
is extracted from the StackTrace
object and if you press the button (green arrow), you will be set at the line displayed inside the class displayed,
but only if there is one and only one instance of Visual Studio, because StackTraceDumperForm
is not able to distinguish which instance of IDE has
the class you are interested in! Therefore, if you want the automation working, remember to have only one running instance of Visual Studio!
Another thing: the green information is about your code, the code that StackTraceDumperForm
is able to reach;
while in gray is the information about code too deep to be reached, i.e., inside the core class of the Framework!
The last thing: another little automation facility is pointed by the blue arrow; if you press the button, then the Output window of the Visual Studio will be cleared!
Conclusion
I'm used to use this simple utility since 2007 and find it very useful during debugging sessions; so I decided to share it with all of you.
As usual, feel free to use the code and to modify it as you like (and let me know your impressions)!
History
- 19 November, 2010 - First release.