如何获取非当前线程的堆栈跟踪?
可以使用 System.Diagnostics.StackTrace 获取堆栈跟踪,但必须挂起线程。 暂停和恢复功能已过时,因此我希望存在更好的方法。
It is possible to get stacktrace using System.Diagnostics.StackTrace, but thread has to be suspended. Suspend and Resume function are obsolete, so I expect that better way exists.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
注意:跳到这个答案的底部进行更新。
这是到目前为止对我有用的方法:
如果死锁,死锁会自动释放,并且您会得到一个空跟踪。 (然后您可以再次调用它。)
我应该补充一点,经过几天的测试,我只在我的 Core i7 机器上创建过一次死锁。 然而,当 CPU 运行在 100% 时,单核虚拟机上死锁很常见。
更新:此方法仅适用于 .NET Framework。 在 .NET Core 和 .NET 5+ 中,无法调用
Suspend
和Resume
,因此您必须使用替代方法,例如 Microsoft 的 ClrMD 库。 添加对 Microsoft.Diagnostics.Runtime 包的 NuGet 引用; 然后您可以调用DataTarget.AttachToProcess
来获取有关线程和堆栈的信息。 请注意,您无法对自己的流程进行采样,因此您必须启动另一个流程,但这并不困难。 这是一个基本的控制台演示,说明了该过程,使用重定向的标准输出将堆栈跟踪发送回主机:这是 LINQPad 6+ 用于显示查询中的实时执行跟踪的机制(带有额外的检查、元数据探测和更详细的IPC)。
NB: Skip to the bottom of this answer for an update.
Here's what's worked for me so far:
If it deadlocks, the deadlock is automatically freed and you get back a null trace. (You can then call it again.)
I should add that after a few days of testing, I've only once been able to create a deadlock on my Core i7 machine. Deadlocks are common, though, on single-core VM when the CPU runs at 100%.
Update: This approach works only for .NET Framework. In .NET Core and .NET 5+,
Suspend
andResume
cannot be called, so you must use an alternative approach such as Microsoft's ClrMD library. Add a NuGet reference to the Microsoft.Diagnostics.Runtime package; then you can callDataTarget.AttachToProcess
to obtain information about threads and stacks. Note that you cannot sample your own process, so you must start another process, but that is not difficult. Here is a basic Console demo that illustrates the process, using a redirected stdout to send the stack traces back to the host:This is the mechanism that LINQPad 6+ uses to show live execution tracking in queries (with additional checks, metadata probing and a more elaborate IPC).
这是一个旧线程,但只是想警告建议的解决方案:挂起和恢复解决方案不起作用 - 我刚刚在尝试顺序挂起/StackTrace/恢复时在代码中遇到了死锁。
问题是 StackTrace 构造函数执行 RuntimeMethodHandle -> MethodBase 转换,这会更改内部 MethodInfoCache,它需要锁定。 发生死锁是因为我正在检查的线程也在进行反射,并且持有该锁。
遗憾的是,挂起/恢复的内容没有在 StackTrace 构造函数内完成 - 那么这个问题很容易被规避。
This is an old Thread, but just wanted to warn about the proposed solution: The Suspend and Resume solution does not work - I just experienced a deadlock in my code trying the sequence Suspend/StackTrace/Resume.
The Problem is that StackTrace constructor does RuntimeMethodHandle -> MethodBase conversions, and this changes a internal MethodInfoCache, which takes a lock. The deadlock occurred because the thread I was examining also was doing reflection, and was holding that lock.
It is a pity that the suspend/resume stuff is not done inside the StackTrace constructor -then this problem could easily have been circumvented.
更新 2022-04-28:此答案仅适用于 .NET Framework。 它与 .NET Core 和 .NET Standard 不兼容。 由于我们迟早都需要迁移,因此您不应该再在新代码中使用它。
正如我在评论中提到的,上面提出的解决方案仍然有很小的可能性出现死锁。 请在下面找到我的版本。
我认为,ManualResetEventSlim“fallbackThreadReady”并不是真正必要的,但为什么在这种微妙的情况下要冒任何风险呢?
Update 2022-04-28: This answer does only work in .NET Framework. It is not compatible with .NET Core and .NET Standard. Since we all need to migrate sooner or later, you should not use it in new code anymore.
As mentioned in my comment, the proposed solution above does still have a tiny probability for a deadlock. Please find my version below.
I think, the ManualResetEventSlim "fallbackThreadReady" is not really necessary, but why risk anything in this delicate case?
根据 C# 3.0 in a Nutshell,这是少数可以调用 Suspend 的情况之一/恢复。
According to C# 3.0 in a Nutshell, this is one of the few situations where it is okay to call Suspend/Resume.
看起来这在过去是受支持的操作,但不幸的是,Microsoft 已废弃此操作:https://msdn.microsoft.com/en-us/library/t2k35tat(v=vs.110).aspx
It looks like this was a supported operation in the past, but unfortunately, Microsoft made this obsolete: https://msdn.microsoft.com/en-us/library/t2k35tat(v=vs.110).aspx
我认为,如果您想在不与目标线程合作的情况下执行此操作(例如,在您的线程执行堆栈跟踪时,让其调用在信号量上阻止它的方法或其他方法),您将需要使用已弃用的 API。
一种可能的替代方法是使用基于 COM 的 ICorDebug 接口.NET 调试器使用。 MDbg 代码库可能会给您一个开始:
http://blogs .msdn.com/jmstall/archive/2005/11/07/views_on_cordbg_and_mdbg.aspx
http://geekswithblogs.net/johnsPerfBlog/archive/2008 /10/13/mdbg-a-management-wrapper-around-icordebug.aspx
I think that if you want to do this without the cooperation of the target thread (such as by having it call a method that blocks it on a Semaphore or something while your thread does the stacktrace) you'll need to use the deprecated APIs.
A possible alternative is the use the COM-based ICorDebug interface that the .NET debuggers use. The MDbg codebase might give you a start:
http://blogs.msdn.com/jmstall/archive/2005/11/07/views_on_cordbg_and_mdbg.aspx
http://geekswithblogs.net/johnsPerfBlog/archive/2008/10/13/mdbg-a-managed-wrapper-around-icordebug.aspx
这适用于 4.6 以上的完整 .Net 框架,并引用 NuGet 包:Microsoft.Diagnostics.Runtime,v2.0.226801,适用于 .Net 4.6。
您可以获得多个线程,我个人在线程中止之前使用此方法来了解原因:
REF:所有线程堆栈https://stackoverflow.com/a/24315960/495455
This works for Full .Net Framework above 4.6 with a reference to the NuGet Package: Microsoft.Diagnostics.Runtime, v2.0.226801 which works with .Net 4.6.
You can get more than one thread, I personally use this before Threads are aborted to understand why:
REF: All threads stacks https://stackoverflow.com/a/24315960/495455