C
C#2y 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
Thinker2y 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
WizardOP2y 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_S2y ago
if anything you're relying on undefined behaviour so
Wizard
WizardOP2y ago
how so?
Chiyoko_S
Chiyoko_S2y 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
WizardOP2y ago
If that's the case, how does the visual studio debugger correctly tell me the locals when it catches the exceptions?
Thinker
Thinker2y ago
Because it pauses execution before the stack unwinds
Wizard
WizardOP2y 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
Thinker2y ago
A new exception is thrown, Visual Studio is notified of this and is able to pause execution
Wizard
WizardOP2y 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
Thinker2y 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
WizardOP2y ago
this 7 year old answer claims to be able to do it using PostSharp:
Wizard
WizardOP2y 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
Thinker2y 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
WizardOP2y 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
Thinker2y ago
Those probably could work, but imo they'd be overkill
Wizard
WizardOP2y ago
What do you recommend? Idk how to read dump files, but would that solve my problem?
Thinker
Thinker2y ago
I don't really know, but my guess would be to have better logging in and around methods which might commonly throw.
Wizard
WizardOP2y ago
i miss python's locals()
Chiyoko_S
Chiyoko_S2y ago
dunno what this postsharp thing is but it seems to rewrite IL so that's doing it in a completely different way
Accord
Accord2y 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.
Want results from more Discord servers?
Add your server