DekGenius.com
[ Team LiB ] Previous Section Next Section

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.

    [ Team LiB ] Previous Section Next Section