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

有帮助吗?

解决方案

根据坚果壳中的C#3.0 ,这是少数情况可以接受的情况之一调用暂停/恢复。

其他提示

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

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上很常见。

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

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

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

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

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"是不是真的有必要,但为什么在这个微妙的情况下冒任何风险?

我认为,如果您想在不与目标线程合作的情况下执行此操作(例如,在您的线程执行堆栈跟踪时,让其调用在信号量上阻止它的方法或其他方法),您将需要使用已弃用的 API。

一个可能的替代方案是使用 基于 COM 的 ICorDebug .NET 调试器使用的接口。MDbg 代码库可能会给您一个开始:

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top