How to get non-current thread's stacktrace?

前端 未结 6 1674
别那么骄傲
别那么骄傲 2020-12-05 02:28

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 e

相关标签:
6条回答
  • 2020-12-05 02:57

    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.

    0 讨论(0)
  • 2020-12-05 02:59

    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
    0 讨论(0)
  • 2020-12-05 03:00

    As mentioned in my comment, the proposed solution above does still have a tiny probability for a deadlock. Please find my version below.

    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();
        }
    }
    }
    

    I think, the ManualResetEventSlim "fallbackThreadReady" is not really necessary, but why risk anything in this delicate case?

    0 讨论(0)
  • 2020-12-05 03:04

    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

    0 讨论(0)
  • 2020-12-05 03:10

    According to C# 3.0 in a Nutshell, this is one of the few situations where it is okay to call Suspend/Resume.

    0 讨论(0)
  • 2020-12-05 03:13

    Here's what's worked for me so far:

    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;
    }
    

    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%.

    0 讨论(0)
提交回复
热议问题