工匠若水可能会迟到,但是从来不会缺席,最终还是觉得将自己的云笔记分享出来吧 ~
Android Native Crash 的捕获其实是有好几种方案的,譬如 coffeecatch、crash 后新进程过滤 logcat、google BreakPad 等,系统层面就另当别论了,系统有那么强大的墓碑机制,所以不讨论墓碑情况。应用层方案来说,其各自都存在一些利弊或者兼容问题,综合来看,Android 系还是推荐使用 google BreakPad 实现,本文就抛开其他方案来聊聊 BreakPad 在 Android App 开发中的接入、使用、定位问题全过程吧。
【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。+微信 codedeveloper 联系我】
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
构建则直接按照如下操作即可:
Application.mk
文件,内容如下:APP_STL := stlport_static
APP_ABI := all
APP_CXXFLAGS := -std=c++11 -D__STDC_LIMIT_MACROS
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)
如果你想用 CMake,那更不用我说了,直接就是把上面这些 mk 翻译下就行,怎么翻译成CMakeLists.txt
我就不扯蛋了。
【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。+微信 codedeveloper 联系我】
如下从几个维度分析说明了如何使用。
这里要特别注意一个问题,记得每次构建备份自己 obj 带符号表的 so 文件,发布一定要用不带符号表的小体积 so 文件,别搞混了,别问我为什么,这是常识。
如上一顿操作后你会发现,你只用在你项目初始化时给他喂个路径就行,然后就等着 native 奔溃后触发他往你路径下写一个.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
打开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,随伏案吐血而亡。