[APM笔记]Flutter Android/iOS使用Breakpad

780 阅读4分钟

github.com/Sunbreak/fl…

Android

官方文档:chromium.googlesource.com/breakpad/br…

编译

参考libbreakpad在android上的编译方法

$ cd $BREAKPAD/src/android
$ cp -r google_breakpad jni
$ $NDK/ndk-build
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-16.
# ...
[arm64-v8a] StaticLibrary  : libbreakpad_client.a
# ...
[armeabi-v7a] StaticLibrary  : libbreakpad_client.a
# ...
[x86] StaticLibrary  : libbreakpad_client.a
# ...
[x86_64] StaticLibrary  : libbreakpad_client.a
$ find . -name libbreakpad_client.a
./obj/local/armeabi-v7a/libbreakpad_client.a
./obj/local/x86/libbreakpad_client.a
./obj/local/arm64-v8a/libbreakpad_client.a
./obj/local/x86_64/libbreakpad_client.a

使用

依赖符号表-解析dmp文件

根据如何在Android平台使用Google Breakpad

在Linux环境中,使用Breakpad工程编译出来的dump_syms解析ELF文件中的DWARF信息

$ $CLI_BREAKPAD/.../dump_syms libflutter-breakpad.so > libflutter-breakpad.so.sym

在Linux环境中,使用Breakpad工程编译出来的minidump_stackwalk,配合symbols符号表,解析dmp崩溃日志

$ $CLI_BREAKPAD/.../minidump_stackwalk libflutter-breakpad.so.dmp symbols > libflutter-breakpad.so.log

最终能直接得到产生崩溃的源码所在的文件flutter_breakpad.cpp和行号44

$ head -n 20 libflutter-breakpad.so.log
Operating system: Android
                  0.0.0 Linux 3.18.71-perf-g4a0ef96 #1 SMP PREEMPT Thu Nov 22 00:40:45 CST 2018 aarch64
CPU: arm64
     4 CPUs

GPU: UNKNOWN

Crash reason:  SIGSEGV /SEGV_MAPERR
Crash address: 0x0
Process uptime: not available

Thread 0 (crashed)
 0  libflutter-breakpad.so!Java_com_example_flutter_1breakpad_Utils_crash [flutter_breakpad.cpp : 44 + 0x0]
     x0 = 0x0000007618a792a0    x1 = 0x0000007fc3f6f4f4
     x2 = 0x0000000000000000    x3 = 0x0000007618a0dc00
     x4 = 0x0000007fc3f6f910    x5 = 0x00000075ff39203b
     x6 = 0x0000000000000002    x7 = 0x0000000000000000
     x8 = 0x0000000000000000    x9 = 0x0000000000000001
    x10 = 0x0000000000430000   x11 = 0x0000000000000000
    x12 = 0x0000000000000001   x13 = 0x0000000000000000

依赖DWARF信息-解析dmp文件

根据breakpad的正确编译和常规用法

使用Android Studio内置minidump_stackwalk(不同Android Studio版本具体路径不同,请自行搜索),直接解析dmp崩溃日志

平台路径
Windows%ANDRIOD_STUDIO%\bin\lldb\minidump_stackwalk.exe
macOS$ANDRIOD_STUDIO/Contents/bin/lldb/bin/minidump_stackwalk
Linux$ANDRIOD_STUDIO/bin/lldb/minidump_stackwalk
$ $ANDRIOD_STUDIO/.../minidump_stackwalk libflutter-breakpad.so.dmp > libflutter-breakpad.so.log

可以看到崩溃代码偏移地址0x15fe0

$ head -n 20 libflutter-breakpad.so.log
Operating system: Android
                  0.0.0 Linux 3.18.71-perf-g4a0ef96 #1 SMP PREEMPT Thu Nov 22 00:40:45 CST 2018 aarch64
CPU: arm64
     4 CPUs

Crash reason:  SIGSEGV
Crash address: 0x0
Process uptime: not available

Thread 0 (crashed)
 0  libflutter-breakpad.so + 0x15fe0
     x0 = 0x0000007618a792a0    x1 = 0x0000007fc3f6f4f4
     x2 = 0x0000000000000000    x3 = 0x0000007618a0dc00
     x4 = 0x0000007fc3f6f910    x5 = 0x00000075ff48f03b
     x6 = 0x0000000000000002    x7 = 0x0000000000000000
     x8 = 0x0000000000000000    x9 = 0x0000000000000001
    x10 = 0x0000000000430000   x11 = 0x0000000000000000
    x12 = 0x0000000000000001   x13 = 0x0000000000000000
    x14 = 0x00000000ffffffff   x15 = 0x00000000000fd090
    x16 = 0x000000761ba5c2c8   x17 = 0x00000075ff02afc4

使用Android NDK内置的addr2line,借助ELF文件,解析崩溃地址对应的源代码的行号

$ANDROID_SDK/ndk/21.1.6352462/toolchains/llvm/prebuilt/${PLATFORM_ARCH}/bin/${ANDROID_ARCH}-addr2line

平台\Androidarmeabi-v7aarm64-v8ax86x86-64
windows/x86-64windows-x86_64/bin/arm-linux-androideabi.exewindows-x86_64/bin/aarch64-linux-android.exewindows-x86_64/bin/i686-linux-android.exewindows-x86_64/bin/x86_64-linux-android.exe
mac/x86-64darwin-x86_64/bin/arm-linux-androideabidarwin-x86_64/bin/aarch64-linux-androiddarwin-x86_64/bin/i686-linux-androiddarwin-x86_64/bin/x86_64-linux-android
linux/x86-64linux-x86_64/bin/arm-linux-androideabilinux-x86_64/bin/aarch64-linux-androidlinux-x86_64/bin/i686-linux-androidlinux-x86_64/bin/x86_64-linux-android
$ $ANDROID_SDK/.../addr2line -f -C -e libflutter-breakpad.so 0x15fe0
/Users/sunbreak/Sunbreak/flutter-breakpad.trial/android/app/src/main/cpp/flutter_breakpad.cpp:44

最终能直接得到产生崩溃的源码所在的文件flutter_breakpad.cpp和行号44

iOS

官方文档:无

编译

根据Breakpad在mac/ios上的跨平台的调用方式

$ cd $BREAKPAD/src/client/ios
$ xcodebuild -sdk iphoneos -arch arm64 && xcodebuild -sdk iphonesimulator -arch x86_64
$ lipo -create build/Release-iphoneos/libBreakpad.a build/Release-iphonesimulator/libBreakpad.a -output libBreakpad.a

使用

根据Google Breakpad IOS

依赖符号表-解析dmp文件

在macOS环境中,使用dsymutil获取Runner的DWARF文件Runner.dSYM

$ dsymutil build/ios/Debug-iphonesimulator/Runner.app/Runner -o Runner.dSYM

在macOS环境中,使用Breakpad工程编译出来的dump_syms解析ELF文件(dSYM)中的DWARF信息

$ $CLI_BREAKPAD/.../dump_syms Runner.dSYM > Runner.sym

在macOS环境中,使用Breakpad工程编译出来的minidump_stackwalk,配合symbols符号表,解析dmp崩溃日志

$ $CLI_BREAKPAD/.../minidump_stackwalk Runner.dmp symbols > Runner.log

最终能直接得到产生崩溃的源码所在的文件util.m和行号5

$ head -n 20 Runner.log
Operating system: iOS
                  11.2.3 20D91
CPU: amd64
     family 6 model 70 stepping 1
     8 CPUs

GPU: UNKNOWN

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

Thread 0 (crashed)
 0  Runner!crash [util.m : 5 + 0x0]
    rax = 0x0000000000000000   rdx = 0x000000010ebe52a0
    rcx = 0x000000010c3e1b90   rbx = 0x0000000000000000
    rsi = 0x000000010c3e1ba0   rdi = 0x0000000000000000
    rbp = 0x00007ffee381d290   rsp = 0x00007ffee381d290
     r8 = 0x0060000000000000    r9 = 0x0000007700000001
    r10 = 0x0000000000000002   r11 = 0x0000000000000246

依赖符号表-手动解析dmp文件

在macOS环境中,使用Breakpad工程编译出来的minidump_stackwalk,直接解析dmp崩溃日志

$ $ANDRIOD_STUDIO/.../minidump_stackwalk Runner.dmp > Runner.log

可以看到崩溃代码偏移地址0x1560

$ head -n 20 Runner.log
Operating system: iOS
                  11.2.3 20D91
CPU: amd64
     family 6 model 70 stepping 1
     8 CPUs

GPU: UNKNOWN

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

Thread 0 (crashed)
 0  Runner + 0x1560
    rax = 0x0000000000000000   rdx = 0x000000010ebe52a0
    rcx = 0x000000010c3e1b90   rbx = 0x0000000000000000
    rsi = 0x000000010c3e1ba0   rdi = 0x0000000000000000
    rbp = 0x00007ffee381d290   rsp = 0x00007ffee381d290
     r8 = 0x0060000000000000    r9 = 0x0000007700000001
    r10 = 0x0000000000000002   r11 = 0x0000000000000246

进而搜索Runner.sym中首列为”1560“的行,即0x1560地址中,偏移量为6,源码行数为5,文件ID为15

$ grep 1560 Runner.sym
1560 6 5 15
# ...

再搜索Runner.sym中前两列为“FILE 15”的行,即源文件为util.m

$ grep "FILE 15" Runner.sym
FILE 15 /Users/sunbreak/Sunbreak/flutter-breakpad.trial/ios/Runner/util.m

依赖dSYM-手动解析dmp文件

获取程序起始地址,即vmaddr 0x0000000100000000

$ otool -l build/ios/Debug-iphonesimulator/Runner.app/Runner | grep -B 1 -A 10 "LC_SEGM" | grep -B 3 -A 8 "__TEXT"
Load command 1
      cmd LC_SEGMENT_64
  cmdsize 1592
  segname __TEXT
   vmaddr 0x0000000100000000
   vmsize 0x0000000000018000
  fileoff 0
 filesize 98304
  maxprot 0x00000005
 initprot 0x00000005
   nsects 19
    flags 0x0

偏移量0x1560对应的地址为0x0000000100000000 + 0x1560 = 0x0000000100001560,通过dwarfdump搜索dSYM文件

$ dwarfdump --lookup 0x0000000100001560 Runner.dSYM
Runner.dSYM/Contents/Resources/DWARF/Runner:	file format Mach-O 64-bit x86-64
0x000975ab: Compile Unit: length = 0x0000006f version = 0x0004 abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x0009761e)

0x000975b6: DW_TAG_compile_unit
              DW_AT_producer	("Apple clang version 12.0.0 (clang-1200.0.32.27)")
              DW_AT_language	(DW_LANG_ObjC)
              DW_AT_name	("/Users/sunbreak/Sunbreak/flutter-breakpad.trial/ios/Runner/util.m")
              DW_AT_LLVM_sysroot	("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.2.sdk")
              DW_AT_APPLE_sdk	("iPhoneSimulator14.2.sdk")
              DW_AT_stmt_list	(0x00017c05)
              DW_AT_comp_dir	("/Users/sunbreak/Sunbreak/flutter-breakpad.trial/ios")
              DW_AT_APPLE_major_runtime_vers	(0x02)
              DW_AT_low_pc	(0x0000000100001550)
              DW_AT_high_pc	(0x0000000100001568)

0x000975ed:   DW_TAG_subprogram
                DW_AT_low_pc	(0x0000000100001550)
                DW_AT_high_pc	(0x0000000100001568)
                DW_AT_frame_base	(DW_OP_reg6 RBP)
                DW_AT_name	("crash")
                DW_AT_decl_file	("/Users/sunbreak/Sunbreak/flutter-breakpad.trial/ios/Runner/util.m")
                DW_AT_decl_line	(3)
                DW_AT_external	(true)
Line info: file 'util.m', line 5, column 8, start line 3

最终能直接得到产生崩溃的源码所在的文件util.m和行号5

参考