.NET Forum / .NET Framework / CLR / June 2006
How to get current running values from .LocalVariables in .NET 2.0?
|
|
Thread rating:  |
Lou Zher - 05 Jun 2006 16:52 GMT We've got a project that needs to be able to see the current running values from the objects returned back by .LocalVariables. How is this done? LocalVariables only appears to give us the types of the method vars. -LZ
Mattias Sjögren - 05 Jun 2006 19:02 GMT >How is this done? It can't be done with Reflection.
Mattias
 Signature Mattias Sjögren [C# MVP] mattias @ mvps.org http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com Please reply only to the newsgroup.
Lou Zher - 07 Jun 2006 15:29 GMT Mattias, Hmm... I was afraid of that. Well, at least I know to bag looking for a solution inside there. Is there something else I should be looking at? I've looked briefly at the Debugging API, but it seems horribly complicated to use for this purpose -- and I'm unclear as to how to make the connection between the references I find via reflection to values I would obtain via ICorDebug and friends -- or should I discover the stack/heap from the debugging API, skipping Reflection altogether? -LZ
>>How is this done? > > It can't be done with Reflection. > > Mattias Ben Voigt - 06 Jun 2006 13:53 GMT > We've got a project that needs to be able to see the current running > values from the objects returned back by .LocalVariables. How is this > done? LocalVariables only appears to give us the types of the method vars. > -LZ What are you trying to do? The values of local variables in any method are only well-defined at very specific times.
If you are trying to add additional information to an exception stack trace, please realize that by the time control reaches a handler, the call stack is already unwound, and stack space may have been overwritten by, for example, finally blocks that have already executed.
Only methods still on the call stack have valid locals.
If you are in the Exception constructor trying to capture the information, then it is theoretically possible to capture the details, however consider that the MSIL is converted to machine code, so any solution is going to be architecture and runtime specific. For example, mono might store local variables in a different order than ms.net. There's a WinAPI routine StackWalk that might be of some use... but not much. You basically require the debugging API distributed with the JIT compiler, since only it can determine where the variables are stored.
Also remember that in the case of recursion, there may be multiple stack frames corresponding to a single MethodBody and LocalVariableInfo.
If you're wanting an automated way to enumerate local variables in the current method, please consider that your code will never be reuseable because the process of encapsulating it changes the scope of variable capture.
The runtime provides minidump functionality, I suggest you use that in lieu of any attempt to capture state information on your own.
Lou Zher - 06 Jun 2006 17:31 GMT > What are you trying to do? The values of local variables in any method > are only well-defined at very specific times. Ben... In a word, Yes. I'm trying to get a stack trace goodies.
> If you are trying to add additional information to an exception stack > trace, please realize that by the time control reaches a handler, the call > stack is already unwound, and stack space may have been overwritten by, > for example, finally blocks that have already executed. > Only methods still on the call stack have valid locals. Well, that's true, but there is a workaround. Exception handling involves a two phase stack walk. The first walks up the stack to look for a qualifying catcher of the exception thrown. Using exception filtering via the WHEN clause in VB.NET, one can generate StackTrace info while the throwing code is still alive. It has to be since the second unwind hasn't occurred yet, which is where the finally blocks are executed. Only then would the locals of each method be allowed to go out of scope. Here's some interesting code to try out:
Sub Main() Try Helper() Catch ex As Exception When ExceptionFilter() End Try End Sub
Sub Helper() MakeExceptionHappen() End Sub
Sub MakeExceptionHappen() Throw New ApplicationException("Bummer") End Sub
Function ExceptionFilter() as Boolean Dim st as New StackTrace() 'gets a stack trace at this point Console.WriteLine(st.ToString) Return False End Function
'and what is printed is: ' at blah.blah.ExceptionFilter(Exception ex) ' at blah.blah.Main() <-- This is interesting! ' at blah.blah.MakeExceptionHappen() ' at blah.blah.Helper() ' at blah.blah.Main() ' at System.AppDomain.nExecuteAssembly ....yadda.yadda......
So what appears to be happening is that the first phase of the exception handling is walking up the stack looking for a qualifying exception handler and then injecting a 'call' to a fragment inside Main which in turn calls the ExceptionFilter. This means that everything down to the point of the thrower is still available just as though the thrower had called the ExceptionFIlter code itself.
Okay, so forget about all that stuff for a moment because it's not really relevant. The question remains, can I get the current values of vars still in scope via Reflection, Introspection, Debugging API, whatever... I'd like to get statics, object instance vars, method parameters, and method locals.
Thanks for your help so far Ben. -LZ
Ben Voigt - 07 Jun 2006 18:25 GMT >> What are you trying to do? The values of local variables in any method >> are only well-defined at very specific times. [quoted text clipped - 14 lines] > would the locals of each method be allowed to go out of scope. Here's some > interesting code to try out: I didn't realize VB.NET gave you access to exception filters... my mistake. Do you get any exception information (which points to the stack frame where the exception occurred, since as you noticed the filter is layered on top of the excepting method) in the exception filter?
For the level of detail you are desiring, you should consider a minidump file. I think this note in MiniDumpWriteDump is especially important: MiniDumpWriteDump may not produce a valid stack trace for the calling thread. To work around this problem, you must capture the state of the calling thread before calling MiniDumpWriteDump and use it as the ExceptionParam parameter. One way to do this is to force an exception inside a __try/__except block and use the EXCEPTION_POINTERS information provided by GetExceptionInformation
Since you already have an exception, that should not be a problem, although getting the exception information in the correct format could be.
> Sub Main() > Try [quoted text clipped - 37 lines] > like to get statics, object instance vars, method parameters, and method > locals. I don't think you will have any luck using the Debugging API within your managed application to attach to itself.
> Thanks for your help so far Ben. > -LZ Lou Zher - 07 Jun 2006 19:17 GMT > I didn't realize VB.NET gave you access to exception filters... my > mistake. Do you get any exception information (which points to the stack > frame where the exception occurred, since as you noticed the filter is > layered on top of the excepting method) in the exception filter? Yeah, isn't that cool? And yes, you can get exception information. In my example, ex is accessible to the filter. So I can generate a StackTrace based on the exception, which would exclude the filter calls, or I can just gen a new one based on the current context. This is really neat because it also means I can examine and exception and choose to catch it or not, vs. catching it, examining it and choosing to rethrow it or not.
You can also add a filter operand to a .try descriptor in MSIL.
> For the level of detail you are desiring, you should consider a minidump > file. I think this note in MiniDumpWriteDump is especially important: [quoted text clipped - 12 lines] > I don't think you will have any luck using the Debugging API within your > managed application to attach to itself. Hmmm... that seems interesting. I will look into MiniDumpWriteDump more, but it seems like that is totally outside the managed space.
My understanding of the Debugging API (which is very poor) was that you'd want to have unmanaged code running in a different thread calling that, but have it triggerable from the managed code so you can capture context.
A related problem was that I'd like to be able to apply this type of technique to the SetEnterLeaveFunctionHooks from the Profiling API to be able to watch state as methods are executed. Anyone have any experience with this?
This doesn't seem like it should be so dang hard. Does anyone know what Visual Studio or mdbg uses to see running values in the locals and autos debugging windows? (I know, I could just download mdbg source and RTFS but I'm hoping someone here has already done that.)
BTW: I have the Debugging Applications for Microsoft .NET and Microsoft Windows by John Robbins (http://www.amazon.com/gp/product/0735615365) . An excellent book, but it falls a little short of helping me with this project. I've tried some of the goodies that are on the CD that comes with the book with mixed results. I anxiously await the release of his new book for .NET 2.0 (http://www.amazon.com/gp/product/0735622027). I mention these in case anyone has had better fortune than I in getting some of the Debugging API and Profiling API programs working so I can ask, how useful are these? Is it worth the effort to get these running?
Ben, Thanks again for your help. -LZ
Lou Zher - 09 Jun 2006 15:14 GMT After some more messing around with this idea, I have come to the realization that I cannot get the running values from .LocalVariables or .ParameterInfo directly, but I'm not giving up yet. It seems Reflection is only designed to operate with a static vision of assemblies, not running ones.
Here's the list of tactics I am looking into: * still looking into what I can do using ICorDebug * looking at the mdbg source to see how I can replicate its ability to see these vars. I stepped through its execution of the 'Print' command, but I must admit to be a bit perplexed as to where the real magic happens. * looking at some form of code injection to automate a series of calls to publicize these scoped vars to a publicly accessible thingy
Is anyone here experienced in doing any of these? Can you offer any tips/tricks regarding these techniques? Are there other techniques I may be missing? I looked at MiniDump, per Ben's suggestion, but it doesn't appear to offer any way to connect its contents with the variable names/types I can get.
Is there another group that is more appropriate for these types of questions? Is there a web forum that is worth trying?
I appreciate your help. -LZ
Ben Voigt - 09 Jun 2006 16:38 GMT > After some more messing around with this idea, I have come to the > realization that I cannot get the running values from .LocalVariables or [quoted text clipped - 15 lines] > appear to offer any way to connect its contents with the variable > names/types I can get. The minidump file is the Windows analogue to a core file. It can be opened with your normal debugger, and you'll be able to see stack trace, local variables, static variables, threads, walk through the trees of contained objects, etc. In addition, you keep your .pdb files in your development environment.
Under most circumstances, the fact that your users can't connect the contents of a minidump file with the variable names is "A Good Thing".
> Is there another group that is more appropriate for these types of > questions? Is there a web forum that is worth trying? > > I appreciate your help. > -LZ Lou Zher - 09 Jun 2006 23:01 GMT Ben, I am now playing around with mdbgcore.dll and have pretty good success with it. Using Mike Stall's example sure helped.
http://blogs.msdn.com/jmstall/archive/2005/11/28/snapshot.aspx
Because this operates as a debugger, the stack capture code has to live in a different process, which isn't quite what I wanted, but I'm making it work. I'm just using System.Diagnostics.Debugger.Break() in the exception filter to signal into the debugger and have it stack dump.
Works quite nicely. Yay! -LZ
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|