[TOC]
简述
XCrash 的崩溃捕获分为, java 崩溃捕获,Native 崩溃捕获 和 Anr 崩溃捕获。
这是 xcrash 是的架构图
图片来自 xCrash 的 github
- JavaCrashHandler 捕获 Java 崩溃,handleException 处理 java 崩
- NativeHandler 捕获 native 崩溃
- AnrHandler 捕获 ABI < 21 的 anr ,高于等于 21 的在 native 层捕获
1 Java 崩溃的捕获
2. Native 崩溃的捕获
Native 崩溃的捕获主要是两方面:
- 一是捕获到崩溃信号
- 二是堆栈的回溯,还原崩溃现场
图片来自 xCrash 的 github
2.1 Native 崩溃捕获的时序
- 初始化
NativeHandler#initialize
加载 xcrash.so
初始化 nativeInit
1 | int initialize(...){ |
- xc_jni.c初始化
xc_jni.c 文件
1 | static jint xc_jni_init(..){ |
这是 Native 的入口
xc_common_init
这是初始化一下公共信息,例如版本、 ABI、app_version 版本等信息crash_dump_all_threads_whitelist
设置崩溃是 dump 白名单线程xc_crash_init
这个是初始化的重点, 后面会详细讲述xc_trace_init
2.2 xc_crash_init
xc_crash.c
1 | int xc_crash_init(...) |
xc_crash_init 是核心方法
xc_crash_prepared_fd 是为崩溃时预留存储文件
① xcc_unwind_init, 根据不同的 API 版本,初始化堆栈回溯 so
- 16 >= API Level <= 20, 使用 libcorkscrew.so
- 21 >= API Level <= 23, 使用 libunwind.so
- APL Level >= 24, 可以使用 _Unwind_Backtrace 直接调用
② xc_crash_init_callback, 设置回调, 回调到 NativeHandler 的 crashCallback 方法
③ for clone and fork, 对于 arm 架构来说,是使用 pipe2 创建管道,用来通知崩溃的信号
④ xcc_signal_crash_register, 注册信号捕获 handler,handler 是 xc_crash_signal_handler
2.3 xcc_signal_crash_register
xcc_signal.c
1 | // 需要监听的信号 |
① 设置而外空间
- SIGSEGV 有可能是栈溢出引起的,如果在默认的栈上运行很用可能会破坏程序运行的现场,无法获得正确的上下文。而且当栈满了,系统会在同一个已经满了的栈上调用 SIGSEGV 的信号处理函数,又再一次引起同样的信号。
- 开辟一块新的空间作为运行信号处理函数的栈。可以使用 sigaltstack 在任意线程注册一个可选的栈,保留一下载紧急情况下使用的空间。
出自 [https://mp.weixin.qq.com/s/g-WzYF3wWAljok1XjPoo7w?]
2.4 xc_crash_signal_handler
xc_crash.c#xc_crash_signal_handler
xc_crash_spot
① 设置 crash 时的信息、包括崩溃时间、崩溃线程等
1 | //set crash spot info |
xc_crash_fork
② fork 一个进程都用了 dump 内容 xc_crash_exec_dumper
1 | static int xc_crash_fork(int (*fn)(void *)) |
xc_crash_exec_dumper
xc_crash_exec_dumper 是在子进程中执行了,该方法有两个功能:
- 一是, 设置 pipe 管道参数,set args pipe size
- 二是,加载 libxcrash_dumper.so 文件 ③
1 | static int xc_crash_exec_dumper(void *arg) |
xcd_core.c#main;
④ 从 xcd_core.c 的 main方法开始执行
当加载 libxcrash_dumper.so, 开始执行的方法是 xcd_core.c#main 方法
main 方法里面包含了几个步骤:
- xcc_unwind_init, 初始化堆栈回溯,为了捕获 dump 子进程可能发生的崩溃
- xcc_signal_crash_register, 注册 signal, 为了捕获 dump 子进程可能发生的崩溃
- xcd_process_create,统计崩溃进程的所有线程信息
- xcd_process_suspend_threads, 挂起该进程 的所有线程
- xcd_process_load_info, 获取进程的信息,包含进程、线程和内存映射信息
- xcd_sys_record, 记录系统信息,包含 ABI Version 版本等
- xcd_process_record, 记录进程信息
- xcd_process_resume_threads,恢复所有线程
xcd_core.c#xcd_process_load_info
xcd_process_load_info 最终通过 xcd_maps_create 方法获取进程的内存映射信息
1 | int xcd_maps_create(xcd_maps_t **self, pid_t pid) |
xcd_process_record
⑥ 记录进程信息
这个方面里面是记录进程里面的主要信息
1 | int xcd_process_record(...){ |
- 获取进程的 id, 名称 和 线程 id
1 | pid: 20501, tid: 20501, name: xcrash.sample >>> xcrash.sample <<< |
获取 signal 信号信息
1
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
获取 Abort message
1 | Abort message: 'Check failed: key_value != nullptr compiler-filter not found in oat header' |
- 获取寄存器信息
1 | r0 00000000 r1 00003d49 r2 00000006 r3 a2787e90 |
- 获取 backtrace 堆栈信息
1 | backtrace: |
- 获取 so 的 build id、md5 和时间
1 | build id: |
获取 stack 信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19stack:
a2787e20 00000002
a2787e24 b2a07260 [anon:.bss]
a2787e28 00000000
a2787e2c 6ee08ad0 [anon:libc_malloc]
a2787e30 b06e1217 /apex/com.android.runtime/lib/libart.so
a2787e34 09b33a2f /system/bin/app_process32 (sigprocmask64+70)
a2787e38 00000000
a2787e3c b2a07260 [anon:.bss]
a2787e40 b47037a4 [anon:libc_malloc]
a2787e44 345acf15 [anon:dalvik-main space (region space)]
a2787e48 a2787f70
a2787e4c a2787e88
a2787e50 a2787f70
a2787e54 a2787e88
a2787e58 00003d3f
a2787e5c b44b8190 [anon:.bss]
#00 a2787e60 a2787ed0
a2787e64 a2787ee0获取内存信息
1 | memory near r1: |
- 内存映射 map
1 | memory map: |
- 系统 logcat 信息
1 | logcat: |
- 文件句柄 fd
1 | open files: |
- 网络信息
1 | network info: |
- 内存信息
1 | memory info: |
3. ANR 的捕获
ANR 的捕获因为 Android 版本的限制,在低于 21 一下是在 AnrHandler 中,通过监听 /data/anr/ 路径下的文件变化来实现的。
1 | // AnrHandler.java |
对于大于及 21 版本以上的,则在 xc_trace.c 中
1 | int xc_trace_init(...) |
xcc_signal_trace_register
xcc_signal_trace_register ① 注册监听 会用 xc_trace_handler 监听
如果有信号过来,xc_trace_handler 里面会写 xc_trace_notifier 的值,通知 dump 线程开始 dump
1 | static void xc_trace_handler(int sig, siginfo_t *si, void *uc) |
xc_trace_dumper
1 | static void *xc_trace_dumper(void *arg) |
4. 总结
xCrash 捕获的崩溃信息可以在第二次启动的时候上报,比较适合自己搭建的崩溃上报系统。市场上现有的 友盟 、Bugly 做的很好,但是有些产品性质的需要,要搭建自己的崩溃上报,xCrash 是个不错的选择.
另外,xCrash 涉及很多系统调用的函数已经堆栈回溯的技术,这方法知识需要自己后续补上。