Можно получить трассировку стека с помощью System.Diagnostics.StackTrace, но поток должен быть приостановлен. Функции приостановки и возобновления устарели, поэтому я ожидаю, что существует лучший способ.
Как получить трассировку стека нетекущего потока?
Ответы (6)
Согласно в двух словах о C # 3.0, это одна из немногих ситуаций, когда можно вызвать приостановку /Продолжить.
Вот что у меня сработало до сих пор:
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
Это старый поток, но я просто хотел предупредить о предлагаемом решении: решение «Приостановить и возобновить» не работает - я просто столкнулся с тупиковой ситуацией в моем коде, пытаясь выполнить последовательность Suspend / StackTrace / Resume.
Проблема в том, что конструктор 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
Я думаю, что если вы хотите сделать это без сотрудничества с целевым потоком (например, заставив его вызвать метод, который блокирует его на семафоре или что-то еще, пока ваш поток выполняет трассировку стека), вам нужно будет использовать устаревшие API.
Возможной альтернативой является использование ICorDebug на основе COM интерфейс, который используют отладчики .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