Можно получить stacktrace, используя System.Diagnostics.StackTrace, но поток должен быть приостановлен. Функция приостановки и возобновления устарела, поэтому я ожидаю, что лучший способ существует.
Как получить нетоковый поток stacktrace?
Ответ 1
В соответствии с С# 3.0 в двух словах, это одна из немногих ситуаций, когда нормально вызывать Suspend/Resume.
Ответ 2
Вот что мне помогло:
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%.
Ответ 3
Это старый поток, но он просто хотел предупредить о предлагаемом решении: решение Suspend и Resume не работает - я просто испытал тупик в своем коде, пробуя последовательность Suspend/StackTrace/Resume.
Проблема заключается в том, что конструктор StackTrace выполняет преобразования RuntimeMethodHandle → MethodBase, и это изменяет внутренний MethodInfoCache, который принимает блокировку. Тупик произошел из-за того, что поток, который я рассматривал, также делал отражение, и держал этот замок.
Жаль, что файл suspend/resume не выполняется внутри конструктора StackTrace, и тогда эту проблему можно было бы легко обойти.
Ответ 4
Как уже упоминалось в моем комментарии, предлагаемое решение все еще имеет крошечную вероятность для тупика. Пожалуйста, найдите мою версию ниже.
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" на самом деле не нужен, но зачем рисковать чем-нибудь в этом деликатном случае?
Ответ 5
Я думаю, что если вы хотите сделать это без сотрудничества с целевым потоком (например, если он вызывает метод, который блокирует его на Семафоре или что-то в то время, когда ваш поток выполняет stacktrace), вам нужно будет использовать устаревшие API.
Возможной альтернативой является использование COM-based ICorDebug интерфейса, который используют отладчики .NET. Кодовая база MDbg может дать вам начало:
Ответ 6
Похоже, что это была поддерживаемая операция в прошлом, но, к сожалению, Microsoft сделала это устаревшим: https://msdn.microsoft.com/en-us/library/t2k35tat(v=vs.110).aspx