Click here to Skip to main content
Click here to Skip to main content

Walking up the StackTrace

, 30 Nov 2011
Rate this:
Please Sign up or sign in to vote.
How to walk up the StackTrace.

This question is very simple: what does this application print and why?

The answer may not be as simple as you think.

using System;
 
namespace StackTrace
{
    class Program
    {
        static void Main()
        {
            Method1();
            Console.ReadLine();
        }
 
        static void Method1()
        {
            var stackTrace = new System.Diagnostics.StackTrace(1);
            var stackFrame = stackTrace.GetFrame(0);
            Console.WriteLine(stackFrame.GetMethod().Name);
 
            Method2();
        }
 
        static void Method2()
        {
            var stackTrace = new System.Diagnostics.StackTrace(1);
            var stackFrame = stackTrace.GetFrame(0);
            Console.WriteLine(stackFrame.GetMethod().Name);
 
            Method3();
        }
 
        static void Method3()
        {
            var stackTrace = new System.Diagnostics.StackTrace(1);
            var stackFrame = stackTrace.GetFrame(0);
            Console.WriteLine(stackFrame.GetMethod().Name);
        }
    }
}

Feel free to run it!!

Answer

At first look and even when you run it, the answer would seem to be:

Main
Method1
Method2

which is right but not always! In fact, in production (i.e., in release mode) that answer is always wrong!! Let me explain:

Obviously StackTrace is populated at RunTime and not at compile time. When you are debugging this application or even when you are running it when it is built with 'debugging' configuration the StackTrace is what you would hope it to be, which is what I wrote above. However if you run the very same application in release mode, the situation is different.

At runtime, when it comes the time for JIT compiler to compile this module, it has a look at the methods and thinks to itself: "Hey, you know what. These calls are very simple and I could inline these methods and save myself, CPU and memory from a lot trouble. This way we will not have to deal with pushing addresses into stack and popping them up later. So I am just going to inline this. This not only performs better but is also really cool. So yeah, let's do it". OK, maybe JIT does not think like this; but the result is the same! Smile | :)

So what is going to happen at RunTime in release mode (and depending on your machine's CPU architecture) is that JIT is going to inline these methods and the application output all of a sudden becomes:

Main
Main
Main

The decision process is actually much more complex than what I naively explained above. So many criteria are involved in making a decision about inlining a method. Here is a few:

  • Methods that are greater than 32 bytes of IL will not be inlined.
  • Virtual functions are not inlined.
  • Methods that have complex flow control will not be in-lined. Complex flow control is any flow control other than if/then/else; in this case, switch or while.
  • Methods that contain exception-handling blocks are not inlined, though methods that throw exceptions are still candidates for inlining.
  • If any of the method's formal arguments are structs, the method will not be inlined.

And then there is tailcalling which makes it even more confusing. Here are a few considerations on tailcalling: 

"For the 64-bit JIT, we tail call whenever we’re allowed to. Here’s what prevents us from tail calling (in no particular order):

  • We inline the call instead (we never inline recursive calls to the same method, but we will tail call them)
  • The call/callvirt/calli is followed by something other than nop or ret IL instructions.
  • The caller or callee return a value type.
  • The caller and callee return different types.
  • The caller is synchronized (MethodImplOptions.Synchronized).
  • The caller is a shared generic method.
  • The caller has imperative security (a call to Assert, Demand, Deny, etc.).
  • The caller has declarative security (custom attributes).
  • The caller is varargs.
  • The callee is varargs.

...."

If you want to learn more about these, Scott Hanselman has a good article that explains these in a good depth and provides much more info on them.

Hope this helps.

If you enjoyed this article/quiz, you can subscribe to GeekQuiz blog or follow GeekQuiz on twitter.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Mehdi Khalili
Software Developer (Senior) ThoughtWorks
United States United States
I work as a Senior Consultant for ThoughtWorks
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralMy vote of 5 PinmemberKabwla.Phone5-Dec-11 23:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 30 Nov 2011
Article Copyright 2011 by Mehdi Khalili
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid