現在のスレッドのスタックトレースを取得する方法は?
-
08-07-2019 - |
質問
System.Diagnostics.StackTraceを使用してスタックトレースを取得することは可能ですが、スレッドを中断する必要があります。サスペンドおよびレジューム機能は廃止されているため、より良い方法が存在することを期待しています。
解決
C#3.0 in a Nutshell によると、これは問題のない数少ない状況の1つです。 Suspend / Resumeを呼び出します。
他のヒント
これまでのところ私のために働いていたものは次のとおりです:
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;
}
デッドロックが発生すると、デッドロックは自動的に解放され、nullトレースが返されます。 (その後、再度呼び出すことができます。)
数日間のテストの後、Core i7マシンでデッドロックを作成できたのは一度だけです。ただし、CPUが100%で実行されるシングルコアVMでは、デッドロックが一般的です。
これは古いスレッドですが、提案されたソリューションについて警告したかっただけです。サスペンドとレジュームのソリューションが機能しない-シーケンスSuspend / StackTrace / Resumeを試みるコードでデッドロックが発生しました。
問題は、StackTraceコンストラクターがRuntimeMethodHandleを実行することです-> MethodBase変換。これにより、ロックを取得する内部MethodInfoCacheが変更されます。私が調べていたスレッドもリフレクションを行っていて、そのロックを保持していたため、デッドロックが発生しました。
Suspend / resumeが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"本当に必要なわけではありませんが、なぜこのデリケートなケースで何かを危険にさらすのですか?
ターゲットスレッドの協力なしでこれを実行したい場合(たとえば、スレッドがスタックトレースを実行している間にセマフォまたは何かでブロックするメソッドを呼び出すようにするなど)、非推奨のAPI。
可能な代替方法は、 COMベースのICorDebugを使用することです。 .NETデバッガーが使用するインターフェイス。 MDbgコードベースから始められるかもしれません:
これは過去にサポートされていた操作のようですが、残念ながらマイクロソフトはこれを廃止しました: https://msdn.microsoft.com/en-us/library/t2k35tat(v = vs.110).aspx