Comment obtenir la stacktrace du thread non actuel?
Il est possible d'obtenir stacktrace en utilisant le système.Diagnostic.StackTrace, mais le thread doit être suspendu. Suspendre et reprendre la fonction sont obsolètes, donc je m'attends à ce que la meilleure façon existe.
6 réponses
Selon C# 3.0 en un Mot, c'est l'une des rares situations où il est possible d'appeler le Suspend/Resume.
Voici ce qui a fonctionné pour moi jusqu'à présent:
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;
}
S'il se bloque, l'impasse est automatiquement libérée et vous récupérez une trace nulle. (Vous pouvez alors l'appeler à nouveau.)
Je devrais ajouter qu'après quelques jours de tests, je n'ai pu créer qu'une seule fois un blocage sur ma machine Core i7. Les blocages sont courants, cependant, sur une machine virtuelle monocœur lorsque le processeur s'exécute à 100%.
C'est un vieux Thread, mais je voulais juste avertir de la solution proposée: la solution Suspend et Resume ne fonctionne pas - je viens de rencontrer un blocage dans mon code en essayant la séquence Suspend / StackTrace / Resume.
Le problème est que le constructeur StackTrace exécute les conversions RuntimeMethodHandle -> MethodBase, et cela change une MethodInfoCache interne, qui prend un verrou. L'impasse s'est produite parce que le fil que j'examinais faisait aussi de la réflexion, et tenait cela verrouillage.
Il est dommage que le truc suspend/resume ne soit pas fait à l'intérieur du constructeur StackTrace-alors ce problème aurait facilement pu être contourné.
Comme mentionné dans mon commentaire, la solution proposée a toujours une faible probabilité d'impasse. Veuillez trouver ma version ci-dessous.
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();
}
}
}
Je pense, le ManualResetEventSlim "fallbackThreadReady" est pas vraiment nécessaire, mais pourquoi risquer quoi que ce soit dans ce cas délicat?
Je pense que si vous voulez le faire sans la coopération du thread cible (par exemple en l'appelant une méthode qui le bloque sur un sémaphore ou quelque chose pendant que votre thread fait la stacktrace), vous devrez utiliser les API obsolètes.
Une alternative possible est l'utilisation de l'interface ICorDebug basée sur COM{[5] que les débogueurs. net utilisent. La base de code MDbg peut vous donner un début:
Il semble que c'était une opération prise en charge dans le passé, mais malheureusement, Microsoft a rendu cela obsolète: https://msdn.microsoft.com/en-us/library/t2k35tat (v=vs. 110).aspx