Mac クライアント C++ コードでのブレークパッドの使用

この記事では、Mac プラットフォーム上の C++ 実行可能プログラムまたはダイナミック リンク ライブラリでブレークパッドを使用する方法について概要を説明します。

「ブレークパッド入門」で説明したように、ブレークパッド システム全体には、C++ プログラムで接続されたブレークパッド クライアント ライブラリだけでなく、シンボルを生成するツール dump_syms、生成されたミニダンプをシンボルに変換するツール minidump_stackwalk、およびアップロード用ツールも含まれています。ミニダンプファイル。この記事では、これらすべてのバイナリをコンパイルして使用する方法を簡単に説明します。

ブレッドパッドを構築する

WebRTC / OpenRTCClientをベースとしたGN+ninjaビルドシステムの構築方法を説明します。

Git 経由でOpenRTCClientのソース コードをダウンロードします。次に、 OpenRTCClient のソース コード ルート ディレクトリで次のコマンドを実行します./build_system/webrtc_pack mac debug crash_catch_system。これにより、クライアント静的ライブラリ ファイルbuild/mac/debug/libbreakpad_client.aが生成されます。これには、2 つの CPU アーキテクチャ arm64 および x64 の静的ライブラリが含まれています。また、シンボルも生成されます。生成されたミニダンプをシンボルに変換するファイルとツールはbuild/mac/x64/debugにあります

ブレークパッドをアプリケーションに統合する

まず、リンク ライブラリ ファイルの検索パスにlibbreakpad_client.aのディレクトリ アドレスを追加するようにビルド プロセスを構成し、リンクにBreakpad_clientライブラリを追加し、バイナリ ファイルにlibbreakpad_client.aをリンクしてから、インクルード パスを include に設定します。ブレークパッドのソース ツリー srcディレクトリ

Google ブレークパッド自体が提供するクラス インターフェイスExceptionHandlerは、さまざまなプラットフォームで大きな違いはありませんが、まったく同じではありません。これにより、ブレークパッドへのアクセスに一定の負担がかかります。

OpenRTCClientでは、作成者は単純なカプセル化、つまりopen_rtc::InstallCrashHandler()と を実装しましたopen_rtc::UninstallCrashHandler()このインターフェイスを使用するには、クラッシュ ハンドラー ヘッダー ファイルをインクルードします。

#include "client/crash_handler.h"

open_rtc::InstallCrashHandler()クラッシュ ハンドラーをインストールするには呼び出します。例外処理はを呼び出した後までアクティブであるopen_rtc::UninstallCrashHandler()ため、プログラムの起動プロセスのできるだけ早い段階で例外処理を呼び出しopen_rtc::InstallCrashHandler()、シャットダウンの直前までアクティブにしておく必要があります。何か役に立つことをするには、open_rtc::InstallCrashHandler()ミニダンプを書き込むことができるパスと、書き込まれたミニダンプに関する情報を受け取るコールバック関数が必要です。

#include <stdlib.h>

#include "client/crash_handler.h"

bool minidumpCallback(const char* dump_dir,
                      const char* minidump_id,
                      void* context,
                      bool succeeded) {
  printf("Dump path: %s, minidump_id %s\n", dump_dir, minidump_id);
  return succeeded;
}

static void crashfunc() {
  volatile int* a = (int*)(NULL);
  *a = 1;
}

int main(int argc, const char *argv[]) {
  open_rtc::InstallCrashHandler(".", nullptr, minidumpCallback, nullptr);
  crashfunc();
  return 0;
}

このサンプル プログラムをコンパイルして実行すると、現在のディレクトリにミニダンプ ファイルが生成され、終了する前にディレクトリ パスとミニダンプ ファイルの ID が出力されます。ID はミニダンプ ファイルのファイル名です。実際のミニダンプ ファイルファイル拡張子「.dmp」が含まれます。

Google ブレークパッドが提供するクラスを直接使用してExceptionHandlerブレークパッドにアクセスすることもできます。このとき、ヘッダー ファイルが最初にインクルードされます。

#include "client/mac/handler/exception_handler.h"

次にExceptionHandlerクラスオブジェクトを作成します。ExceptionHandler例外処理はオブジェクトのライフサイクル全体を通じてアクティブであるため、プログラムの起動時にできるだけ早くインスタンス化され、シャットダウン状態にできるだけ近い状態でアクティブに維持される必要があります。何か役に立つことを行うには、ExceptionHandlerコンストラクターにはミニダンプを書き込むことができるパスと、書き込まれたミニダンプに関する情報を受け取るコールバック関数が必要です。

#include <stdio.h>
#include "client/mac/handler/exception_handler.h"

bool MinidumpCallback(const char* dump_dir,
                      const char* minidump_id,
                      void* context,
                      bool succeeded) {
  printf("Dump path: %s, minidump_id %s\n", dump_dir, minidump_id);
  return succeeded;
}

int main(int argc, const char *argv[]) {
  google_breakpad::ExceptionHandler eh(".", NULL, MinidumpCallback, NULL,
                                       true, nullptr);
  crashfunc();
}

このプログラムをコンパイルして実行すると、前のプログラムと同じ結果が得られます。

WebRTC のビルド構成により、USE_PROTECTED_ALLOCATIONS=1マクロ定義が有効になっています ( webrtc/third_party/breakpad/BUILD.gn)。ただし、ブレークパッド ( ) には次のようなコードがありますwebrtc/third_party/breakpad/breakpad/src/client/mac/handler/exception_handler.cc

bool ExceptionHandler::InstallHandler() {
  // If a handler is already installed, something is really wrong.
  if (gProtectedData.handler != NULL) {
    return false;
  }
  if (!IsOutOfProcess()) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGABRT);
    sa.sa_sigaction = ExceptionHandler::SignalHandler;
    sa.sa_flags = SA_SIGINFO;

    scoped_ptr<struct sigaction> old(new struct sigaction);
    if (sigaction(SIGABRT, &sa, old.get()) == -1) {
      return false;
    }
    old_handler_.swap(old);
    gProtectedData.handler = this;
#if USE_PROTECTED_ALLOCATIONS
    assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
    mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
#endif
  }

  try {
#if USE_PROTECTED_ALLOCATIONS
    previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
      ExceptionParameters();
#else
    previous_ = new ExceptionParameters();
#endif
  }
  catch (std::bad_alloc) {
    return false;
  } 

このコードは、ExceptionHandlerオブジェクトの作成時に実行されます。グローバル変数はgBreakpadAllocator実際にはwebrtc/third_party/breakpad/breakpad/src/client/mac/Framework/Breakpad.mmで初期化され、このコードはBreakpadCreate()OC コードでブレークパッド クライアントを接続することによってのみ実行されます。このように、ブレークパッド クライアントがブレークパッドの C++ インターフェイスを介して直接アクセスされると、gBreakpadAllocatorグローバル変数の値は空になり、ExceptionHandlerオブジェクトの作成時にヌル ポインターが原因でプログラムがクラッシュします。

OpenRTCClientプロジェクトではUSE_PROTECTED_ALLOCATIONS=1C++ インターフェイスを介してブレークパッド クライアントにアクセスすることによって発生する問題は、マクロの定義を削除することで解決されます。もちろん、gBreakpadAllocatorの値をチェックすることでこの問題も解決できます。

プログラムのシンボルを生成する

上で説明したように、OpenRTCClientを通じてブレークパッドを構築すると、さまざまなツールが一緒に生成されます。ここでは、このプロセスを説明するためにOpenRTCClientのsmoke_testを例に挙げます。

ビルドsmoke_test:

OpenRTCClient %  ./build_system/webrtc_build build:smoke_test mac x64 debug

シンボル ファイルを生成します。

OpenRTCClient %  ./build/mac/x64/debug/dump_syms -g build/mac/x64/debug/smoke_test.dSYM build/mac/x64/debug/smoke_test > build/mac/x64/debug/smoke_test.sym

OpenRTCClient のベースとなっているwebrtc/build/toolchain/apple/linker_driver.pyにはいくつかの問題があるようです

warning: (x86_64)  could not find object file symbol for symbol __ZNSt3__17forwardIPN6webrtc20AudioEncoderG722ImplEEEOT_RNS_16remove_referenceIS4_E4typeE
warning: (x86_64)  could not find object file symbol for symbol __ZNSt3__122__compressed_pair_elemIPN6webrtc12AudioEncoderELi0ELb0EEC2IPNS1_20AudioEncoderG722ImplEvEEOT_
warning: (x86_64)  could not find object file symbol for symbol __ZNSt3__122__compressed_pair_elemINS_14default_deleteIN6webrtc12AudioEncoderEEELi1ELb1EEC2INS1_INS2_20AudioEncoderG722ImplEEEvEEOT_
warning: (x86_64)  could not find object file symbol for symbol __ZNSt3__114default_deleteIN6webrtc12AudioEncoderEEC2INS1_20AudioEncoderG722ImplEEERKNS0_IT_EEPNS_9enable_ifIXsr14is_convertibleIPS6_PS2_EE5valueEvE4typeE
warning: (x86_64)  could not find object file symbol for symbol __ZN6webrtc16AudioEncoderOpus11SdpToConfigERKNS_14SdpAudioFormatE
warning: (x86_64)  could not find object file symbol for symbol __ZN6webrtc16AudioEncoderOpus16MakeAudioEncoderERKNS_22AudioEncoderOpusConfigEiN4absl8optionalINS_16AudioCodecPairIdEEE
warning: (x86_64)  could not find object file symbol for symbol __ZN6webrtc16AudioEncoderOpus17QueryAudioEncoderERKNS_22AudioEncoderOpusConfigE
warning: (x86_64)  could not find object file symbol for symbol __ZN6webrtc16AudioEncoderOpus23AppendSupportedEncodersEPNSt3__16vectorINS_14AudioCodecSpecENS1_9allocatorIS3_EEEE
. . . . . .
warning: (x86_64)  could not find object file symbol for symbol _dav1d_itx_dsp_init_x86_8bpc
warning: (x86_64)  could not find object file symbol for symbol _iclip_u8
warning: (x86_64)  could not find object file symbol for symbol _dav1d_loop_filter_dsp_init_8bpc
warning: (x86_64)  could not find object file symbol for symbol _dav1d_loop_filter_dsp_init_x86_8bpc
warning: (x86_64)  could not find object file symbol for symbol _iclip_u8
warning: (x86_64)  could not find object file symbol for symbol _dav1d_loop_restoration_dsp_init_8bpc
warning: (x86_64)  could not find object file symbol for symbol _dav1d_loop_restoration_dsp_init_x86_8bpc
warning: (x86_64)  could not find object file symbol for symbol _iclip_u8
warning: (x86_64)  could not find object file symbol for symbol _dav1d_mc_dsp_init_8bpc
warning: (x86_64)  could not find object file symbol for symbol _dav1d_mc_dsp_init_x86_8bpc
warning: (x86_64)  could not find object file symbol for symbol _dav1d_get_cpu_flags_x86
 build success 

この問題は、Chromium メーリング リスト、macOS ビルド警告でも議論されています。この問題により、シンボル ファイルの生成が失敗するようです。

smoke_test.dSYM/Contents/Resources/DWARF/smoke_test: the DIE at offset 0x134c917 has a DW_AT_specification attribute referring to the DIE at offset 0x134c8c6, which was not marked as a declaration
smoke_test.dSYM/Contents/Resources/DWARF/smoke_test: the DIE at offset 0x134ca01 has a DW_AT_specification attribute referring to the DIE at offset 0x134c9b1, which was not marked as a declaration
smoke_test.dSYM/Contents/Resources/DWARF/smoke_test: the DIE at offset 0x134caeb has a DW_AT_specification attribute referring to the DIE at offset 0x134ca9b, which was not marked as a declaration
smoke_test.dSYM/Contents/Resources/DWARF/smoke_test: the DIE at offset 0x134cc0b has a DW_AT_specification attribute referring to the DIE at offset 0x134cbc9, which was not marked as a declaration
Assertion failed: (it_debug == externs_.end() || (*it_debug)->address >= range.address + range.size), function AddFunction, file module.cc, line 175.

生成されたバイナリをリンクすると、build/mac/x64/debug/dump_symsクラッシュや実行エラーが発生します。

OpenRTCClientプロジェクトではwebrtc/build/toolchain/apple/linker_driver.pyM88 の に置き換えることでこの問題を解決していますwebrtc/build/toolchain/mac/linker_driver.pyこれにより、シンボル ファイルが正常に生成されます。

ツールを通じてこれらのシンボル ファイルを使用するにはminidump_stackwalk、シンボル ファイルを特定のディレクトリ構造に配置する必要があります。シンボル ファイルの最初の行には、このディレクトリ構造を生成するために必要な次のような情報が含まれています (出力は異なる場合があります)。

OpenRTCClient % ./build/mac/x64/debug/dump_syms -g build/mac/x64/debug/smoke_test.dSYM build/mac/x64/debug/smoke_test > build/mac/x64/debug/smoke_test.sym
OpenRTCClient % head build/mac/x64/debug/smoke_test.sym                                                                                                    
MODULE mac x86_64 4C4C44D755553144A13B271E21EDCA370 smoke_test
FILE 0 /Users/henryhan/Projects/opensource/OpenRTCClient/build/mac/x64/debug/../../../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFBase.h
FILE 1 /Users/henryhan/Projects/opensource/OpenRTCClient/build/mac/x64/debug/../../../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFByteOrder.h
FILE 2 /Users/henryhan/Projects/opensource/OpenRTCClient/build/mac/x64/debug/../../../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/usr/include/_ctype.h
FILE 3 /Users/henryhan/Projects/opensource/OpenRTCClient/build/mac/x64/debug/../../../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/usr/include/_wctype.h
FILE 4 /Users/henryhan/Projects/opensource/OpenRTCClient/build/mac/x64/debug/../../../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/usr/include/architecture/byte_order.h
FILE 5 /Users/henryhan/Projects/opensource/OpenRTCClient/build/mac/x64/debug/../../../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/usr/include/dispatch/once.h
FILE 6 /Users/henryhan/Projects/opensource/OpenRTCClient/build/mac/x64/debug/../../../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/usr/include/dispatch/queue.h
FILE 7 /Users/henryhan/Projects/opensource/OpenRTCClient/build/mac/x64/debug/../../../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/usr/include/libkern/i386/_OSByteOrder.h
FILE 8 /Users/henryhan/Projects/opensource/OpenRTCClient/build/mac/x64/debug/../../../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/usr/include/math.h
OpenRTCClient % mkdir -p build/mac/x64/debug/symbols/smoke_test/4C4C44D755553144A13B271E21EDCA370
OpenRTCClient % mv build/mac/x64/debug/smoke_test.sym build/mac/x64/debug/symbols/smoke_test/4C4C44D755553144A13B271E21EDCA370

ミニダンプを処理してスタック トレースを生成する

ここではまずクラッシュをシミュレートします。OpenRTCClientプロジェクトのスモークテストには、クラッシュを引き起こす可能性のあるテスト ケースがいくつかあります。

OpenRTCClient % ./build/mac/x64/debug/smoke_test '--gtest_filter=CrashCatchTest.*' --gtest_also_run_disabled_tests
Note: Google Test filter = CrashCatchTest.*
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from CrashCatchTest
[ RUN      ] CrashCatchTest.DISABLED_crash_catch_common
Dump path: ~/OpenRTCClient/build/mac/x64/debug, minidump_id A25EA989-A9EC-400F-B44F-AF4B517101A1

クラッシュが発生すると、キャプチャされたクラッシュ ミニダンプ ファイルに関連するいくつかの情報が吐き出されます。この情報に基づいて、クラッシュ ファイルのフル パスが であることがわかります~/OpenRTCClient/build/mac/x64/debug/A25EA989-A9EC-400F-B44F-AF4B517101A1.dmp

Breakpad にはminidump_stackwalk、ミニダンプとそれに対応するテキスト形式のシンボルを取得し、シンボリック スタック トレースを生成する と呼ばれるツールが含まれています。上記の手順に従って Breakpad のソース コードをコンパイルした場合、そのソース コードはOpenRTCClient/build/mac/x64/debugディレクトリに配置されているはずです。ミニダンプとシンボリック パスをコマンド ライン引数として渡すだけです。

OpenRTCClient % build/mac/x64/debug/minidump_stackwalk ./build/mac/x64/debug/A25EA989-A9EC-400F-B44F-AF4B517101A1.dmp ./build/mac/x64/debug/symbols 
Operating system: Mac OS X
                  11.6.7 20G630
CPU: amd64
     family 6 model 158 stepping 10
     12 CPUs

GPU: UNKNOWN

Crash reason:  EXC_BAD_ACCESS / KERN_INVALID_ADDRESS
Crash address: 0x0
Process uptime: 0 seconds

Thread 0 (crashed)
 0  smoke_test!crashfunc() [crash_catch_test.cpp : 48 + 0x0]
    rax = 0x0000000000000000   rdx = 0x00000000000fb340
    rcx = 0x0000000000000000   rbx = 0x0000000000000000
    rsi = 0x00000000d587bb72   rdi = 0x0000000107d11080
    rbp = 0x00007ffeeebff1c0   rsp = 0x00007ffeeebff1c0
     r8 = 0x00000000000000d7    r9 = 0x0000000000000008
    r10 = 0x00007ff972a00000   r11 = 0x0000000000000000
    r12 = 0x0000000000000000   r13 = 0x0000000000000000
    r14 = 0x0000000000000000   r15 = 0x0000000000000000
    rip = 0x000000010101744e
    Found by: given as instruction pointer in context
 1  smoke_test!CrashCatchTest_DISABLED_crash_catch_common_Test::TestBody() [crash_catch_test.cpp : 53 + 0x5]
    rbp = 0x00007ffeeebff200   rsp = 0x00007ffeeebff1d0
    rip = 0x00000001010173ff
    Found by: previous frame's frame pointer
 2  smoke_test!void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) [gtest.cc : 2631 + 0x2]
    rbp = 0x00007ffeeebff260   rsp = 0x00007ffeeebff210
    rip = 0x0000000101095e4b
    Found by: previous frame's frame pointer
 3  smoke_test!void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) [gtest.cc : 2686 + 0x15]
    rbp = 0x00007ffeeebff2d0   rsp = 0x00007ffeeebff270
    rip = 0x00000001010719c7
    Found by: previous frame's frame pointer
 4  smoke_test!testing::Test::Run() [gtest.cc : 2706 + 0x24]
    rbp = 0x00007ffeeebff330   rsp = 0x00007ffeeebff2e0
    rip = 0x0000000101071911
    Found by: previous frame's frame pointer
 5  smoke_test!testing::TestInfo::Run() [gtest.cc : 2885 + 0x5]
    rbp = 0x00007ffeeebff3b0   rsp = 0x00007ffeeebff340
    rip = 0x0000000101072455
    Found by: previous frame's frame pointer
 6  smoke_test!testing::TestSuite::Run() [gtest.cc : 3044 + 0x5]
    rbp = 0x00007ffeeebff430   rsp = 0x00007ffeeebff3c0
    rip = 0x00000001010732fb
    Found by: previous frame's frame pointer
 7  smoke_test!testing::internal::UnitTestImpl::RunAllTests() [gtest.cc : 5915 + 0x5]
    rbp = 0x00007ffeeebff510   rsp = 0x00007ffeeebff440
    rip = 0x000000010107f763
    Found by: previous frame's frame pointer
 8  smoke_test!bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) [gtest.cc : 2631 + 0x2]
    rbp = 0x00007ffeeebff570   rsp = 0x00007ffeeebff520
    rip = 0x000000010109bccb
    Found by: previous frame's frame pointer
 9  smoke_test!bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) [gtest.cc : 2686 + 0x15]
    rbp = 0x00007ffeeebff5e0   rsp = 0x00007ffeeebff580
    rip = 0x000000010107f277
    Found by: previous frame's frame pointer
10  smoke_test!testing::UnitTest::Run() [gtest.cc : 5482 + 0x27]
    rbp = 0x00007ffeeebff650   rsp = 0x00007ffeeebff5f0
    rip = 0x000000010107f15f
    Found by: previous frame's frame pointer
11  smoke_test!RUN_ALL_TESTS() [gtest.h : 2497 + 0x5]
    rbp = 0x00007ffeeebff660   rsp = 0x00007ffeeebff660
    rip = 0x000000010104ce41
    Found by: previous frame's frame pointer
12  smoke_test!main [main.cpp : 12 + 0x5]
    rbp = 0x00007ffeeebff690   rsp = 0x00007ffeeebff670
    rip = 0x000000010104cdf2
    Found by: previous frame's frame pointer
13  libdyld.dylib + 0x15f3d
    rbp = 0x00007ffeeebff6a0   rsp = 0x00007ffeeebff6a0
    rip = 0x00007fff2036bf3d
    Found by: previous frame's frame pointer

Loaded modules:
0x101000000 - 0x104299fff  smoke_test  ???  (main)
0x7fff20088000 - 0x7fff20089fff  libsystem_blocks.dylib  ???
0x7fff2008a000 - 0x7fff200bffff  libxpc.dylib  ???
0x7fff200c0000 - 0x7fff200d7fff  libsystem_trace.dylib  ???
0x7fff200d8000 - 0x7fff20175fff  libcorecrypto.dylib  ???
0x7fff20176000 - 0x7fff201a2fff  libsystem_malloc.dylib  ???
0x7fff201a3000 - 0x7fff201e7fff  libdispatch.dylib  ???
0x7fff201e8000 - 0x7fff20221fff  libobjc.A.dylib  ???

stderr に詳細な出力が生成され、stdout にスタック トレースが生成されるため、stderr をリダイレクトする必要がある場合があります。

参考資料

Mac で Breakpad のダンプ ファイルを使用したデバッグ

Linux プログラムでのブレークパッドの使用

ブレークパッドの正しいコンパイルと一般的な使用法

mac/ios での Breakpad のクロスプラットフォーム呼び出し方法

[APM ノート] ブレークパッドを使用した CLI Windows/macOS/Linux

おすすめ

転載: blog.csdn.net/tq08g2z/article/details/125601826