Възможно е да получите stacktrace с помощта на System.Diagnostics.StackTrace, но нишката трябва да бъде спряна. Функциите за спиране и възобновяване са остарели, така че очаквам, че съществува по-добър начин.
Как да получа проследяване на стека на нетекуща нишка?
Отговори (6)
Според C# 3.0 in a Nutshell, това е една от малкото ситуации, в които е добре да извикате Suspend /Продължи.
Ето какво работи за мен досега:
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. Блокировките обаче са често срещани при едноядрени виртуални машини, когато процесорът работи на 100%.
ManualResetEvent
, за да избегнете изпълнението на targetThread.Resume() и хвърлянето на изключение всеки път... if (!noDeadLockSafeGuard.WaitOne(200)) { try { targetThread.Resume( ); } хващам { } }
- person Vincent Van Den Berghe; 20.07.2012
#pragma warning disable 0618
преди метода и #pragma warning restore 0618
след това, за да накара този код да се компилира.
- person Warren Rumak; 09.09.2013
Това е стара тема, но просто исках да предупредя за предложеното решение: Решението за спиране и възобновяване не работи - току-що получих задънена улица в моя код, опитвайки последователността Спиране/StackTrace/Възобновяване.
Проблемът е, че конструкторът на 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" всъщност не е необходим, но защо да рискуваме нещо в този деликатен случай?
Изглежда, че това е била поддържана операция в миналото, но за съжаление Microsoft направи това остаряло: https://msdn.microsoft.com/en-us/library/t2k35tat(v=vs.110).aspx
Мисля, че ако искате да направите това без сътрудничеството на целевата нишка (като например като я накарате да извика метод, който го блокира на Semaphore или нещо подобно, докато нишката ви прави проследяването на стека), ще трябва да използвате остарелите API.
Възможна алтернатива е използването на COM-базиран ICorDebug интерфейс, който .NET дебъгерите използват. Кодовата база MDbg може да ви даде начало:
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