Android在Windows下无需编译Breakpad源码就能使用

1. 为什么要使用Google Breakpad?

  我们在开发过程中,Android JNI层Crash问题或者我们引用的第三方.so库文件报错,都是一个比较头疼的问题。相对Java层来说,由于c/c++造成的crash没有输出如同Java的Exception Strace堆栈信息,所以定位问题也是个比较艰难的事情。
  Google Breakpad是一套完整的工具集,从Crash的捕获到Crash的dump,都提供了相对应的工具。它记录了崩溃时的.dump文件,无论我们是在本地或者发送到服务器端,都可以用相对应的工具来解析.dump文件帮助我们查找C和C++堆栈踪迹。
  但是,由于Google Breakpad是C/C++编写的,很多Android开发同学并没有这方面的经验,想用而却不能用。而且,Google Breakpad在Github上面的说明,估计很多人看了就懵逼了:

image.png

  其实,它的意思是需要去编译Google Breakpad源码去生成适合不同平台下的minidump_stackwalk文件,比如Windows、Mac、Linux等等。当然我也是踩了坑的,参考这个说明,多次尝试后在Windows下确实没有成功过;后来,利用VMware Workstation Pro中安装Ubuntu 18.10下成功编出来了。
  另外,我在踩坑过程中,查了各种资料,基本都是上面图中的翻版,没啥用。所以,当我无意中全局搜索发现Android自带有minidump_stackwalk执行文件,便怀着好奇心试了试,所以才有今天这篇文章。

2. 如何在Windows下无需编译Google Breakpad源码就可使用?

1. 利用模拟器测试

  第一步:下载Google Breakpad源码
  第二步:虽然代码中很多地方引用了#include "third_party/lss/linux_syscall_support.h"这个Linux调用库,但源码中并没有给出lss目录,所以我们需要自己翻墙去下载https://chromium.googlesource.com/linux-syscall-support/源码,并将lss目录拷贝到third_party下,否则编译不过。

image.png

  第三步:利用Android Studio新建一个项目,具体参考我的实例项目。你只需要将Breakpad的源码src目录替换到我的项目中去就行(该项目中是采用的CMakeLists.txt来配置的,而Google Breakpad中的README.ANDROID给出的是Android.mk配置,这个大家自行取舍用哪种!
  第四步:利用模拟器运行启动项目会是下面的界面,点击按钮应用闪退。
image.png

  第五步:模拟器的文件管理器中会生成一个crashDump目录,里面有一个.dump文件。
image.png

  第六步:有了.dump文件,我们就需要对其进行解析,工具的名称是minidump_stackwalk。其实,Android Studio的安装目录下bin\lldb\bin就存在这么一个执行文件(我用的是Windows 10)。好,那么来试试(将模拟器目录下的.dump文件拷贝到当前目录并重新命名了):
image.png

  成功解析文件。大家看下

Operating system: Android
                  0.0.0 Linux 3.10.0+ #256 SMP PREEMPT Fri May 19 11:58:12 PDT 2017 i686
CPU: x86
     GenuineIntel family 6 model 31 stepping 1
     4 CPUs

GPU: UNKNOWN

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

Thread 0 (crashed)
 0  libcrash-lib.so + 0x544
    eip = 0x9e810544   esp = 0xbfe32c08   ebp = 0xbfe32c18   ebx = 0x9e811fe4
    esi = 0xa447baa9   edi = 0xbfe32e08   eax = 0x00000000   ecx = 0x43e40746
    edx = 0xbfe32c30   efl = 0x00010286
    Found by: given as instruction pointer in context
 1  libcrash-lib.so + 0x52c
    eip = 0x9e81052c   esp = 0xbfe32c20   ebp = 0xbfe32c28
    Found by: previous frame's frame pointer
... // 后面内容直接省略掉,因为libcrash-lib.so相关的信息已经都展示出来了

  好,从.dump文件解析出来的信息中,根据文章Android 平台 Native 代码的崩溃捕获机制及实现的介绍,我们可知Crash reason: SIGSEGV代表哪种类型的错误:

SIGSEGV 是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号。
Thread 0 (crashed) // crash 发生时候的线程
 0  libcrash-lib.so + 0x544 // 发生 crash 的位置和寄存器信息 

  第七步:有了具体的寄存器信息,我们进行符号解析,可以使用Android NDK中提供的addr2line来根据地址进行一个符号反解的过程,该工具在Android SDK目录下可以找到。
  工具链的选择要根据.so的类型来决定,看解析后的文件,有显示CPU信息。下面是NDK 18的工具链目录:

image.png

  如果是arm-64位的so,解析是需要使用aarch64-linux-android-4.9下的工具链。
  如果是arm-32位的so,解析是需要使用arm-linux-androideabi-4.9下的工具链。
  如果是x86-64位的so,解析是需要使用x86_64-4.9下的工具链。
  如果是x86-32位的so,解析是需要使用x86-4.9下的工具链。
  这里,因为CPU信息是x86,所以选择x86-4.9下的工具链。我们将项目中build目录下的x86对应的libcrash-lib.so(app\build\intermediates\transforms\mergeJniLibs\debug\0\lib\x86\libcrash-lib.so)拷贝到x86-4.9下的工具链目录(x86-4.9\prebuilt\windows-x86_64\bin)中,然后执行如下命令:
image.png

  是不是看到具体哪个类下的哪一行报错了!!!

2. 利用真机测试

  前五步一样,请参考模拟器测试部分类容。
  第六步:将真机中的.dump文件进行解析:


image.png

  从上面的截图能看到,有一堆的ERROR错误信息,但是生成了解析文件。打开看下:

Operating system: Android
                  0.0.0 Linux 4.4.23+ #1 SMP PREEMPT Mon Sep 17 22:10:21 CST 2018 aarch64
CPU: arm64
     8 CPUs

GPU: UNKNOWN

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

Thread 0 (crashed)
 0  libcrash-lib.so + 0x5f8
     x0 = 0x0000000000000000    x1 = 0x0000000000000001
     x2 = 0x0000007fcd3ec4a0    x3 = 0x0000007ab06f0a58
     x4 = 0x0000007fcd3ec0b0    x5 = 0x0000007ab07e4476
     x6 = 0x0000007ab07e43f8    x7 = 0x0000000000000000
     x8 = 0x918e7d903505e7f1    x9 = 0x918e7d903505e7f1
    x10 = 0x0000000000430000   x11 = 0x0000000000000000
    x12 = 0x0000007ab451de90   x13 = 0xd962dd2e22e77fe1
    x14 = 0x0000007ab451d000   x15 = 0xffffffffffffffff
    x16 = 0x0000007a932befe8   x17 = 0x0000007a932ae5e8
    x18 = 0x0000000000000020   x19 = 0x0000007aafca3a00
    x20 = 0x0000007ab06f0ab8   x21 = 0x0000007aafca3a00
    x22 = 0x0000007fcd3ec4fc   x23 = 0x0000007ab07eed97
    x24 = 0x0000000000000004   x25 = 0x0000007aafca3aa0
    x26 = 0x0000000000000000   x27 = 0x0000000000000000
    x28 = 0x0000007fcd3ec240    fp = 0x0000007fcd3ec200
     lr = 0x0000007a932ae5e0    sp = 0x0000007fcd3ec1f0
     pc = 0x0000007a932ae5f8
    Found by: given as instruction pointer in context
 1  libcrash-lib.so + 0x5dc
     fp = 0x0000007fcd3ec240    lr = 0x0000007aafaf5704
     sp = 0x0000007fcd3ec210    pc = 0x0000007a932ae5e0
    Found by: previous frame's frame pointer

  的确与模拟解析出来的有点不一样哦。
  第七步:我们利用aarch64-linux-android-4.9下的工具链对其进行符号反解。

image.png

  虽然,第六步解析.dump文件的过程中提示ERROR信息,但是第七步执行完后,最终的结果是一样的,也是我们预测的结果。

3. 将真机的测试结果在Ubuntu中验证

  文章开始就提到,Google Breakpad在Github上的说明是要自己编译minidump_stackwalk的,那么下面就用在Ubuntu上根据Breakpad源码编译出来的minidump_stackwalk进行对.dump解析:

image.png

  由于解析过程中提示信息太多,所以我单独将其复制下来了(放在我的实例项目根目录下了),因为仔细看发现也有ERROR信息,而且同Windows下Android自带的minidump_stackwalk解析时ERROR信息是一样的:
image.png

  从验证结果来看,我们可以直接利用Android自带的minidump_stackwalk直接进行解析。


  对比真机测试和模拟器测试,在解析.dump文件时,前者会出现ERROR信息,而后者却正常呢?我们先来看下ERROR信息的错误,虽然能看懂英文,但不知其具体含义,不过我们可以在解析后的文件中看到的确有0x15cd207000000001和0x80238(其实是0x0000000000080238,省去了中间的0而已):

Thread 0 (crashed)
 0  libcrash-lib.so + 0x5f8
     ......
 1  libcrash-lib.so + 0x5dc
     ......
 2  libart.so + 0x512700
     fp = 0x15cd207000000001    lr = 0x0000000000000000
     sp = 0x0000007fcd3ec250    pc = 0x0000007aafaf5704
    Found by: previous frame's frame pointer
Thread 15
 0  libc.so + 0x699e8
     ......
 1  libc.so + 0x42c00
     fp = 0x0000000000080238    lr = 0x0000007aa1e62e80
     sp = 0x0000007a928823f0    pc = 0x0000007ab0d5bc04
    Found by: previous frame's frame pointer

  大家从上述两个截取的片段中应该能够看到对应的地址了吧,0x15cd207000000001指向的是libart.so的系统库(我用的华为荣耀8,华为自研的海思芯片,也就是麒麟);0x0000000000080238指向的是libc.so的系统库。所以,我猜测应该是华为手机kernel层做了什么事情吧,而模拟器针对的是Google原生系统,是标准库(大家也可以拿别的手机也试试);不管我的猜测是否正确,ERROR信息中涉及的寄存器信息均与自己写的、待分析的libcrash-lib.so没有关系,也就是说不影响我们的业务代码分析,所以可以忽略。

3. 总结一下

  其实,回过头来看Google Breakpad在Github上的说明,是针对没有minidump_stackwalk执行文件的开发者而言的,打个比方(可能不恰当,假设做Java后台的没有minidump_stackwalk):做Java Web的,利用JNI实现了一个需求,想捕获异常并分析,那么他就必须在他开发的平台上(Windows、Mac或Linux下)利用Google Breakpad源码生成一个minidump_stackwalk文件。只是,Android已经提供这个文件,只是有些人不知道而已。
  另外,我推测:在Linux、Mac下,只要安装了Android Studio应该都不需要编译Google Breakpad源码,对与不对,不喜忽喷,用Mac的同志可以帮忙测试下,欢迎留言!!!

你可能感兴趣的:(Android在Windows下无需编译Breakpad源码就能使用)