从零搭建 Native Crash 收集方案,逐一解决重点问题
2024-02-12 11:06:25
Native Crash 一直是 Android 平台上比较麻烦的问题,因为捕获麻烦,获取到了内容又不全,内容全了信息又不对,信息对了又不好处理。相比于 Java Crash,Native Crash 是通过信号量或者线程的方式来进行崩溃的捕获的,这也使得我们很难在外部去获取到一个 Native Crash 的详细信息,这也是 Native Crash 一直以来都被成为疑难杂症的原因之一。
当然了,随着 Android 版本的更新迭代,Google 官方也给我们提供了很多便捷的功能,去捕获 Native Crash 的具体信息,以方便我们进行开发,不过其局限性还是挺大的,因此,在工作中,我们还是希望能够自己实现一个 Native Crash 的收集方案,然后交由后台进行分析处理。这样的话,不仅可以提高 Native Crash 收集的效率,也可以提高对 Crash 信息内容的完整性和准确性。
一、收集
在收集 Native Crash 之前,我们首先要对崩溃的信号量进行监听。Android 平台上,Native Crash 的信号量是 SIGSEGV、SIGBUS、SIGILL、SIGFPE 这几种,可以通过 signal 函数来进行信号量的监听。
signal(SIGSEGV, SignalHandle);
signal(SIGBUS, SignalHandle);
signal(SIGILL, SignalHandle);
signal(SIGFPE, SignalHandle);
收到信号量后,我们要获取 Native Crash 崩溃的具体信息。Android 平台提供了backtrace
和backtrace_symbols
这两个函数可以获取 Native Crash 的堆栈信息,通过这两个函数,我们可以获取到 Native Crash 的线程 ID、崩溃时刻的寄存器状态、崩溃时刻所处的函数调用情况,以及函数具体的位置。获取到这些信息后,我们要做的就是保存起来或者发送到后台进行分析了。
void SignalHandle(int signum) {
NativeCrashContext context;
context.tid = gettid();
context.signo = signum;
DumpRegs(&context);
DumpBacktrace(&context);
DumpMaps(&context);
// ...
}
二、解析
有了 Native Crash 的堆栈信息后,我们要对堆栈信息进行解析,具体解析就是解析出函数名、行数、文件路径等信息。Android 平台上提供了addr2line
这个工具,可以将 so 地址解析成具体的函数名、行数、文件路径。我们只需要将 Native Crash 的堆栈信息交给 addr2line 解析即可。
char cmd[1024] = {0};
snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", sopath, addr);
FILE *fp = popen(cmd, "r");
char buf[1024] = {0};
while (fgets(buf, sizeof(buf), fp) != NULL) {
// ...
}
pclose(fp);
三、上传
有了 Native Crash 的详细信息后,我们要做的就是将这些信息上传到后台进行分析了。我们可以通过 HTTP、Socket 等方式将 Native Crash 的信息上传到后台,在后台进行分析、处理,并最终反馈给我们,这样,我们就可以知道 Native Crash 的具体原因了。
// 这里只是示例,实际中不要这么做,要对上传信息做加密处理
string url = "http://example.com/crash_report";
string body = GetCrashReport();
int ret = HttpRequest(url, body);
// ...
四、总结
实现了一个 Native Crash 收集的方案,解决了 Native Crash 收集过程中的重点问题,包括信号量的监听、堆栈信息的获取、堆栈信息的解析、信息的上传等,为 Native Crash 的收集和分析提供了一个基础的方案。
除了上述方案外,我们还可以利用 Android 平台提供的其他功能来收集 Native Crash 的信息,例如:
NDK_DEBUG=1
setprop debug.crash-reporting true
adb bugreport
这些功能可以帮助我们获取到 Native Crash 的详细信息,但它们都有各自的局限性。因此,在实际的开发工作中,我们需要根据自己的需要来选择合适的 Native Crash 收集方案。