Domanda

È possibile ottenere stacktrace usando System.Diagnostics.StackTrace, ma il thread deve essere sospeso. Le funzioni di sospensione e ripresa sono obsolete, quindi mi aspetto che esista un modo migliore.

È stato utile?

Soluzione

Secondo C # 3.0 in breve , questa è una delle poche situazioni in cui va bene per chiamare Suspend / Resume.

Altri suggerimenti

Ecco cosa ha funzionato per me finora:

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

Se si verifica un deadlock, il deadlock viene automaticamente liberato e si ottiene una traccia nulla. (Puoi quindi chiamarlo di nuovo.)

Dovrei aggiungere che dopo alcuni giorni di test, sono stato solo una volta in grado di creare un deadlock sulla mia macchina Core i7. I deadlock sono comuni, tuttavia, sulla VM single-core quando la CPU funziona al 100%.

Questo è un vecchio thread, ma volevo solo mettere in guardia sulla soluzione proposta: la soluzione Suspend and Resume non funziona - ho appena avuto un deadlock nel mio codice mentre provavo la sequenza Suspend / StackTrace / Resume.

Il problema è che il costruttore StackTrace esegue RuntimeMethodHandle - > Conversioni di MethodBase e questo cambia un MethodInfoCache interno, che accetta un blocco. Il deadlock si è verificato perché anche il thread che stavo esaminando stava riflettendo e teneva quel blocco.

È un peccato che le cose di sospensione / ripresa non vengano eseguite all'interno del costruttore StackTrace, quindi questo problema potrebbe essere facilmente aggirato.

Come menzionato nel mio commento, la soluzione proposta ha ancora una piccola probabilità di un deadlock. Di seguito trovi la mia versione.

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

Penso che il ManualResetEventSlim "fallbackThreadReady" non è davvero necessario, ma perché rischiare qualcosa in questo caso delicato?

Penso che se vuoi farlo senza la collaborazione del thread target (ad esempio chiamandolo un metodo che lo blocca su un semaforo o qualcosa mentre il tuo thread esegue lo stacktrace) dovrai usare il API obsolete.

Una possibile alternativa è l'uso di ICorDebug basato su COM utilizzata dai debugger .NET. La base di codice MDbg potrebbe darti un inizio:

Sembra che questa sia stata un'operazione supportata in passato, ma sfortunatamente Microsoft l'ha resa obsoleta: https://msdn.microsoft.com/en-us/library/t2k35tat (v = vs.110) .aspx

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top