16

まず、問題があります。 私はいくつかのフリー プロジェクトを持っていますが、どのソフトウェアにもバグが含まれています。バグに遭遇したときに、スタックトレースを含むバグレポートを送ってくれる仲間のユーザーもいます。障害の場所を簡単に見つけるために、このスタック トレースの行番号を確認したいと考えています。アプリケーションが .pdb ファイルなしで出荷された場合、すべての行情報が失われるため、現在、すべてのプロジェクトが .pdb ファイルでデプロイされているため、生成されたスタック トレースにはこの番号が含まれます。しかし!しかし、私はこのファイルをディストリビューションで見たくないので、すべての .pdb を削除したいと考えています。ユーザーを混乱させたり、インストーラーのスペースを消費したりします。

Delphi ソリューション: 昔、私が Delphi プログラマーだったとき、次の手法を使用していました。例外が発生した場合、アプリケーションはスタック上を歩き、アドレスを収集します。次に、バグ レポートを受け取ったときに、収集したアドレスと、MY マシンにある対応するシンボル ファイルに基づいて、関数名と行番号を使用して有効なスタック トレースを再構築するツールを使用しました。

質問: .NET で同じことを行うための lib やテクニックなどはありますか?

ステータスの更新:非常に興味深いことです。多くの場合、質問をすることが独自の調査を開始するための最良の方法です。たとえば、私はこの問題についてしばらく考えていましたが、数日前に答えを探し始めました。

オプション 1: MiniDumps。多くのグーグル検索の後、コードからミニ ダンプを作成する方法と、マネージド ミニ ダンプからスタックを再作成する方法を見つけました。

ただし、このソリューションでは、2 つの追加アセンブリ (サイズが最大 1 MB) を再配布する必要があり、ミニ ダンプにはある程度のスペースが必要であり、ユーザーがそれらを電子メールで送信するのは不快です。したがって、私の目的からすると、現時点では受け入れられません。

オプション 2: 手がかりをくれた weiqure に感謝します。スタック フレームごとにマネージド IL オフセットを抽出できます。問題は、このオフセットに基づいて .pdb から行番号を取得する方法です。そして、私が見つけたもの:

このツールを使用すると、リリース ビルドごとに xml ファイルを作成し、それらをリポジトリに配置できます。ユーザーのマシンで例外が発生した場合、IL オフセットを使用してフォーマットされたエラー メッセージを作成できます。次に、ユーザーはこのメッセージ (非常に小さい) をメールで送信します。最後に、フォーマットされたエラー メッセージから結果のスタックを再作成する簡単なツールを作成することができます。

なぜ誰もこのようなツールを実装しないのだろうか? これが私だけにとって興味深いものだとは思いません。

4

4 に答える 4

16

System.Diagnostics.StackTrace を使用して、例外から最後の MSIL 命令のオフセットを取得できます。

// Using System.Diagnostics
static void Main(string[] args)
{
    try { ThrowError(); }
    catch (Exception e)
    {
        StackTrace st = new System.Diagnostics.StackTrace(e);
        string stackTrace = "";
        foreach (StackFrame frame in st.GetFrames())
        {
            stackTrace = "at " + frame.GetMethod().Module.Name + "." + 
                frame.GetMethod().ReflectedType.Name + "." 
                + frame.GetMethod().Name 
                + "  (IL offset: 0x" + frame.GetILOffset().ToString("x") + ")\n" + stackTrace;
        }
        Console.Write(stackTrace);
        Console.WriteLine("Message: " + e.Message);
    }
    Console.ReadLine();
}

static void ThrowError()
{
    DateTime myDateTime = new DateTime();
    myDateTime = new DateTime(2000, 5555555, 1); // won't work
    Console.WriteLine(myDateTime.ToString());
}

出力:

ConsoleApplicationN.exe.Program.Main (IL オフセット: 0x7)
で ConsoleApplicationN.exe.Program.ThrowError (IL オフセット: 0x1b)
で mscorlib.dll.DateTime..ctor (IL オフセット: 0x9)
で mscorlib.dll.DateTime. DateToTicks (IL オフセット: 0x61)
メッセージ: Year、Month、および Day パラメーターは、表現できない DateTime を記述しています。

その後、 ReflectorまたはILSpyを使用してオフセットを解釈できます。

.method private hidebysig static void ThrowError() cil managed
{
    .maxstack 4
    .locals init (
        [0] valuetype [mscorlib]System.DateTime myDateTime)
    L_0000: nop 
    L_0001: ldloca.s myDateTime
    L_0003: initobj [mscorlib]System.DateTime
    L_0009: ldloca.s myDateTime
    L_000b: ldc.i4 0x7d0
    L_0010: ldc.i4 0x54c563
    L_0015: ldc.i4.1 
    L_0016: call instance void [mscorlib]System.DateTime::.ctor(int32, int32, int32)
    L_001b: nop 
    L_001c: ldloca.s myDateTime
    L_001e: constrained [mscorlib]System.DateTime
    L_0024: callvirt instance string [mscorlib]System.Object::ToString()
    L_0029: call void [mscorlib]System.Console::WriteLine(string)
    L_002e: nop 
    L_002f: ret 
}

0x1b の前の命令が例外をスローしたことがわかります。そのための C# コードを見つけるのは簡単です。

 myDateTime = new DateTime(2000, 5555555, 1);

ここで IL コードを C# コードにマップすることもできますが、ゲインが小さすぎて労力が大きすぎると思います (ただし、リフレクター プラグインがあるかもしれません)。ILオフセットで問題ないはずです。

于 2009-06-24T13:25:56.650 に答える
5

Environment.FailFastを使用し、 Application.UnhandledExceptionでFailFast を呼び出す必要があります。ダンプ ファイルが作成されます。

MSDN から:

FailFast メソッドは、メッセージ パラメーターを使用して Windows アプリケーション イベント ログにログ エントリを書き込み、アプリケーションのダンプを作成してから、現在のプロセスを終了します。

アプリケーションの状態が修復不可能なほど破損している場合は、Exit メソッドの代わりに FailFast メソッドを使用してアプリケーションを終了し、アプリケーションの try-finally ブロックとファイナライザーを実行すると、プログラム リソースが破損します。FailFast メソッドは現在のプロセスを終了し、CriticalFinalizerObject オブジェクトを実行しますが、アクティブな try-finally ブロックまたはファイナライザーは実行しません。

ログ ファイルを収集して送信する簡単なアプリを作成できます。

現在、ダンプ ファイルを開くのは少しトリッキーです。Visual Studio はマネージ ダンプ ファイルを処理できません (.NET 4.0 で修正済み) 。WinDBGを使用できますが、 SOSを使用する必要があります。

于 2009-06-24T12:45:22.970 に答える
2

バグが発生した場合はいつでもアプリケーションのミニダンプを作成し、オフライン分析に使用できます。これには、クライアント マシンに pdb を展開する必要はありません。このリンクは、学習の出発点として役立ちます。

于 2009-06-24T12:38:49.007 に答える
1

ソースと行の情報を正確に取得するには、PDB ファイルが必要であるという正しい方向に進んでいます。PDB ファイルの出荷に関する問題が実際に妥当な懸念事項であると認識されている場合は疑問に思いますが、正当な理由があると仮定すると、これを行うことはできますが、適切なソフトウェア ビルド環境を作成するには、より多くの労力が必要になります。

Microsoft Symbol Serverはデバッグ シンボルのインデックスを作成し、後日クラッシュ ダンプがあり、シンボルを利用する必要がある場合などに使用できるように格納します。Microsoft のと同様に、Visual Studio または Windbg のいずれかを独自のシンボル サーバー インスタンスに向けることができ、アプリケーションのそのバージョンのデバッグに必要なシンボルをプルダウンします (出荷前にシンボル サーバーでシンボルにインデックスを付けたと仮定します)。

ビルドに適切なシンボルを取得したら、そのビルドに属する適切なソース ファイルがあることを確認する必要があります。

ここでMicrosoft Source Serverの出番です。Symbol Server がシンボルにインデックスを付けるのと同じように、ソース サーバーはソースにインデックスを付けて、ソフトウェア ビルドに属する適切なバージョンのソース コードがあることを確認します。

バージョン コントロールシンボル サーバー、およびソース サーバーの作業バージョンは、ソフトウェア構成管理戦略の一部である必要があります。

アプリケーションのスナップショットを生成するための API を提供するサード パーティ製のツール (商用のものもあります) がありますが、既に理解しているように、それらのスナップショットを何らかの方法で環境にアップロードするためのメカニズムが必要です。

PDB ファイルに関するジョン・ロビンス

ソース サーバーの John Robbins

Symbol Server を起動して実行する方法については、 WinDbg のドキュメントを参照してください。

于 2009-08-10T13:23:26.213 に答える