21

I would like to be able to obtain all the parameter values from the stack frame in .NET. A bit like how you're able to see the values in the call stack when in the Visual Studio debugger. My approach has concentrated on using the StackFrame class and then to reflect over a ParameterInfo array. I've had success with reflection and properties, but this is proving a bit trickier.

Is there an approach for achieving this?

The code so far looks like this:

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        a.Go(1);
    }
}

public class A
{
    internal void Go(int x)
    {
        B b = new B();
        b.Go(4);
    }
}

public class B
{
    internal void Go(int y)
    {
        Console.WriteLine(GetStackTrace());

    }
    public static string GetStackTrace()
    {
        StringBuilder sb = new StringBuilder();
        StackTrace st = new StackTrace(true);
        StackFrame[] frames = st.GetFrames();

        foreach (StackFrame frame in frames)
        {
            MethodBase method = frame.GetMethod();

            sb.AppendFormat("{0} - {1}",method.DeclaringType, method.Name);
            ParameterInfo[] paramaters = method.GetParameters();
            foreach (ParameterInfo paramater in paramaters)
            {
                sb.AppendFormat("{0}: {1}", paramater.Name, paramater.ToString());
            }
            sb.AppendLine();
        }
        return sb.ToString();
    }
}

The output looks like this:

SfApp.B - GetStackTrace
SfApp.B - Go
y: Int32 y
SfApp.A - Go
x: Int32 x
SfApp.Program - Main
args: System.String[] args

I'd like it to look more like this:

SfApp.B - GetStackTrace
SfApp.B - Go
y: 4
SfApp.A - Go
x: 1
SfApp.Program - Main

Just for a bit of context, my plan was to try and use this when I throw my own exceptions. I'll look at your suggestions in more detail and see if I can see it fitting.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Martin Clarke
  • 5,636
  • 7
  • 38
  • 58

2 Answers2

10

It seems it can't be done that way. It will only provide meta information about the method and its parameters. Not the actual value at the time of the callstack.

Some suggest deriving your classes from ContextBoundObject and use IMessageSink to be notified off all method calls and the values of the parameters. This is normally used for .NET Remoting.

Another suggestion might be to write a debugger. This is how the IDE gets its information. Microsoft has Mdbg of which you can get the source code. Or write a CLR profiler.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Lars Truijens
  • 42,837
  • 6
  • 126
  • 143
2

I am quite sure this is possible somehow, but I am not competent enough to give you the answer you wish. I am suggesting a different approach, less flexible but still useful if you have a stack trace in advance and you would like to see the arguments passed to each frame.

Typically you would scatter log messages at the start of each method present in your stack trace, but that is pretty cumbersome and not always possible: what if you call methods defined in an external library?


While I was searching for an inspiration on how to tackle this problem, I found a library called Harmony, that allows you to patch methods at runtime. Basically a method can be decorated with a prefix, a postfix and/or a finalizer. It is all very well explained in the documentation, but the idea you could use in order to provide details about methods in a stack trace is to create a prefix that prints the parameters of each frame.

Unfortunately, with Harmony only, I do not think it is possible to do this for the methods in the current StackTrace, as you would like to do, even using a postfix/finalizer. Nonetheless it can be done before the target method is called.


I have created a very simple library called DebugLogger that internally uses Harmony and that simplifies the procedure. Here is the repository, but you can also find it on Nuget:

dotnet add package DebugLogger --version 1.0.0

It still has many limitations, in part because of Harmony, there are just a bunch of tests and is not ready to be considered a library, but it is a good starting point in my opinion. I have prepared a Fiddle showing the initialization of DebugLogger and a couple of dummy classes to make the demo "meaningful".

Marco Luzzara
  • 5,540
  • 3
  • 16
  • 42