tombstone问题追踪与分析

android中有3种crash情况:未捕获的异常ANR闪退。未捕获的异常一般用crash文件就可以记录异常信息,而ANR无响应表现就是界面卡着无法响应用户操作,而闪退则是整个app瞬间退出。闪退一般是由于调用so库出错导致,像类似非法地址访问等,此时会生成/data/tombstones/tombstone_x文件,通过此文件分析崩溃后的错误信息 这种方法则主要是分析程序崩溃之后产生的错误信息相关文件来确定产生 Bug 的原因和位置。文件内容如下例:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'Android-x86/android_x86/x86:5.1.1/LMY48W/woshijpf04211939:eng/test-keys'
Revision: '0'
ABI: 'x86'
pid: 1019, tid: 1019, name: surfaceflinger  >>> /system/bin/surfaceflinger <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4
    eax a6265c06  ebx b7467d88  ecx b7631a22  edx a6265c06
    esi 00000000  edi b6867140
    xcs 00000073  xds 0000007b  xes 0000007b  xfs 00000000  xss 0000007b
    eip b745a639  ebp bfcfc1e8  esp bfcfc150  flags 00010282
    backtrace:
    #00 pc 00006639  /system/lib/libui.so (android::Fence::waitForever(char const*)+41)
    #01 pc 00034b86  /system/lib/libsurfaceflinger.so
    #02 pc 0003229e  /system/lib/libsurfaceflinger.so
    #03 pc 0002cb9c  /system/lib/libgui.so (android::BufferQueue::ProxyConsumerListener::onFrameAvailable(android::BufferItem const&)+652)
    #04 pc 000342f4  /system/lib/libgui.so (android::BufferQueueProducer::queueBuffer(int, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*)+2580)
    #05 pc 0004eafb  /system/lib/libgui.so (android::Surface::queueBuffer(ANativeWindowBuffer*, int)+411)
    #06 pc 0004ce06  /system/lib/libgui.so (android::Surface::hook_queueBuffer(ANativeWindow*, ANativeWindowBuffer*, int)+38)
    #07 pc 00014bc6  /system/lib/egl/libGLES_android.so
    #08 pc 00017f73  /system/lib/egl/libGLES_android.so (eglSwapBuffers+163)
    #09 pc 00015fdb  /system/lib/libEGL.so (eglSwapBuffers+203)
    #10 pc 000013ea  /system/lib/hw/hwcomposer.x86.so
    #11 pc 00034730  /system/lib/libsurfaceflinger.so
    #12 pc 000256d4  /system/lib/libsurfaceflinger.so
    #13 pc 00024bf4  /system/lib/libsurfaceflinger.so
    #14 pc 000236fb  /system/lib/libsurfaceflinger.so
    #15 pc 0002338a  /system/lib/libsurfaceflinger.so
    #16 pc 0001e0ff  /system/lib/libsurfaceflinger.so
    #17 pc 0001d9ce  /system/lib/libutils.so (android::Looper::pollInner(int)+926)
    #18 pc 0001db73  /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+67)
    #19 pc 0001e561  /system/lib/libsurfaceflinger.so
    #20 pc 00022ce7  /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::run()+39)
    #21 pc 00000ca3  /system/bin/surfaceflinger
    #22 pc 0001365a  /system/lib/libc.so (__libc_init+106)
    #23 pc 00000da8  /system/bin/surfaceflinger
    stack:
         bfcfc110  00000000  
         bfcfc114  b6839270  
         bfcfc118  00000000  
         bfcfc11c  00000000  
         bfcfc120  b68394e0  
         bfcfc124  00000002  
         bfcfc128  00000002  
         bfcfc12c  b75d8185  /system/lib/libutils.so (android::RefBase::incStrong(void const*) const+53)
         bfcfc130  b6839270  
         bfcfc134  bfcfc1e8  [stack]
         bfcfc138  00000002  
         bfcfc13c  a6265c06  
         bfcfc140  b7467d88  /system/lib/libui.so
         bfcfc144  00000000  
         bfcfc148  b6867140  
         bfcfc14c  b745a639  /system/lib/libui.so (android::Fence::waitForever(char const*)+41)
    #00  bfcfc150  b683af18  
         bfcfc154  bfcfc1e8  [stack]
         bfcfc158  00000000  
         bfcfc15c  00000000  
         bfcfc160  00000000  
         bfcfc164  b683af18  
         bfcfc168  b75ec9c4  /system/lib/libutils.so
         bfcfc16c  b75d8285  /system/lib/libutils.so (android::RefBase::weakref_type::decWeak(void const*)+37)
         bfcfc170  00000000  
         bfcfc174  00000000  
         bfcfc178  00000000  
         bfcfc17c  00000000  
         bfcfc180  b7642968  /system/lib/libsurfaceflinger.so
         bfcfc184  bfcfc1e8  [stack]
         bfcfc188  b6867140  
         bfcfc18c  b7622b87  /system/lib/libsurfaceflinger.so

tombstone 文件它主要由下面几部分组成:

  • Build fingerprint
  • Crashed process and PIDs
  • Terminated signal and fault address
  • CPU registers
  • Call stack
  • Stack content of each call
    但是也不是 tombstone 中的信息我们都需要分析,我们最主要的就是分析 Crashed process and PIDs、Terminated signal and fault address和Call stack 部分。

Crashed process and PIDs 信息

信号机制是进程之间相互传递消息的一种方法,下表展示的是一些常见的信号种类。
从上面 tombstone 文件中的第 5 行中我们可以看到 Crash 掉进程的基本信息,如下所示:

pid: 1019, tid: 1019, name: surfaceflinger  >>> /system/bin/surfaceflinger <<<

如果 pid 等于 tid ,那么就说明这个程序是在主线程中 Crash 掉的,name 的属性则表示 Crash 进程的名称以及在文件系统中位置。

Terminated signal and fault address 信息

在上面 tombstone 文件中的第 6 行中我们可以看到程序是因为什么信号导致了 Crash 以及出现错误的地址,如下所示:

signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4

这里的信息说明出现进程 Crash 的原因是因为程序产生了段错误的信号,访问了非法的内存空间,而访问的非法地址是 0x4。

信号名默认动作发出原因
SIGQUIT3终止进程键盘的退出键被按下,虚拟机下用来打印堆栈
SIGILL4终止进程非法指令
SIGABRT6终止进程由abort(3)发出的终止指令,用户无法终止
SIGSEGV11终止进程无效的内存引用
SIGBUS7终止进程总线错误(错误的内存访问)
  • SIGBUS与SIGSEGV的区别
    SIGBUS(Bus error)意味着指针所对应的地址是有效地址,但总线不能正常使用该指针。通常是未对齐的数据访问所致。比如int型要4字节对齐,short型的2字节对齐。例如:short array[16];int * p = (int *)&array[1];*p = 1;SIGSEGV(Segment fault)意味着指针所对应的地址是无效地址,没有物理内存对应该地址。例如:int pi= (int)0x00001111;*pi = 17

Call Stack 信息

调用栈信息是分析程序崩溃的非常重要的一个信息,它主要记录了程序在 Crash 前的函数调用关系以及当前正在执行函数的信息,它对应的是我们 tombstone 文件中 backtrace 符号开始的信息,上面例子中的 backtrace 的信息如下所示:

 backtrace:
    #00 pc 00006639  /system/lib/libui.so (android::Fence::waitForever(char const*)+41)
    #01 pc 00034b86  /system/lib/libsurfaceflinger.so
    #02 pc 0003229e  /system/lib/libsurfaceflinger.so
    #03 pc 0002cb9c  /system/lib/libgui.so (android::BufferQueue::ProxyConsumerListener::onFrameAvailable(android::BufferItem const&)+652)
    #04 pc 000342f4  /system/lib/libgui.so (android::BufferQueueProducer::queueBuffer(int, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*)+2580)
    #05 pc 0004eafb  /system/lib/libgui.so (android::Surface::queueBuffer(ANativeWindowBuffer*, int)+411)
    #06 pc 0004ce06  /system/lib/libgui.so 
    .....

在上面的输出信息中, ##00,#01,#02……等表示的都是函数调用栈中栈帧的编号,其中编号越小的栈帧表示着当前最近调用的函数信息,所以栈帧标号 #00 表示的就是当前正在执行并导致程序 Crash 函数的信息。

在栈帧的每一行中,pc 后面的 16 进制数值表示的是当前函数正在执行语句的在共享链接库或者可执行文件中的位置,然后 /system/lib/libui.so 则表示的是当前执行指令是在哪个文件当中,后面的小括号则是注明对应的是哪个函数。

例如,在上面的例子中,我们就可以定位到是程序是在 Fence::waitForever(char const* )中出现了错误,但是具体在那一行呢,我们还不是特别清楚,所以就需要我们进一步地使用更加高级的工具来帮助我们解析 tombstone 中有关调用栈的信息。

定位 Crash 源码位置的工具

前面我们简要地介绍了 tombstone 文件的结构以及每个部分的相关含义,我们可以得到导致程序 Crash 掉的主要原因是什么(根据 Signal 的类型),也知道了是在主线程还是在子线程中挂掉了,但是我们对程序具体在代码中的哪个位置挂掉了,还不是特别清楚,最多还只是通过 backtrace 中的栈帧的信息大概定位到位于哪个函数中,但具体是哪个文件哪个函数那一行还是不清楚的。所以,我们接下来就需要借助一些更加高级的工具来定位 Bug 在代码中的具体位置。

Google 提供的 Android NDK 开发包中已经为我们提供了非常便利好用的解析工具了https://developer.android.google.cn/ndk/downloads/index.html

我们主要使用了下面两种工具来解析 tombstone:

addr2line

addr2line 是 NDK 中用来获得指定动态链接库文件或者可执行文件中指定地址对应的源代码信息,它位于 NDK 包中的如下位置中:

$NDK_HOME/toolchains/x86-4.6/prebuilt/linux-x86/bin/i686-linux-android-addr2line

其中 NDK_HOME 表示你 NDK 的安装路径。 虽然在 Linux 中同样有 addr2line 命令了,但是它与 NDK 中提供的 addr2line 指令还是略有差别的,所以我们可以使用 alias 来将 shell 中默认的 addr2line 指令链接的 NDK 的上述路径中,命令如下所示:

alias addr2line='$NDK_HOME/toolchains/x86-4.6/prebuilt/linux-x86/bin/i686-linux-android-addr2line'

addr2line基本用法如下:

#:addr2line -f -e libui.so 00006639
_ZN7android5Fence11waitForeverEPKc
/xx/Fence.cpp:59

使用了 addr2line 工具之后,我们终于看到 libui.so 文件中地址 00006639 对应的源码是什么了,它对应的是 Android 系统源码中 /home/woshijpf/newspace/android-x86/frameworks/native/libs/ui/Fence.cpp:59 处代码(因为上面的 tombstone 文件是由于 Android 系统中的 surfaceFlinger 进程崩溃而产生的,所以它对应的源码也是 Android 系统中有关 SurfaceFlinger 部分的源码)。

objdump

objdump可以将so库进行反汇编,反汇编后得到重定向文件,然后根据偏移地址得到更详细的函数调用上下文信息。

我实际使用ndk 20版本发现addr2line 运行可以,objdump总有一些错误,所以最后使用ndk 10命令执行成功。

objdump -S -D D:\xx\xx.so > D:\xx\xx.log

打开xx.log文件,搜索偏移地址“6639 ”,即可看到此地址的汇编指令操作。

ndk-stack

ndk 的使用说明如下所示:

Usage:
ndk-stack -sym [-dump ]
-sym Contains full path to the root directory for symbols.
-dump Contains full path to the file containing the crash dump.
This is an optional parameter. If ommited, ndk-stack will
read input data from stdin

其中,

  • dump 参数很容易理解, 即 dump 下来的 log 文本文件. ndk-stack会分析此文件,这里我们就是指定我们从 Android 系统中获取到的 tombstone(这个需要通过 adb pull 的方式拉取到本地的宿主机中) 文件存放的位置。
  • sym 参数就是你android项目下,编译成功之后,obj目录下的文件。

参考:
https://www.jianshu.com/p/dd0b0a09aa78
https://blog.csdn.net/zerokkqq/article/details/73392172
https://cloud.tencent.com/developer/article/1969660

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值