適用対象: .NET Core 2.1、.NET Core 3.1、.NET 5
この記事では、createdump ツールを使用して Linux で .NET Core クラッシュ ダンプ ファイルをキャプチャし、lldb を使用してクラッシュの問題を診断する方法について説明します。
前提条件
これらのトラブルシューティング ラボに従う最小要件は、ASP.NET Core アプリケーションを使用して、CPU と CPU のパフォーマンスの低い問題を示す必要があります。
インターネット上でこの目標を達成するためのいくつかのサンプル アプリケーションを見つけることができます。 たとえば、 Microsoft の単純な webapi サンプルをダウンロードして設定し 望ましくない動作を示すことができます。 または、サンプル プロジェクトとして、BuggyAmb ASP.NET Core アプリケーションを使用できます。
このシリーズの前の部分に従っている場合は、次のセットアップを準備しておく必要があります。
- Nginx は、次の 2 つの Web サイトをホストするように構成されています。
- 1 つ目は、 myfirstwebsite ホスト ヘッダー (
http://myfirstwebsite) を使用して要求をリッスンし、ポート 5000 でリッスンするデモ ASP.NET Core アプリケーションに要求をルーティングします。 - 2 つ目は、 buggyamb ホスト ヘッダー (
http://buggyamb) を使用して要求をリッスンし、ポート 5001 でリッスンする 2 番目の ASP.NET Core サンプル バグのあるアプリケーションに要求をルーティングします。
- 1 つ目は、 myfirstwebsite ホスト ヘッダー (
- ASP.NET コア アプリケーションは、サーバーが再起動されたとき、またはアプリケーションが応答を停止したときに自動的に再起動するサービスとして実行されている必要があります。
- Linux ローカル ファイアウォールが有効になっており、SSH トラフィックと HTTP トラフィックを許可するように構成されています。
Note
セットアップの準備ができていない場合は、「Part 2 Core アプリ ASP.NET 作成して実行する」に進みます。
このラボを続行するには、Nginx の背後で実行されている少なくとも 1 つの問題のある ASP.NET Core Web アプリケーションが必要です。
このラボの目標
自動的に生成されたコア ダンプ ファイルは、すべてのマネージド状態情報が含まれていないため、役に立ちません。 .NET Core コアのクラッシュ ダンプ ファイルをキャプチャするために推奨されるツールは createdump です。
このパートでは、createdump を使用してクラッシュ ダンプ ファイルをキャプチャし、lldb でファイルを開いてクラッシュの問題を診断する方法について説明します。
プロセス終了時に実行するように createdump を構成する
Createdump は、すべての .NET Core ランタイムと共に自動的にインストールされます。
createdump 構成ポリシードキュメントで説明されているように、環境変数を含む構成オプションを設定できます。 これらは、パラメーターとして createdump コマンドに渡されます。 サポートされている環境変数を次に示します。
COMPlus_DbgEnableMiniDump: 1 に設定すると、終了時にコア ダンプの自動生成が有効になります。 既定値は 0 です。COMPlus_DbgMiniDumpType: これは、作成されるミニ ダンプ ファイルの種類です。 この既定値は、 2 (または、MiniDumpWithPrivateReadWriteMemoryの列挙型) です。 つまり、生成されるダンプ ファイルには、GC ヒープと、プロセス内のすべての既存のスレッドのスタック トレースをキャプチャするために必要な情報が含まれます。COMPlus_DbgMiniDumpName: 設定されている場合は、テンプレートとして使用して、ダンプ ファイルのパスとファイル名を作成します。 pid は、%dパラメーターを使用して名前に入れることができます。 既定のテンプレートは /tmp/coredump.%dです。 この環境変数を使用すると、出力ディレクトリを構成できます。COMPlus_CreateDumpDiagnostics: 1 に設定すると、createdump ツールの診断メッセージ (TRACE マクロ) が有効になります。 この設定は、createdump が期待どおりに動作せず、メモリ ダンプ ファイルを生成しない場合に便利です。
これらの変数の詳細については、 createdump 構成ポリシーを参照してください。
ここで重要な変数は COMPlus_DbgEnableMiniDump。 この環境変数を 1 に設定する必要があります。 この環境を設定するには、いくつかの方法があります。
- アプリケーションの構成ファイルで設定します。
export COMPlus_DbgEnableMiniDump=1コマンドを使用して設定します。 この設定は、オペレーティング システムの再起動後も保持されません。 そのため、再起動後も設定を有効のままにする場合は、永続的として設定する必要があります。- ASP.NET Core サービス ユニット ファイルで設定します。
コア サービス ユニット ファイルでこの変数 ASP.NET 設定するのが最も簡単な方法です。 欠点は、サービスを再起動する必要があるということです。 このトラブルシューティング セクションでは、これが示されているオプションになります。
バグのあるアプリケーションのサービス ファイルを開き、 COMPlus_DbgEnableMiniDump=1 環境変数を追加します。 これは、このトレーニングの前の章で何度か行ったのと同じです。
構成が変更されたため、サービス構成を再読み込みする必要があります。 次に、BuggyAmb サービスを再起動します。
これらの変更を行った後、クラッシュの問題を再現します。 createdump が機能する場合は、ダンプ ファイルを /tmp/ ディレクトリに coredump.< として書き込む必要があります。PID>。 同じ手順に従って問題を再現します。
- Crash 3を選択します。 ページは正しく読み込まれますが、プロセスがクラッシュしたことを示す誤ったメッセージが返されます。
- [ Slow を選択します。 これにより、製品テーブルの代わりに "HTTP 502" 応答コード (無効なゲートウェイ エラー) が生成されます。
- 問題が発生すると、どのページもレンダリングされず、同じエラー メッセージが 10 ~ 15 秒間表示されます。
- 10 ~ 15 秒後、アプリケーションは正常に動作し始めます。
これで、 /tmp ディレクトリにコア ダンプ ファイルが作成されます。
コア ダンプ ファイルがない場合は、 buggyamb.service ファイルが正しく構成されていることを確認してください。 また、サービス構成を再読み込みし、サービスを再起動する必要があります。
lldb でコア ダンプ ファイルを開く
サンプル分析に従って、ダンプ ファイルを ~/dumps/ フォルダーに移動することをお勧めします。 ダンプ ファイルを開くには、 lldb --core ~/dumps/coredump.<10354>を実行します。 このコマンドでは、 10354 プレースホルダーをプロセスの PID に置き換えます。
Note
以前にダンプ ファイルを開いて lldb を操作したことがある場合は、シンボルを設定し、SOS をインストール済みです。 シンボルをもう一度ダウンロードしなくても、同じ .NET Core バージョンのダンプ ファイルを開くことができます。 ただし、シンボルがまだダウンロードされていない別の .NET Core バージョン ダンプ ファイルを開いた場合は、分析を開始する前に、そのバージョンのシンボルをダウンロードする必要があります。
SOS clrstack コマンドを実行して、マネージド呼び出し履歴を表示します。 システムによって生成されたコア ダンプ ファイルを使用して同じコマンドを実行したときにエラーが発生したことを思い出してください。 今回は、適切なマネージド呼び出し履歴が表示されます。
これは良いスタートです。 ただし、表示される呼び出し履歴は、デバッグされたプロセスのメイン スレッドに属しています。 例外がスローされるスレッドではありません。
Note
Windows 上の WinDbg でクラッシュ ダンプ ファイルを開くと、WinDbg はクラッシュの原因となったスレッドを直接選択します。 ただし、これは lldb では当たりません。 lldb では、WinDbg はメモリ ダンプを生成するためにデバッガーをトリガーしたスレッドを自動的に選択しません。
この WinDbg 動作はデバッグ時に役立ちますが、lldb のこの機能の欠如は世界の終わりではありません。 代わりに、すべてのスレッドを調べて、例外がスローされる可能性がある場所を判断できます。 まず、 thread list コマンドを使用してネイティブ スレッドを確認します。
ダンプ ファイルが生成された時点で何が実行されていたかを理解できるように、すべてのスレッド呼び出しスタックの迅速な検査を実行することから始めるのが常に良い考えです。 最初に、 thread list コマンドを含むネイティブ スレッドの一覧を見てください。
Note
リスト (thread #1) の最初のスレッドの近くにあるアスタリスク (*) は、それがアクティブ なスレッドであることを示します。
ネイティブ スレッドの検査では、あまり明らかにされません。 これは .NET Core アプリケーションであるため、SOS clrthreads コマンドを実行して CLR スレッドの一覧を調べます。 このコマンドは、アプリケーションで実行されているマネージド スレッドを一覧表示します。
このスクリーンショットには、すべてのマネージド スレッドが表示されていません。 ただし、注目すべき詳細はスクリーンショットに一覧表示されています。 Thread #15 には、システム ログに表示されている例外があります。
そのスレッドの呼び出し履歴を調べます。 これを行うには、まず対象のスレッドを選択する必要があります。 実行するメモリ ダンプ分析では、スレッド番号が異なる可能性が最も高くなります。 アクティブ スレッドとして別のスレッドを選択するには、 thread select コマンドを使用し、lldb dbg スレッド ID を渡します。 たとえば、 thread select 15 を実行してスレッド 15 に切り替えます。 その後、実行するすべてのコマンドは、そのスレッドのコンテキストに含まれます。 ネイティブ呼び出し履歴を表示するには、 bt (バック トレース) コマンドを実行します。
このスクリーンショットでわかるように、このスレッドはクラッシュをトリガーしたスレッドです。
PROCEndProcessおよびPROCAbort()は、ハンドルされない例外の後に呼び出されます。POCCreateCrashDumpは、クラッシュ ダンプが .NET Core によって書き込まれたことを示します。
clrstack コマンドを実行して、マネージド呼び出し履歴を調べることができます。 しかし、これはあまり明らかにされません。 pe コマンドを実行して、例外の詳細を取得します。
この情報は、LogTheRequest() メソッドの Crash3 ページでSystem.Net.HttpWebRequestがトリガーされたことを示します。 これは、問題の特定に役立つ重要な情報です。 しかし、HTTP 要求の URL を見つけたい場合はどうでしょうか。 続行するには、スタックで参照されているオブジェクトを調べて、この一覧から詳細情報を収集できるかどうかを確認します。 現在のスタックの境界内にあるすべてのマネージド オブジェクトを表示するには、 dso実行します。
これは役に立ちません。 System.Net.HttpWebRequestインスタンスは表示されません。 例外のインスタンスがあり、既に検査済みです。 そのため、このコマンドは原因に関連する新しい情報を生成しませんでした。
すべてのマネージド オブジェクトはマネージド ヒープに格納され、 dumpheapを実行してマネージド ヒープを確認できます。 パラメーターを指定せずに dumpheap を実行しないでください。その場合、コマンドはマネージド ヒープ内のすべてのオブジェクト (大きなリスト) を一覧表示します。 代わりに、 dumpheap -stat コマンドを使用してヒープの統計を取得できます。
次の形式でコマンドを実行することで、もう 1 つの戦術を使用して統計を絞り込むことができます。
dumpheap -stat -type System.Net.HttpWebRequest
次のスクリーンショットは、名前に文字列 System.Net.HttpWebRequest を含むマネージド オブジェクトの統計情報を表示します。
サンプル アプリケーションでは、マネージド ヒープに System.Net.HttpWebRequest オブジェクトが 1 つだけ存在します。 前の一覧では、 HttpWebRequest エントリの横に表示されるアドレスは、メモリ内のそのオブジェクトのアドレスではありません。 代わりに、 System.Net.HttpWebRequest型のオブジェクトの "メソッド テーブル" に対応するアドレスです。 オブジェクトの実際のリストを取得するには、次の方法で、そのメソッド テーブル (MT) アドレスを dumpheap コマンドに渡します。
dumpheap -mt <address>
たとえば、 dumpheap -mt 00007f53623cb640 を実行してオブジェクトのアドレスを検索します。
これで、問題のあるオブジェクトのアドレスを識別できるようになりました。 この例では 00007f51300c0868 です。 そのアドレスを dumpobj コマンドに渡すことで、オブジェクトのプロパティを調査できます。 これにより、そのオブジェクトのプロパティが一覧表示されます。 この例では、 dumpobj 00007f51300c0868 を実行してオブジェクトのプロパティを調べます。
Note
System.Net.HttpWebRequest オブジェクトを調査中で、そのプロパティの 1 つが_requestUri。 これは、 System.Uri 型のオブジェクトです。 URI を決定する必要があります。 したがって、 _requestUri プロパティのアドレスを dumpobj コマンドに渡します
System.Uri オブジェクトのアドレスをコピーし、dumpobjをもう一度使用して調査します。 dumpobj 00007f51300bfbb8 を実行します。 生成したメモリ ダンプ ファイル内のオブジェクトのアドレスは、最も確実に異なります。 一覧には、System.Uriの_string プロパティが表示されます。
_stringのアドレスをコピーし、それに対して dumpobj コマンドをもう一度実行します。 dumpobj 00007f51300bfb40 を実行します。 結果は次のスクリーンショットに示されています。
最後に、HttpWebRequest: http://buggyamb/Problem/Api/NotExistingLoggingApiの URL を見つけることができます。 名前が示すように、これはおそらくアプリケーション内の既存のページではありません。
結論として、クラッシュがどのように発生したかについての理論は次のとおりです。
- httpWebRequest は、Crash3 Web ページの
LogTheRequest()メソッド内の既存の URL に対して行われます。 - 実際のアプリケーションでは、この問題を解決する解決策は、
HttpWebRequestが行われたときにエラーを処理することです。 ただし、この場合、ソリューションははるかに簡単です。既存のページにHttpWebRequest要求を行わないようにします。
この時点で、クラッシュの原因についてさらに質問があるはずです。 たとえば、 Slow リンクを選択した後にクラッシュがトリガーされたのはなぜですか?
自分で調査を続けてください。 次に推奨される手順は、HttpWebRequest オブジェクト アドレスを使用して gcroot コマンドを実行し、ルートの場所を確認することです。 これは、クラッシュがどのように発生したかを把握するのに役立つ場合があります。
これでラボは終了です。 Ctrl + C を押すか、q コマンドを使用して lldb デバッガーを終了します。