Recipe 5.14 Obtaining a Stack Trace
Problem
You need a view of what the stack looks
like at any particular point in your application. However, you do not
have an exception object from which to obtain this stack trace.
Solution
Use the following line of code to obtain a stack trace at any point
in your application:
string currentStackTrace = System.Environment.StackTrace;
The variable currentStackTrace now contains the
stack trace at the location where this line of code was executed.
Discussion
A good use of the Solution is tracking down stack overflow problems.
You can obtain the current stack trace at various points in your
application and then calculate the stack depth. This depth
calculation can then be logged to determine when and why the stack is
overflowing or potential trouble spots where the stack may grow very
large.
It is very easy to
obtain a stack trace using the
System.Environment.StackTrace property.
Unfortunately, this stack trace also lists three methods defined in
the System.Environment class that are called when
you use the Environment.StackTrace property. The
returned stack trace, using this method, will look something like the
following:
at System.Environment.GetStackTrace(Exception e)
at System.Environment.GetStackTrace(Exception e)
at System.Environment.get_StackTrace( )
at Chapter_Code.Class1.ObtainingStackTrace( ) in c:\book cs cookbook\test.cs:line 260
at Chapter_Code.Class1.Main(String[] args) in c:\book cs cookbook\main.cs:line 78
The first three items in the stack trace are method calls that we are
not interested in. To fix this, we can write the following method to
find and remove these items from the stack trace:
public static string GetStackTraceInfo(string currentStackTrace)
{
string firstStackTraceCall = "System.Environment.get_StackTrace( )";
int posOfStackTraceCall = currentStackTrace.IndexOf(firstStackTraceCall);
return (currentStackTrace.Substring(posOfStackTraceCall +
firstStackTraceCall.Length));
}
This method is called using the following line of code:
string stackTraceInfo = GetStackTraceInfo(System.Environment.StackTrace);
The second line in the
GetStackTraceInfo method creates and initializes a
string variable to the first called StackTrace
method-which is actually a call to the get
portion of the StackTrace property. This variable
is used in the third line to obtain its starting position in the
complete stack trace string. The final line of code grabs the end of
the complete stack trace string, starting at the ending of the first
called StackTrace method. The
FinalStackTrace variable now contains the
following string:
at Chapter_Code.Class1.ObtainingStackTrace( ) in c:\book cs cookbook\test.cs:line 260
at Chapter_Code.Class1.Main(String[] args) in c:\book cs cookbook\main.cs:line 78
This is the current stack trace at the point in the code where the
Environment.StackTrace method was called.
Now that we have a stack trace of our code, we can calculate the
stack depth at the point where we call
Environment.StackTrace. The following code uses a
regular expression to determine the depth of a stack
trace:
using System;
using System.Text.RegularExpressions;
public static int GetStackTraceDepth(string currentStackTrace)
{
string firstStackTraceCall = "System.Environment.get_StackTrace( )";
int posOfStackTraceCall = currentStackTrace.IndexOf(firstStackTraceCall);
string finalStackTrace = currentStackTrace.Substring(posOfStackTraceCall +
firstStackTraceCall.Length);
MatchCollection methodCallMatches = Regex.Matches(finalStackTrace,
@"\sat\s.*(\sin\s.*\:line\s\d*)?");
return (methodCallMatches.Count);
}
This regular expression captures every method call in the stack trace
string. Note that, if the correct symbols are located for our
assembly, the stack trace might look like this:
at Chapter_Code.Class1.ObtainingStackTrace( ) in c:\book cs cookbook\test.cs:line 260
at Chapter_Code.Class1.Main(String[] args) in c:\book cs cookbook\main.cs:line 78
However, if the correct symbols cannot be found, the stack trace
string will look similar to the following:
at Chapter_Code.Class1.ObtainingStackTrace( )
at Chapter_Code.Class1.Main(String[] args)
The file and line numbers are not displayed in this case, and the
regular expression must take this into account.
To get a count of the stack depth, use the Count
property of the MatchCollection object to give the
total number of method calls in the stack. In addition, we can obtain
each individual method call as an independent string by iterating
through the MatchCollection object. The code to do
this is:
Console.WriteLine("-------------");
foreach(Match m in MethodCallMatches)
{
Console.WriteLine(m.Value + System.Environment.NewLine + "-------------");
}
This code will display the following:
-------------
at Chapter_Code.Class1.ObtainingStackTrace( ) in
c:\book cs cookbook\test.cs:line 260
-------------
at Chapter_Code.Class1.Main(String[] args) in
c:\book cs cookbook\main.cs:line 78
-------------
Each method and its information are contained within a
Match object within the
MatchCollection object.
The Environment.StackTrace method can be useful as
a debugging tool. You can see at various points in your application
which methods have been called and their calling order. This can come
in very handy when creating and debugging an application that uses
recursion. In addition, you can also keep track of the stack depth by
using the Environment.StackTrace property.
See Also
See the "Environment.StackTrace
Property" topic in the MSDN documentation.
|