欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android Native Crash 捕获之 BreakPad

程序员文章站 2022-03-12 07:52:19
工匠若水可能会迟到,但是从来不会缺席,最终还是觉得将自己的云笔记分享出来吧 ~背景Android Native Crash 的捕获其实是有好几种方案的,譬如 coffeecatch、crash 后新进程过滤 logcat、google BreakPad 等,系统层面就另当别论了,系统有那么强大的墓碑机制,所以不讨论墓碑情况。应用层方案来说,其各自都存在一些利弊或者兼容问题,综合来看,Android 系还是推荐使用 google BreakPad 实现,本文就抛开其他方案来聊聊 BreakPad 在 An...

工匠若水可能会迟到,但是从来不会缺席,最终还是觉得将自己的云笔记分享出来吧 ~

背景

Android Native Crash 的捕获其实是有好几种方案的,譬如 coffeecatch、crash 后新进程过滤 logcat、google BreakPad 等,系统层面就另当别论了,系统有那么强大的墓碑机制,所以不讨论墓碑情况。应用层方案来说,其各自都存在一些利弊或者兼容问题,综合来看,Android 系还是推荐使用 google BreakPad 实现,本文就抛开其他方案来聊聊 BreakPad 在 Android App 开发中的接入、使用、定位问题全过程吧。

【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。+微信 codedeveloper 联系我】

Breakpad 是什么

Breakpad 是一个库和工具套件,具有一定的跨平台能力,google 浏览器,Android 等都有利用其进行 Native 捕获,他可以把已发布的脱表应用程序 Native 崩溃记录在一个 dump 文件中,然后我们可以把这个文件捞回服务器,并从这些 minidump 文件里的 C 和 C++ 堆栈分析崩溃问题原因。他也能根据请求把没有崩溃的程序 Native 堆栈写入 minidump,类似 Java 层主动打印调用栈能力。

其项目地址为https://github.com/google/breakpad

【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。+微信 codedeveloper 联系我】

编译适合自己平台的库

上来一把梭,官方文档真的很详细了,不再多说,照着敲就行,记得自己要编译的是 Android 平台即可。上官方 Android 编译 README 文件。下面只说下注意的几个坑即可:

  • 注意你的 ndk 版本,建议使用 NDK 16b 版本或者低一点的。

  • 照着文档你可能发现编译直接失败,分析失败原因你会发现是很多文件 include 的linux_syscall_support.h头文件缺失了,全局搜代码也没有这个linux_syscall_support.h文件,一波 google 搜索linux_syscall_support.h关键词发现这玩意在https://chromium.googlesource.com/linux-syscall-support/里面,忽然记得文档说 Breakpad 来自 chromium,那这可能就是真相了,直接下载下来。下载下来放哪呢?莫慌,去看用他的地方吧,发现代码如此引用#include "third_party/lss/linux_syscall_support.h",妥了,直接把他连同 lss 目录复制到 Breakpad 项目的 third_party 下,然后编译一把过。

  • 官方README.ANDROID给出的是Android.mk配置,如果你需用通过 CMake 来编译则自己翻译成 CMakeLists.txt 就行,这玩意就看你自己了。

    如果你采用Android.mk构建则直接按照如下操作即可:

    1. 你的项目 jni 目录下添加Application.mk文件,内容如下:
      APP_STL := stlport_static
      APP_ABI := all
      APP_CXXFLAGS := -std=c++11 -D__STDC_LIMIT_MACROS
      
    2. 你的项目 jni 目录下添加我自己编写的这个一把过的Android.mk文件,内容如下:
      ROOT_PATH := $(call my-dir)
      # 如下是你 breakpad 项目的路径,直接引用其中的 Android.mk 编译,此步骤编译产物是一个静态库,名字为 breakpad_client,这是官方代码,我们没做任何修改哦。
      include $(ROOT_PATH)/third-breakpad/android/google_breakpad/Android.mk
      LOCAL_PATH := $(ROOT_PATH)
      include $(CLEAR_VARS)
      # 我们项目中对其包装的 jni 层 so 名字
      LOCAL_MODULE := native-crash
      # 我们项目中对其包装的 jni 层代码
      LOCAL_SRC_FILES := NativeHandler.cpp
      # 我们项目中链接 breakpad 项目静态产物
      LOCAL_STATIC_LIBRARIES += breakpad_client
      
      include $(BUILD_SHARED_LIBRARY)
      
    3. 如上一把梭就完成了,没啥难度,然后 java 层直接使用即可,如上你要看不懂就自己去学习下 C 编译原理及 NDK 相关基础吧。
    4. 当然是不要脸的附上我的 demo 地址https://github.com/yanbober/android-crash/tree/master/native-crash-core,很不幸的是这个项目夭折了,不过 native 捕获这块没啥大问题。
  • 如果你想用 CMake,那更不用我说了,直接就是把上面这些 mk 翻译下就行,怎么翻译成CMakeLists.txt我就不扯蛋了。

【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。+微信 codedeveloper 联系我】

Breakpad 怎么用

如下从几个维度分析说明了如何使用。

构建 so 注意事项

这里要特别注意一个问题,记得每次构建备份自己 obj 带符号表的 so 文件,发布一定要用不带符号表的小体积 so 文件,别搞混了,别问我为什么,这是常识。

获取 native crash 后的 dump

如上一顿操作后你会发现,你只用在你项目初始化时给他喂个路径就行,然后就等着 native 奔溃后触发他往你路径下写一个.dump文件吧。

如果你需要线上使用且需要保留所有的 dump,那就自己简单封装管理下文件目录啥的吧,然后每次启动发现有这个文件就上传到你服务器吧。

dump 文件怎么转成可阅读的崩溃堆栈

假设你程序现在 native crash 了,你也从服务端拿到 dump 文件了,接着我们需要对其做解析即可。

解析工具从哪来?还是 Breakpad 编译就行,具体参见官方文档里如何编译 minidump_stackwalk 工具部分吧。

如果你像我一样足够懒,或者因为第一次编译了 minidump_stackwalk 工具但是后来丢了,这时候你的第一选择肯定就是重新拉代码编译出一个工具。我想说的是,你可以偷懒,哈哈。

现成的 minidump_stackwalk 工具在哪里?

在这里,无论你是 Mac、windows、linux,我都试过,在 Android Studio 的安装目录下的bin\lldb\bin里面就存在一个对应平台的可执行文件叫做 minidump_stackwalk,这就是偷懒的办法。

所以我们直接用这个工具执行解析 dump 即可,如下操作:

./minidump_stackwalk xxooxx.dump > crash.txt

dump转换成可阅读崩溃堆栈文件后怎么分析

打开crash.txt你会发现类似如下的日志:

Operating system: Android
                  0.0.0 Linux 4.14.116 #1 SMP PREEMPT Wed Apr 8 17:01:19 CST 2020 armv8l
CPU: arm
     ARMv1 ARM part(0x4100d0b0) features: half,thumb,fastmult,vfpv2,edsp,neon,vfpv3,tls,vfpv4,idiva,idivt
     8 CPUs

GPU: UNKNOWN

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

Thread 0 (crashed)
 0  test.so + 0x18efd86
     r0 = 0x00000000    r1 = 0x00000000    r2 = 0x7fffffff    r3 = 0x00000000
     r4 = 0x0000002a    r5 = 0xffdfd948    r6 = 0xd1403280    r7 = 0xd13cdd14
     r8 = 0xffdfd4f4    r9 = 0xffdfd940   r10 = 0xffdfd944   r12 = 0xffdfd4b8
     fp = 0xffdfd4f4    sp = 0xffdfd4e0    lr = 0xf3d6886d    pc = 0xcfc03d86
    Found by: given as instruction pointer in context
    ......

通过上面的Crash reason: SIGSEGV我们可以知道是 unix 系统里的哪种类型错误,最关键的其实是(crashed)关键词开始的行,显示了哪个 so 哪里 crash 了,即test.so + 0x18efd86显示了发生 crash 的位置和寄存器信息。

有了具体的崩溃寄存器信息,我们接下来直接进行符号解析即可,然后就能直接看到是源码的哪一行崩溃了,直接去修复即可,使用 Android NDK 里面提供的 addr2line 工具(SDK 里面自己找)将寄存器地址转换为对应符号。记得 addr2line 要用和自己 so 的 ABI 匹配的目录(上面crash.txt中有崩溃 so 的 ABI 信息)。

arm-linux-androideabi-addr2line -f -C -e obj-test.so 0x18efd86
//具体崩溃代码位置,其他行号啥的自己去看 addr2line 参数列表吧
xxxxxxFunc()

这里就不再过多解释 addr2line 的用法了,和安卓墓碑机制的 tombstone 解析一样用法,自己去查吧,懒得写了。

crash.txt中崩溃的不是我的 so 怎么办

一般 app 开发者都是凉拌,最多就是反馈给对应固件厂商或者 SDK 的开发者,因为想要通过寄存器地址反转到代码行数是需要有对应版本 obj 中带符号表的 so 才行,而这些 so 的备份只有对应提供者有。譬如如下这种奇葩的 bug。

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

Thread 0 (crashed)
 0  libmonochrome.so + 0x18efd86

唯一途径就是反馈给厂商背锅了。

【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。+微信 codedeveloper 联系我】

总结

甩锅需要证据,证据来源如上分析,一顿操作猛如虎,最后发现 native crash 最多的都特么是三方 SDK,随伏案吐血而亡。

本文地址:https://blog.csdn.net/yanbober/article/details/108586835