ロックの問題を追跡するのに苦労しているので、すべてのメソッド呼び出しのエントリと終了をログに記録したいと思います。すべてのメソッドにコードを追加することなく、以前に C++ でこれを行ったことがあります。これはC#で可能ですか?
6 に答える
おそらく最善の策は、AOP (アスペクト指向プログラミング) フレームワークを使用して、メソッド実行の前後にトレース コードを自動的に呼び出すことです。AOP および .NET の一般的な選択肢はPostSharpです。
あなたの主な目標が関数のエントリ/終了ポイントとその間の時折の情報をログに記録することである場合、コンストラクターが関数 entryをトレースし、Dispose() が exit をトレースするDisposableロギング オブジェクトで良い結果が得られました。これにより、呼び出しコードは各メソッドのコードを単一のusingステートメント内に単純にラップできます。中間の任意のログ用のメソッドも提供されています。以下は、完全な C# ETW イベント トレース クラスと、関数の入口/出口ラッパーです。
using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace MyExample
{
// This class traces function entry/exit
// Constructor is used to automatically log function entry.
// Dispose is used to automatically log function exit.
// use "using(FnTraceWrap x = new FnTraceWrap()){ function code }" pattern for function entry/exit tracing
public class FnTraceWrap : IDisposable
{
string methodName;
string className;
private bool _disposed = false;
public FnTraceWrap()
{
StackFrame frame;
MethodBase method;
frame = new StackFrame(1);
method = frame.GetMethod();
this.methodName = method.Name;
this.className = method.DeclaringType.Name;
MyEventSourceClass.Log.TraceEnter(this.className, this.methodName);
}
public void TraceMessage(string format, params object[] args)
{
string message = String.Format(format, args);
MyEventSourceClass.Log.TraceMessage(message);
}
public void Dispose()
{
if (!this._disposed)
{
this._disposed = true;
MyEventSourceClass.Log.TraceExit(this.className, this.methodName);
}
}
}
[EventSource(Name = "MyEventSource")]
sealed class MyEventSourceClass : EventSource
{
// Global singleton instance
public static MyEventSourceClass Log = new MyEventSourceClass();
private MyEventSourceClass()
{
}
[Event(1, Opcode = EventOpcode.Info, Level = EventLevel.Informational)]
public void TraceMessage(string message)
{
WriteEvent(1, message);
}
[Event(2, Message = "{0}({1}) - {2}: {3}", Opcode = EventOpcode.Info, Level = EventLevel.Informational)]
public void TraceCodeLine([CallerFilePath] string filePath = "",
[CallerLineNumber] int line = 0,
[CallerMemberName] string memberName = "", string message = "")
{
WriteEvent(2, filePath, line, memberName, message);
}
// Function-level entry and exit tracing
[Event(3, Message = "Entering {0}.{1}", Opcode = EventOpcode.Start, Level = EventLevel.Informational)]
public void TraceEnter(string className, string methodName)
{
WriteEvent(3, className, methodName);
}
[Event(4, Message = "Exiting {0}.{1}", Opcode = EventOpcode.Stop, Level = EventLevel.Informational)]
public void TraceExit(string className, string methodName)
{
WriteEvent(4, className, methodName);
}
}
}
それを使用するコードは次のようになります。
public void DoWork(string foo)
{
using (FnTraceWrap fnTrace = new FnTraceWrap())
{
fnTrace.TraceMessage("Doing work on {0}.", foo);
/*
code ...
*/
}
}
プロファイラーは、開発中に実行中のコードを確認するのに最適ですが、本番環境でカスタム トレースを実行する機能を探している場合は、Denis G. が言及したように、PostSharpが最適なツールです。すべてを変更する必要はありません。コードと簡単にオン/オフを切り替えることができます。
また、数分で簡単にセットアップできます。PostSharp の作成者である Gaël Fraiteur は、既存のアプリにトレースを追加することがいかに簡単かを示すビデオさえ持っています。ドキュメント セクション
に例とチュートリアルがあります。
Red Gate のANTS Profilerを使用するのが最善の策です。それができない場合は、ウィンザー城のインターセプターを調べてください。ただし、IoC を介して型をロードしていることを前提としています。
リフレクションも別の方法です。System.Reflection.Emitメソッドを使用して、コードをメモリに「書き込む」ことができます。そのコードは、メソッドのコードを置き換えて実行できますが、適切なログを記録します。しかし、それで頑張ってください... Aspect#のようなアスペクト指向プログラミングフレームワークを使用する方が簡単です。
ロックの問題が定着するのを待って、メモリダンプを実行し、さまざまなスレッドでコールスタックを分析している可能性があります。DebugDiagまたはWindows用のデバッグツールに付属のadplusスクリプト(この場合はハングモード)を使用できます。
Tess Ferrandezには、 .NETメモリダンプを使用してさまざまな問題をデバッグする方法を学ぶための優れたラボシリーズもあります。私はそれを強くお勧めします。
それが起こっていることをどうやって知っていますか?これがマルチスレッドアプリケーションの場合は、状態をテストし、実行時にSystem.Diagnostics.Debugger.Break()を呼び出して検出することをお勧めします。次に、[スレッド]ウィンドウを開き、関連する各スレッドの呼び出しスタックをステップ実行します。