Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I change current stacktrace in C#?

Tags:

c#

mono

unity3d

In short, I need to modify the current stack trace in C#, so that method that I call (which I cannot modify) would think that it was someone else who called it.

Now, to give a little context about why I need to do such a horrible hack.

In Unity3d, Debug.Log creates a new record in developer console. When you double-click it, it opens the IDE to show exact file and line that was responsible for that record. However, for various reasons (disable debug in production, make it usable in other threads) I created a wrapper class Print that I use instead of UnityEngine.Debug.

But with wrapper, now when a developer clicks on the log record in console, it is Print that gets opened, not the actual place where Print.Log was called. And since Debug.Log does it regardless of the error message or context object, I figured that it uses call stack to identify the file to open. Needless to say, I want to fix that.

like image 979
Max Yankov Avatar asked Aug 11 '14 13:08

Max Yankov


1 Answers

I don't believe it is possible. Looking at the source code of Environment.StackTrace and the subsequent GetStackTrace() call we can see it creates the stacktrace each time whenever it is called. Since there doesn't seem to be anything you could inject to change some of its working, I think it's safe to conclude that you cannot change the stacktrace without modifying how it is called.

If you would have the possibility to do that, you could use something like this which comes down to retrieving the current stacktrace and using reflection to change the most recent StackFrame's method field to mimic what it is you're after.

void Main()
{
    DoSomething();
}

void DoSomething(){
    Console.WriteLine (Environment.StackTrace); // Last frame: at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)

    var frames = new StackTrace().GetFrames();
    var lastframe = frames[0]; // last frame: DoSomething at offset 100 in file:line:column <filename unknown>:0:0
    var methodField = lastframe.GetType().GetField("method", BindingFlags.Instance | BindingFlags.NonPublic);
    var redirectedMethod = typeof(Redirect).GetMethod("RedirectToMethod", BindingFlags.Instance | BindingFlags.Public);
    methodField.SetValue(lastframe, redirectedMethod);
    Console.WriteLine (frames); // last frame: RedirectToMethod at offset 100 in file:line:column <filename unknown>:0:0
    Console.WriteLine (Environment.StackTrace); // last frame: at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
}

public class Redirect {
    public void RedirectToMethod() { }
}

But once again: this is very brittle and not applicable in your current situation. What you are trying to do is fairly exotic and should probably be avoided altogether.

As to an actual solution: I'm afraid I don't have any experience with Unity so I can only google but this thread might be interesting to you. I referred to .NET sources here but you can find very similar code in the Mono source ofcourse.

like image 192
Jeroen Vannevel Avatar answered Oct 16 '22 08:10

Jeroen Vannevel