Bugsnag は最近、ミニダンプ処理のサポートを追加しました。これにより、顧客はElectronのネイティブ クラッシュ、またはBreakpadまたはCrashpad の使用時に生成されたクラッシュを追跡できます。
ミニダンプ サポートの追加には、通常のエラー イベント処理のスループットに影響を与えることなく効率的でスケーラブルな方法でファイルを処理できるようにするために対処しなければならない多くの技術的な課題がありました。
ミニダンプは、一部のプロセスがクラッシュしたときに生成されるファイルです。これらはコア ダンプよりも小さく、基本的なデバッグ操作を実行するために必要なデータを提供するだけです。
ミニダンプ ファイル形式は、OS で予期しないエラーが発生したときに Windows で使用するために考案されました。その後、Google の Breakpad や Crashpad などのツールは、複数のプラットフォームでアプリケーションのクラッシュ ダンプを生成するために同じ形式を採用しました。 Electron で構築されたアプリは、ネイティブ クラッシュのミニダンプ ファイルも生成します (Electron は Crashpad を使用するため)。
ミニダンプ ファイルには、クラッシュ時のプロセスに関する情報が含まれており、次のような詳細が含まれています。
ミニダンプ内には、アクティブな各スレッド (エラーが発生した時点) のランタイム スタックと、それらのスレッドのレジスタ値があります。
これらの詳細は、スタックをウォークし、各スレッドのスタック トレースを生成するために使用できます。場合によっては、このメソッドのみを使用して有効な (シンボル化されていない) スタック トレースを取得できますが、デバッグ ファイル内で提供される追加の呼び出しフレーム情報 (CFI) が必要になる場合もあります。
Call Frame Information (CFI) レコードには、特定のマシン アドレスでレジスタ値を復元する方法が記述されています。このデータは通常、デバッグ ファイルで提供され、スタックをウォークするときに、より信頼性の高いスタック トレースを生成できます。
CFI 情報がない場合、スタック ウォーカーはスタックをスキャンして呼び出しフレームを見つけようとする必要がありますが、これは信頼性が低く、無効なスタック トレースが生成される可能性があります。
スタック トレースが取得されると、各フレームのアドレスだけが含まれます。意味のあるスタック トレース (各フレームの関数名、ソース ファイル、およびソース行番号) を生成するには、それを「シンボル化」する必要があります。つまり、デバッグ シンボルをそれらに適用します。
これには、コンパイラからの関連するデバッグ ファイル (macOS の dSYM など) を使用できますが、Breakpad は代わりに使用できる独自のシンボル ファイル形式を定義しています。
ブレークパッド シンボル ファイルは、ほとんどのコンパイラで生成されたデバッグ ファイルよりも単純ですが (抽象構文ツリーなどの詳細は含まれません)、スタック トレースをシンボル化するために必要な詳細を読み取り可能な形式で提供します。
Breakpad には、ミニダンプを手動で処理したり、処理のために専用のサーバー/サービスにアップロードしたりするのに役立つ多くのツールが用意されています。
minidump_stackwalkミニダンプ ファイルとオプションの Breakpad シンボル ファイルを取り込み、各スレッドのスタック トレースを他の情報 (クラッシュの理由、各フレームのレジスタ値、オペレーティング システムの詳細など) と共に出力します。これは、ミニダンプを解析し、それらから意味のあるスタック トレースを取得するための便利なツールです。
minidump_dump ミニダンプに関する詳細情報 (ミニダンプ内の各ストリームの詳細など) を提供します。
minidump_uploadミニダンプ ファイルを処理専用サーバー (Bugsnag など) にアップロードします。
dump_symsアプリケーションのバイナリ ファイルとデバッグ ファイルからブレークパッド シンボル ファイルを生成します。
symupload Breakpadシンボル ファイルを処理専用サーバー (Bugsnag など) にアップロードします。
関連するシンボル ファイルを含む minidump_stackwalk ツールの出力の例を以下に示します。
-- CODE language-plaintext --オペレーティング システム: Windows NT 10.0.19041 1151CPU: amd64 ファミリ 23 モデル 24 ステッピング 1 8 CPU
GPU: 不明
クラッシュの理由: 未処理の C++ 例外クラッシュ アドレス: 0x7ff887f54ed9プロセスの稼働時間: 4 秒
スレッド 0 (クラッシュ) 0 KERNELBASE.dll + 0x34ed9
rax = 0x655c67616e736775 rdx = 0x6f6d2d6576697461 rcx = 0x6e2d7070635c656d rbx = 0x00007ff87f731600 rsi = 0x000000b80f3fcb70 rdi = 0x000000b80f3fca40 rbp = 0x000000b80f3fca10 rsp = 0x000000b80f3fc8d0 r8 = 0xaaaaaaaa0065646f r9 = 0xaaaaaaaaaaaaaaaa r10 = 0xaaaaaaaaaaaaaaaa r11 = 0xaaaaaaaaaaaaaaaa r12 = 0x00007ff87f6f1ba0 r13 = 0x000010ff084af60d r14 = 0xffffffff00000000 r15 = 0x0000000000000420 rip = 0x00007ff887f54ed9 Found by: given as instruction pointer in context 1 KERNELBASE.dll + 0x34ed9 rbp = 0x000000b80f3fca10 rsp = 0x000000b80f3fc908 rip = 0x00007ff887f54ed9 Found by: stack scanning 2 ntdll.dll + 0x34a5f rbp = 0x000000b80f3fca10 rsp = 0x000000b80f3fc960 rip = 0x00007ff88a6a4a5f Found by: stack scanning 3 my_example.node!CxxThrowException [throw.cpp : 131 + 0x14] rbp = 0x000000b80f3fca10 rsp = 0x000000b80f3fc9b0 rip = 0x00007ff87f6fab75 発見者: スタックスキャン 4 my_example.node!RunExample(Nan::FunctionCallbackI nfo v8::Value const &) [my_example.cpp : 26 + 0x22] rbp = 0x000000b80f3fca10 rsp = 0x000000b80f3fca20 rip = 0x00007ff87f6f1ec2 発見者: call frame info.. .
Bugsnag にミニダンプのサポートを追加する際に、いくつかの技術的な課題に直面しました。
ミニダンプは、通常のエラー イベントの JSON ペイロードとは大きく異なるため、効率的で回復力があり、スケーラブルな方法でミニダンプを処理できるようにする必要がありました。
ミニダンプ ファイルは、受け取る通常のペイロードよりもはるかに大きくなる可能性があります。通常のペイロードは平均 20KB 程度ですが、ミニダンプのサイズは通常数百キロバイトです。さらに、多数のアクティブなスレッドまたは大きなスタックを持つスレッドがある場合、ミニダンプは非常に大きくなる可能性があります (数十メガバイト)。
通常、エラー イベント ペイロードを受信すると、非同期処理のためにそれらを Kafka キューに追加して、アップロードでバックログを処理できるようにします。
大きなミニダンプ ファイルをキューに入れる場合、キュー メカニズムが信頼できるものであることを確認する必要がありました。ミニダンプ ファイルは適切に圧縮されますが (通常、元のサイズの約 10% まで)、圧縮されたファイルが大きすぎるというリスクが依然としてありました。
内部 Kafka インスタンスでさまざまなサイズのメッセージを使用して負荷テストを行ったところ、次のことがわかりました。
データ スループットとレプリケーション ラグは、ファイルのサイズの影響を実際には受けませんでした。
平均ファイル サイズが大きくなるにつれて、1 秒あたりに処理できるメッセージの数が減少しました。
このメッセージ スループットの低下は、多数の特に大きなファイルを同時にキューに入れている場合にのみ顕著でしたが、これらは非常にまれであると予想されるため、Kafka は目的に適しています。
Breakpad の minidump_stackwalk ツールを使用してミニダンプをシンボル化する場合、Breakpad シンボル ファイルが提供されると、ミニダンプの処理に非常に長い時間がかかることがあります (シンボル ファイルの読み込み、解析、および関連するシンボル データの検索にかかる時間のため)。
サンプルの Electron ミニダンプでは、Breakpad シンボル ファイルがない場合は 20 ミリ秒、それらがある場合は 14 秒かかりました!
1 つのミニダンプを手動でシンボル化する場合、処理時間が遅くなってもそれほど問題にはなりませんが、ミニダンプ処理の高いスループットを維持できるように、ミニダンプをできるだけ効率的に処理およびシンボル化できるようにする必要があります。
これを実現するために、独自のシンボリック ロジックを実装しました。 Breakpad シンボル ファイル形式はシンプルで、十分に文書化されているため、ファイルを解析して、アドレスを簡単に検索できる特注のマッピング ファイルを生成できます。
特注のファイルは、Breakpad シンボル ファイルよりもはるかに大きくなりますが、ルックアップの実行効率も大幅に向上します。このブレークパッド シンボル ファイルの前処理を前もって行うと、ミニダンプの処理にかかる時間が大幅に短縮されます (ただし、シンボル ファイルのストレージ要件が増加します)。
ミニダンプ処理の初期設計では、(効率を向上させるために) スタックをウォークするときに Breakpad シンボル ファイルの使用を完全に省略しましたが、すぐに、Call Frame Information データの欠落が原因で無効なスタック トレースが発生することがあることに気付きました。
スタック ウォーキング用に完全な Breakpad シンボル ファイルを渡すと遅くなることがわかっていたので (スタック トレースもシンボル化しようとするため)、代わりに、スタックを歩くために必要な情報。
これにより、Breakpad シンボル ファイルのサイズとミニダンプの処理にかかる時間が大幅に短縮されましたが、それでもあまり効率的ではありませんでした (Electron ミニダンプの例では 1.5 秒かかりました)。
そのため、トリミングされたブレークパッド シンボル ファイルをシリアル化して、(毎回ファイルを解析するのではなく) より効率的に読み取れるようにするオプションを検討しました。ファイルのシリアル化されたバージョンを使用すると、処理時間が 1.5 秒から 200 ミリ秒に短縮されました。
このパフォーマンスの向上は、サービスのインスタンスごとにより高いスループットのミニダンプをサポートできるようになり、インフラストラクチャのコストを抑えることができることを意味します。
新機能の採用が拡大するにつれて、インフラストラクチャの使用状況を綿密に監視して、効率的な方法でミニダンプを処理し続け、他にパフォーマンスの改善が必要かどうかを確認します。
Bugsnag は、アプリケーションの安定性を向上させながら、ソフトウェアのバグに優先順位を付けて修正するのに役立ちます。