C
C#13mo ago
Wizard

❔ Get locals and their values from an Exception object.

Is it possible to get the local variables and their values from a StackTrace object created from a System.Exception object? I have the method public void LogException(ex). I'm trying to log all variables names and values. I'm not using compiler optimizations if it matters. I tried using reflection and a few other crazy things but nothing seems to work properly. Current code:
public static void LogException( Exception ex )
{
Log( $"Exception: {ex.GetType().Name} - {ex.Message}" );
Log( $"Stack trace: {ex.StackTrace}" );

StackTrace stackTrace = new StackTrace( ex, true );
for ( int i = 0; i < stackTrace.FrameCount; i++ )
{
StackFrame frame = stackTrace.GetFrame( i );
MethodBase method = frame.GetMethod();
Type declaringType = method.DeclaringType;

Log( $"Local variables at frame {i}:" );

MethodInfo methodInfo = method as MethodInfo;
if ( methodInfo != null )
{
LocalVariableInfo[] localVariables = methodInfo.GetMethodBody()?.LocalVariables?.ToArray();
if ( localVariables != null )
{
foreach ( LocalVariableInfo localVariable in localVariables )
{
object value = frame.GetMethod().GetMethodBody()?.LocalVariables[localVariable.LocalIndex];
Log( $"{localVariable.LocalType} {localVariable} = {value}" );
}
}
}

Log( $"Method: {declaringType?.FullName}.{method.Name}" );
Log( $"File: {frame.GetFileName()}" );
Log( $"Line: {frame.GetFileLineNumber()}" );
}

ExceptionLogged.Invoke( ex ); // Raise the ExceptionLogged event
}
public static void LogException( Exception ex )
{
Log( $"Exception: {ex.GetType().Name} - {ex.Message}" );
Log( $"Stack trace: {ex.StackTrace}" );

StackTrace stackTrace = new StackTrace( ex, true );
for ( int i = 0; i < stackTrace.FrameCount; i++ )
{
StackFrame frame = stackTrace.GetFrame( i );
MethodBase method = frame.GetMethod();
Type declaringType = method.DeclaringType;

Log( $"Local variables at frame {i}:" );

MethodInfo methodInfo = method as MethodInfo;
if ( methodInfo != null )
{
LocalVariableInfo[] localVariables = methodInfo.GetMethodBody()?.LocalVariables?.ToArray();
if ( localVariables != null )
{
foreach ( LocalVariableInfo localVariable in localVariables )
{
object value = frame.GetMethod().GetMethodBody()?.LocalVariables[localVariable.LocalIndex];
Log( $"{localVariable.LocalType} {localVariable} = {value}" );
}
}
}

Log( $"Method: {declaringType?.FullName}.{method.Name}" );
Log( $"File: {frame.GetFileName()}" );
Log( $"Line: {frame.GetFileLineNumber()}" );
}

ExceptionLogged.Invoke( ex ); // Raise the ExceptionLogged event
}
21 Replies
Thinker
Thinker13mo ago
no the stack frames are popped before you ever have a chance to access them I'm pretty sure thus no local variable information would be available (someone please correct me if I'm wrong)
Wizard
Wizard13mo ago
That's what I thought! But I keep finding stackoverflow articles that claim that it's available if compiler optimizations are turned off. I have all optimizations off in debug mode which is where this code will be used.
Chiyoko_S
Chiyoko_S13mo ago
if anything you're relying on undefined behaviour so
Wizard
Wizard13mo ago
how so?
Chiyoko_S
Chiyoko_S13mo ago
local variables will be on stack / registers once the stack gets unwound it is very likely that they get overwritten pretty soon especially registers
Wizard
Wizard13mo ago
If that's the case, how does the visual studio debugger correctly tell me the locals when it catches the exceptions?
Thinker
Thinker13mo ago
Because it pauses execution before the stack unwinds
Wizard
Wizard13mo ago
You mention the vs debugger pauses execution before the stack unwinds. I'm still a bit confused. Stack information is already saved to the StackTrace object in my LogException. Are you implying execution is never halted while the exception instance is created? That makes no sense for a single-threaded app. Even so there should be a way to do it the same way visual studio does it if that's the only possibility. I've seen people claim it's possible on stackoverflow but their example code is just too difficult for me to understand or their use case is too far from mine to be useable.
Thinker
Thinker13mo ago
A new exception is thrown, Visual Studio is notified of this and is able to pause execution
Wizard
Wizard13mo ago
why does pausing execution matter when the stacktrace obj is already saved to a var? the saving is instantaneous so it should be the same as pausing?
Thinker
Thinker13mo ago
Because the stack trace is just info about the individual steps taken to get to the point where the exception occurred It's meant purely for things like printing the stack trace as debug info
Wizard
Wizard13mo ago
this 7 year old answer claims to be able to do it using PostSharp:
Wizard
Wizard13mo ago
Stack Overflow
How to Trace all local variables when an exception occurs
any generic way to trace/log values of all local variables when an exception occurs in a method? (in C# 3)
Thinker
Thinker13mo ago
jeez, C# 3 - A throw statement/expression is reached - A new exception object is created - The current stack trace is stored into the exception - The debugger is notified of the exception - The stack unwinds Local variables aren't saved at any point during this
Wizard
Wizard13mo ago
basically I created an app and added a boatload of LogException calls. Users keep sending me their logs but the stacktrace I'm logging is beyond useless as it doesn't have any locals. Yes I know it fails at this line in method x, but it doesn't help me unless I know what the value is. Been trying to update my code to just verbosely log all locals I need, but I'm 3 hours into that project and I'm not even a quarter of the way done. So I'm out of ideas if this get-locals-from-stacktrace idea isn't possible. it's not like I can even send them manually either. Take this example:
try
{
int x = 4;
throw;
}
catch (Exception ex)
{
LogException(ex, new[] {x});
}
try
{
int x = 4;
throw;
}
catch (Exception ex)
{
LogException(ex, new[] {x});
}
Wouldn't work because x is in a different scope Thank you for the analysis. I think I'm understanding the problem a bit better now A lot of answers recommend either PostSharp, IntelliTrace, Minidumps, or some sort of reflection.
Thinker
Thinker13mo ago
Those probably could work, but imo they'd be overkill
Wizard
Wizard13mo ago
What do you recommend? Idk how to read dump files, but would that solve my problem?
Thinker
Thinker13mo ago
I don't really know, but my guess would be to have better logging in and around methods which might commonly throw.
Wizard
Wizard13mo ago
i miss python's locals()
Chiyoko_S
Chiyoko_S13mo ago
dunno what this postsharp thing is but it seems to rewrite IL so that's doing it in a completely different way
Accord
Accord13mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.