在调试 .NET CLR 应用程序时如何查看评估堆栈上的局部变量?
我正在使用 Windbg(带有 sos 扩展)并尝试调试崩溃的应用程序。 我能够转储引发异常的调用的 IL,并且通过检查代码,如果我可以转储评估堆栈的内容,我似乎可以获得所需的信息。 是否可以用 WinDbg & 做什么? 求救?
这是我所做的:
- 启动 WinDbg
- 附加到崩溃的进程
- loadby sos mscorwks (加载 sos 扩展)
!token2ee theModuleName 0600009a (其中 theModuleName 是应用程序(和程序集)的名称 我正在调试,9a 是 Windows 错误报告工具报告的崩溃方法的方法偏移量。 我得到这个输出:
模块:000e2c3c(应用程序名称.exe)
代币:0x0600009a
方法描述:000e67c8
名称:MyNamespace.MyClassName.theCulpritFn(MyOtherClass)
JITTED 代码地址:0081b1d0!dumpil 00e67c8 (转储相关方法的 IL)。 这是输出:
<前><代码> // .. // .. 为简洁起见,省略了前面的代码 。抓住 { IL_0071: stloc.0 IL_0072:没有 IL_0073: ldstr“无法设置 CurrentServer 属性:” IL_0078: ldarg.0 IL_0079: ldfld MyNamespace.MyClassName::_currentServer IL_007e: brtrue.s IL_0087 IL_0080:ldstr“” IL_0085:兄弟 IL_0092 IL_0087: ldarg.0 IL_0088: ldfld MyNamespace.MyClassName::_currentServer IL_008d: callvirt MyNamespace.MyOtherClass::get_Name IL_0092:调用 System.String::Concat IL_0097: ldloc.0 IL_0098: newobj MyNamespace.MySpecialExceptionType::ctor IL_009d:抛出 }问题是:有没有办法让我看到在抛出异常之前压入堆栈的内容。 如果我没记错的话,传递给异常构造函数的参数应该是计算堆栈上索引 0 处的局部变量。
PS 当我尝试调用 !clrstack -a 时,我收到一条消息: 无法遍历托管堆栈。 当前线程可能不是托管线程。 您可以运行 !threads 来获取进程中托管线程的列表。
谢谢!
I'm using Windbg (with the sos extention) and trying to debug a crashed application. I was able to dump the IL of the call that threw the exception and by examining the code, it seems like I could obtain the information that I need if I could dump the contents of the evaluation stack. Is it possible to do what with WinDbg & sos ?
Here's what I did:
- Started WinDbg
- Attached to the crashed process
- loadby sos mscorwks (to load the sos extension)
!token2ee theModuleName 0600009a (where theModuleName is the name of the app(and assembly) t
hat I'm debuging and 9a is the method offset of the method that crashed as reported by the Windows Error Reporting tool. I got this output:Module: 000e2c3c (theApplicationName.exe)
Token: 0x0600009a
MethodDesc: 000e67c8
Name: MyNamespace.MyClassName.theCulpritFn(MyOtherClass)
JITTED Code Address: 0081b1d0!dumpil 00e67c8 (which dumped the IL for the method in question). This is the output:
// .. // .. the previous code omitted for brevity .catch { IL_0071: stloc.0 IL_0072: nop IL_0073: ldstr "Can't set CurrentServer property for: " IL_0078: ldarg.0 IL_0079: ldfld MyNamespace.MyClassName::_currentServer IL_007e: brtrue.s IL_0087 IL_0080: ldstr "" IL_0085: br.s IL_0092 IL_0087: ldarg.0 IL_0088: ldfld MyNamespace.MyClassName::_currentServer IL_008d: callvirt MyNamespace.MyOtherClass::get_Name IL_0092: call System.String::Concat IL_0097: ldloc.0 IL_0098: newobj MyNamespace.MySpecialExceptionType::ctor IL_009d: throw }
The question is: is there a way for me to see what was pushed on the stack before the exception was thrown. If I'm not mistaken, the argument passed into the exception constructor should be the local variable at index 0 on the evaluation stack.
P.S. When I tried to call !clrstack -a I got a message saying:
Unable to walk the managed stack. The current thread is likely not a managed thread. You can run !threads to get a list of managed threads in the process.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您需要识别并选择正确的线程。 当前线程的id显示在WinDbg提示符中。
!threads
将显示应用程序中的所有托管线程。 一旦确定,您可以使用~Xs
切换线程,其中 X 是线程的 WinDbg id。!clrstack
将向您显示堆栈跟踪。 如果您想要局部变量和/或参数,请使用-l
/-p
(或两者都使用-a
)。您可以使用
~*e!clrstack
浏览所有线程并列出它们的调用堆栈。如果 local/parameters 不能满足您的需要,请使用
!dso
显示推入堆栈的对象。You need to identify and select the right thread. The id of the current thread is displayed in the WinDbg prompt.
!threads
will display all managed threads in your application. Once identified you can switch threads using~Xs
where X is the WinDbg id for the thread.!clrstack
will show you the stack trace. If you want locals and/or parameters use-l
/-p
(or-a
for both).You can go through all the threads and list their call stack with
~*e!clrstack
.If local/parameters doesn't give you what you need, use
!dso
to display objects pushed onto the stack.哇,如果没有真正的转储来翻阅,这很难。 :-)
这里有几个问题..然后我将添加一个之前在上面的答案中未列出的命令..
问题:
你确定你正在捕获第一个异常,而不仅仅是最后一个未处理的异常吗并拆除进程?
您能否查看引发异常的线程的本机调用堆栈。
注意:应该调用 RaiseException() 或访问冲突。
谨防异步异常,它们与您的代码完全无关,并且可能会进入并将您弹出到 catch 块中...或更糟(Thread.AbortException/System.OutofMemoryException 和... StackOverflowException :-)
要进一步挖掘,您可以在错误线程上运行 !dumpstack 。输出不准确,但它确实在遍历线程调用堆栈方面做了出色的工作,您可能会得到幸运的是,您会在消息中看到一个 @ 符号,该符号引用了带有 .cxr 和 .exr 的异常。 如果这样做,那么您可以运行 .cxr -cxr-address 并查看异常链中的第一个异常是什么。
要点:虽然我可能回避了这个问题,但如果调用 WER 的异常未处理并取消了该进程,您可能需要记录初始调用堆栈或将先前的异常添加为内部异常这样您就可以确定根本原因。
谢谢,
亚伦
Wow, this hard without an actual dump to poke through. :-)
Here are a couple of questions.. and then I'll add one command that was not listed previously in the answers above..
Questions:
Are you sure you are catching the first exception and not just the last one that was unhandled and torn down the process?
Can you take a look at the native call stack for the thread that threw the exception.
Note: there should be a call to RaiseException() or a access violation.
Beware of Asynchronous exceptions that have absolutely nothing to do with your code and can come in and pop you into a catch block... or worse (Thread.AbortException/System.OutofMemoryException and... StackOverflowException :-)
To dig in further you run !dumpstack on the faulting thread.. The output is not exact however it does do an amazing job of walking the threads call stack and you may get lucky and see a @ symbol that references a exception with .cxr and .exr in the message. If you do, then you can run .cxr -cxr-address and see what the first exception was in the exception chain.
Point: Although I may be side stepping the question, if the exception invoked WER it was not handled and took down the process you may want to Log the initial call stack or add the previous exception as an inner exception so you can determine the root cause.
Thanks,
Aaron