如何获取非当前线程的堆栈跟踪?

时间:2008-11-12 19:24:47

标签: .net multithreading debugging stack-trace visual-studio-debugging

可以使用System.Diagnostics.StackTrace获取堆栈跟踪,但必须暂停线程。暂停和恢复功能已经过时,所以我希望存在更好的方法。

6 个答案:

答案 0 :(得分:21)

到目前为止,这对我有用:

StackTrace GetStackTrace (Thread targetThread)
{
    StackTrace stackTrace = null;
    var ready = new ManualResetEventSlim();

    new Thread (() =>
    {
        // Backstop to release thread in case of deadlock:
        ready.Set();
        Thread.Sleep (200);
        try { targetThread.Resume(); } catch { }
    }).Start();

    ready.Wait();
    targetThread.Suspend();
    try { stackTrace = new StackTrace (targetThread, true); }
    catch { /* Deadlock */ }
    finally
    {
        try { targetThread.Resume(); }
        catch { stackTrace = null;  /* Deadlock */  }
    }

    return stackTrace;
}

如果死锁,则自动释放死锁并返回空跟踪。 (然后你可以再次调用它。)

我应该补充说,经过几天的测试,我只能在我的Core i7机器上创建死锁。但是,当CPU以100%运行时,死锁在单核VM上很常见。

答案 1 :(得分:16)

这是一个旧的线程,但只是想提出有关建议的解决方案的警告:暂停和恢复解决方案不起作用 - 我刚刚在我的代码中遇到了死锁,尝试序列Suspend / StackTrace / Resume。

问题是StackTrace构造函数执行RuntimeMethodHandle - > MethodBase转换,这会更改内部MethodInfoCache,它会锁定。发生了死锁,因为我正在检查的线程也正在进行反射,并持有该锁定。

很遗憾,挂起/恢复内容不是在StackTrace构造函数内部完成的 - 然而这个问题很容易被绕过。

答案 2 :(得分:11)

根据C# 3.0 in a Nutshell,这是可以调用暂停/恢复的少数情况之一。

答案 3 :(得分:11)

正如我在评论中所提到的,建议的解决方案仍然存在很小的死锁概率。请在下面找到我的版本。

private static StackTrace GetStackTrace(Thread targetThread) {
using (ManualResetEvent fallbackThreadReady = new ManualResetEvent(false), exitedSafely = new ManualResetEvent(false)) {
    Thread fallbackThread = new Thread(delegate() {
        fallbackThreadReady.Set();
        while (!exitedSafely.WaitOne(200)) {
            try {
                targetThread.Resume();
            } catch (Exception) {/*Whatever happens, do never stop to resume the target-thread regularly until the main-thread has exited safely.*/}
        }
    });
    fallbackThread.Name = "GetStackFallbackThread";
    try {
        fallbackThread.Start();
        fallbackThreadReady.WaitOne();
        //From here, you have about 200ms to get the stack-trace.
        targetThread.Suspend();
        StackTrace trace = null;
        try {
            trace = new StackTrace(targetThread, true);
        } catch (ThreadStateException) {
            //failed to get stack trace, since the fallback-thread resumed the thread
            //possible reasons:
            //1.) This thread was just too slow (not very likely)
            //2.) The deadlock ocurred and the fallbackThread rescued the situation.
            //In both cases just return null.
        }
        try {
            targetThread.Resume();
        } catch (ThreadStateException) {/*Thread is running again already*/}
        return trace;
    } finally {
        //Just signal the backup-thread to stop.
        exitedSafely.Set();
        //Join the thread to avoid disposing "exited safely" too early. And also make sure that no leftover threads are cluttering iis by accident.
        fallbackThread.Join();
    }
}
}

我认为,ManualResetEventSlim“fallbackThreadReady”并不是必需的,但为什么在这种微妙的情况下会冒任何风险呢?

答案 4 :(得分:2)

我认为,如果你想在没有目标线程的合作的情况下这样做(比如通过让它调用一个方法来阻塞信号量或者你的线程执行堆栈跟踪的东西),你需要使用弃用的API。

可能的替代方法是使用.NET调试器使用的COM-based ICorDebug接口。 MDbg代码库可能会给你一个开始:

答案 5 :(得分:2)

过去看起来这是一个受支持的操作,但不幸的是,微软已经过时了:https://msdn.microsoft.com/en-us/library/t2k35tat(v=vs.110).aspx