Pregunta

Es posible obtener stacktrace usando System.Diagnostics.StackTrace, pero el hilo debe suspenderse. La función Suspender y Reanudar está obsoleta, por lo que espero que exista una mejor manera.

¿Fue útil?

Solución

Según C # 3.0 en pocas palabras , esta es una de las pocas situaciones en las que está bien para llamar a Suspender / Reanudar.

Otros consejos

Esto es lo que me ha funcionado hasta ahora:

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

Si se interrumpe, el punto muerto se libera automáticamente y usted obtiene un rastro nulo. (Luego puede volver a llamarlo).

Debo agregar que después de unos días de pruebas, solo una vez pude crear un punto muerto en mi máquina Core i7. Sin embargo, los puntos muertos son comunes en máquinas virtuales de un solo núcleo cuando la CPU se ejecuta al 100%.

Este es un subproceso antiguo, pero solo quería advertir sobre la solución propuesta: la solución Suspender y reanudar no funciona: acabo de experimentar un punto muerto en mi código al intentar la secuencia Suspender / StackTrace / Resume.

El problema es que el constructor StackTrace hace RuntimeMethodHandle - > Conversiones de MethodBase, y esto cambia un MethodInfoCache interno, que toma un bloqueo. El punto muerto se produjo porque el hilo que estaba examinando también estaba reflexionando y mantenía ese bloqueo.

Es una pena que las cosas de suspender / reanudar no se realicen dentro del constructor StackTrace, entonces este problema podría haberse evitado fácilmente.

Como mencioné en mi comentario, la solución propuesta todavía tiene una pequeña probabilidad de un punto muerto. Encuentra mi versión a continuación.

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

Creo que el ManualResetEventSlim " fallbackThreadReady " no es realmente necesario, pero ¿por qué arriesgar algo en este caso delicado?

Creo que si quieres hacer esto sin la cooperación del hilo objetivo (como hacer que llame a un método que lo bloquea en un semáforo o algo mientras tu hilo hace el seguimiento de la pila) necesitarás usar el API obsoletas.

Una posible alternativa es el uso de ICorDebug basado en COM interfaz que utilizan los depuradores .NET. La base de código MDbg podría darle un comienzo:

Parece que esta fue una operación admitida en el pasado, pero desafortunadamente, Microsoft hizo que esta fuera obsoleta: https://msdn.microsoft.com/en-us/library/t2k35tat (v = vs.110) .aspx

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top