BlackWaspTM
.NET Framework
.NET 2.0+

Obtaining a Stack Trace

Call stacks are used to control the flow of programs as methods and properties are executed and terminated. When adding diagnostic code, such as logging, to software it can be useful to examine the call stack for a thread or exception using a stack trace.

Call Stacks

During execution of all but the simplest C# programs, there will be many calls to class and structure members including methods, properties and constructors. Each call changes the current execution position within the program until the called member terminates, at which point control returns to the original location.

To control the program flow during and following calls, it is essential that the calling location is stored, in order that it can be returned to. This is enabled using call stacks. Each thread of execution includes a call stack that uses a last-in, first-out (LIFO) stack structure. When a call is made, the details of the caller are pushed onto the top of the stack and the called method is launched. When this terminates, either because the end of the member is reached or because a return statement is encountered, the caller details are 'popped' from the stack and used to determine the next code to execute. Using a stack in this manner allows a large number of members to be called and tracked easily.

When debugging your code you can examine the call stack and each of the calls it contains, which are called stack frames, using the Call Stack window in Visual Studio. In some cases it is useful to be able to examine the call stack programmatically using a stack trace. Although it is generally a bad idea to change the behaviour of a class depending upon the content of a stack trace, there are times when it is appropriate. For example, if you are developing your own debugging or diagnostic tools, or when you wish to create logs that contain call stack information. For logging specifically, you might want to examine the current call stack or the details of the stack trace that was generated at the time an exception was thrown.

Obtaining the Current Stack Trace

If you want to obtain a stack trace for the current thread's call stack, you can do so using two classes from the System.Diagnostics namespace. These are StackFrame, which represents a single entry in the call stack, and StackTrace, which holds a full trace containing a number of StackFrame objects. You can examine StackFrame objects using reflection techniques, so we'll be using the System.Reflection namespace too.

using System.Diagnostics;
using System.Reflection;

To read the call stack when you just wish to know the methods and properties that have been called, you can create a new StackTrace using the class's default constructor. Calling the GetFrames method returns an array of StackFrames, with the items from the top of the stack appearing at the start of the array; the item at index zero of the array represents the most recent call.

Once you have the StackFrames you can call their GetMethod method to obtain a MethodBase object that describes their members. This class has been described in the Reflection tutorial so I won't describe it again here. We'll simply use its Name property to find the name of the method or property that was called.

The following example code can be used in a console application project. It recursively calls a method named, "RecursiveCall". After five calls, control passes to the "ShowCallStack" method, which creates a new StackTrace, obtains the stack frames and outputs the names of the called methods. The output shown in the comment shows the calls in the order they appear in the call stack.

NB: When running in the Visual Studio debugger you may see additional items in the call stack that are added by the debugger before Main begins.

class Program
{
    static void Main(string[] args)
    {
        RecursiveCall(5);
    }

    private static void RecursiveCall(int count)
    {
        if (count > 0)
        {
            RecursiveCall(count - 1);
        }
        else
        {
            ShowCallStack();
        }
    }

    private static void ShowCallStack()
    {
        StackTrace trace = new StackTrace();
        StackFrame[] frames = trace.GetFrames();
        foreach (StackFrame frame in frames)
        {
            MethodBase info = frame.GetMethod();
            Console.WriteLine("{0}.{1}", info.ReflectedType.FullName, info.Name);
        }
    }
}

/* OUTPUT

Demo.Program.ShowCallStack
Demo.Program.RecursiveCall
Demo.Program.RecursiveCall
Demo.Program.RecursiveCall
Demo.Program.RecursiveCall
Demo.Program.RecursiveCall
Demo.Program.Main

*/
4 November 2012