Android开发中,在Java层可以方便的捕获crashlog,但对于 Native 层的 crashlog 通常无法直接获取,只能通过系统的logcat来分析crash日志。

做过 Linux 和 Win32 开发的都知道,在pc上程序crash时可以生成 core dump 文件通过相关的工具分析函数调用堆栈及崩溃时的内存信息。

那么作为软件开发者有没有方法自己获取native层的crashlog呢?Android 系统是 Linux 内核,既然在Linux中crash时可以生成dump文件,那么在Android中也是有办法的。

Linux系统的Crash dump

Linux 栈调用回溯

对 Linux 应用程序而言, 因为有 glibc 库的支持, 所以构造程序的函数调用链相对容易。在 glibc 库提供的关于堆栈回朔的一系列库函数中,其核心函数是 backtrace()。它负责遍历从程序入口点到当前调用点的所有堆栈帧,然后生成函数调用的地址序列。为了完成函数地址和函数名称的转换,函数backtrace_symbols() 负责将 backtrace()生成的地址序列转换成一系列字符串列表,在每个字符串列表中包括了函数名称,当前指令在函数中的偏移量和函数的返回地址。由于 backtrace_symbols() 需要动态申请空间以保存字符串列表,如果应用程序 crash 时破坏了系统内存,可能导致 backtrace_symbols()结果错误。为此,glibc库还提供了一个更安全的地址转换函数:backtrace_symbols_fd() 。该函数将生成的字符串直接输出到外部文件,而不再需要申请新的内存空间。对于 backtrace() 的详细使用方法可以通过man backtrace 查看。

在Andrid中,由于谷歌没有使用glibc库,而是使用了精简版本的bionic库,其中并没有 backtrace() 可用。获取调用堆栈还需要采用其他方法。

Linux 信号机制

信号机制是 Linux 进程间通信的一种重要方式,Linux 信号一方面用于正常的进程间通信和同步,如任务控制(SIGINT, SIGTSTP,SIGKILL, SIGCONT,……);另一方面,它还负责监控系统异常及中断。 当应用程序运行异常时, Linux 内核将产生错误信号并通知当前进程。 当前进程在接收到该错误信号后,可以有三种不同的处理方式。 1. 忽略该信号。
2. 捕捉该信号并执行对应的信号处理函数(signal handler)。
3. 执行该信号的缺省操作(如 SIGTERM, 其缺省操作是终止进程)。

当 Linux 应用程序在执行时发生严重错误,一般会导致程序 crash。其中,Linux 专门提供了一类 crash 信号,在程序接收到此类信号时,缺省操作是将 crash 的现场信息记录到 core 文件,然后终止进程。

Crash信号列表

Signal Description
SIGSEGV Invalid memory reference.
SIGBUS Access to an undefined portion of a memory object.
SIGFPE Arithmetic operation error, like divide by zero.
SIGILL Illegal instruction, like execute garbage or a privileged instruction
SIGSYS Bad system call.
SIGXCPU CPU time limit exceeded.
SIGXFSZ File size limit exceeded.

Linux 信号处理 sigaction

#include<signal.h>
int sigaction(int sig, struct sigaction *act , struct sigaction *oact) ; struct sigaction{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}

这个函数可以: 1. 给一个signal安装一个handler,并且在使用sigaction修改该handler之前,不用reinstall。 2. 使用sigaction结构,该结构包含handler,其中可以指定2个handler,一个是使用sigiinfo_t等参数的handler,即支持给handler更多的参数,使其可以知道自己是被什么进程,那个用户,发来的什么信号,发来该信号的具体的原因是什么,当然要像这样,得给sigaction的sa_flags设置SA_SIGINFO标记。 3.使用sigaction的sa_flags标记还可以指定系统调用被这个信号打断后,是直接返回,还是自动restart. 一个典型就是,一般我们不让SIGALRM信号将被打断的系统调用restart,因为SIGALARM一般本来就是用来打断一个block的调用的。 4. 为了模仿老的signal函数的作用,实现unreliable 的类似signal的操作,可以通过给sa_flags设置SA_RESETHAND使handler不会自动reinstall,以及SA_NODEFER标记来使在本信号的handler内部,本信号不被自动block,当然如果你手动在sa_mask中指定要block本信号的话就可以将其block了。 5. 通过使用sigaction结构中的sa_mask,可以在该handler执行的过程中,block一些信号,注意,这个mask是与我们使用sigprocmask设置的mask不同的mask,这个mask的作用范围仅限于本handler函数,而且他不会将我们用sigprocmask设置的mask取消,而仅仅是在其基础上再次将一些信号block掉,当handler结束时,系统会自动将mask恢复成以前的样子,所以这个sigaction中的sa_mask只作用本信号的handler的执行时间。

一个使用 sigaction 进行信号处理的示例:

#include <signal.h>

void sig_handler_with_arg(int sig,siginfo_t *sig_info,void *unused){……}

int main(int argc,char **argv)
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = sig_handler_with_arg;
sa.sa_flags = SA_RESETHAND; sigaction(SIGSEGV, &sa, NULL);
...
}

Android tombstones 分析

Android系统中应用出现nativecrash时,会在 /data/tombstones 目录下生成 tombstone_xx 的日志文件,记录了应用crash发生时的内存、寄存器、堆栈信息等。并且通过logcat将其内容输出。

Android 4.0中tombstones处理部分的源码位于 /system/core/debuggerd 和 bonic/linker/debugger.c 中。

在 bonic/linker/debugger.c 中的 debugger_init() 中对7个Signal进行了注册处理,debugger_signal_handler作为信号处理函数。

void debugger_init()
{
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_sigaction = debugger_signal_handler;
act.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&act.sa_mask); sigaction(SIGILL, &act, NULL);
sigaction(SIGABRT, &act, NULL);
sigaction(SIGBUS, &act, NULL);
sigaction(SIGFPE, &act, NULL);
sigaction(SIGSEGV, &act, NULL);
sigaction(SIGSTKFLT, &act, NULL);
sigaction(SIGPIPE, &act, NULL);
}

debugger_signal_handler 中,通过socket client 与 /system/core/debuggerd 中的socket server进行通信,在/system/core/debuggerd中进行crash进程的分析( handle_crashing_process 函数中),生成tombstones文件(dump_crash_report 函数)。

unwind_backtrace_with_ptrace 函数获取backtrae,通过 ptrace 读取寄存器和相关内存地址。

Google Breakpad 项目

Google Breakpad 是Google开源的跨平台崩溃转储和分析模块,他支持Windows,Linux和Mac和Solaris系统,并可以编译到Android工程中。Google-breakpad的好处在于可以屏蔽了不同平台的差异,使用统一的文件格式记录和分析符号文件格式和崩溃栈信息。

在Linux系统上,google-breakpad也是通过信号机制来捕获crash,大致过程可以通过源码中的注释了解:

//    The signal flow looks like this:

//  SignalHandler (uses a global stack of ExceptionHandler objects to find
// | one to handle the signal. If the first rejects it, try
// | the second etc...)
// V
// HandleSignal ----------------------------| (clones a new process which
// | | shares an address space with
// (wait for cloned | the crashed process. This
// process) | allows us to ptrace the crashed
// | | process)
// V V
// (set signal handler to ThreadEntry (static function to bounce
// SIG_DFL and rethrow, | back into the object)
// killing the crashed |
// process) V
// DoDump (writes minidump)
// |
// V
// sys_exit

客户端中google-breakpad的使用也很简单,可以参照官方wiki的教程文档:How To Add Breakpad To Your Linux Application 。

Android平台抓取native crash log的更多相关文章

  1. android adb命令 抓取系统各种 log

    getLog.bat: adb root adb remount adb wait-for-device adb logcat -v time > C:\log.txt 在Android中不同的 ...

  2. 如何抓取崩溃的log日志

    4.手机录屏工具的推荐 Andriod:录屏大师,易录屏等等. iOS:AirPlayer,iTools. 5.如何抓取崩溃的log日志? android闪退获取日志方法: 1.下载adb工具包 2. ...

  3. 高通qxdm抓取sensor的log【学习笔记】

    高通qxdm抓取sensor的log 打开qxdm,打开设置界面,去掉其他无关的log,打开Log packets .Message packets的SNS的log 之后需要把端口打开,把端口打开之后 ...

  4. android 抓取native层奔溃

    使用android的breakpad工具 使用这个工具需要下载Breakpad的源码,然后进行编译,编译之后会生成两个工具 我们使用这两个工具来解析奔溃的位置.这里我们可以下载已经编译好的工具 下载地 ...

  5. MTK平台-抓取蓝牙log

    一.MTKLOG抓取 .在拔号键盘输入暗码 *#*##*#* 进入工模EngineerMode .在 Log and Debugging -> MTKLogger 点击开始 .MTKLog存储路 ...

  6. 抓取崩溃的log日志

    1.下载adb工具包 也就是解锁软件,如果要解锁的话,需确认有fastboot 安装jdk.sdk 2.注意事项 请确保电脑上只连接了一台手机设备(最好只连接一条USB线),同时确保手机已开启USB调 ...

  7. 抓取 USB 总线LOG

    在实际工作中经常会遇到需要分析USB报文的情形.比如对比不同厂家4G/5G模块.解决实际IP over USB传输效率低下问题. 这时候如果能抓取到 USBMOM 总线的报文将会大有裨益.毕竟所有ho ...

  8. 《React Native 精解与实战》书籍连载「Android 平台与 React Native 混合开发」

    此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...

  9. Android端抓取日志

    一.背景: ADT-Bundlee for Windows 是由GoogleAndroid官方提供的集成式IDE,已经包含了Eclipse,你无需再去下载Eclipse,并且里面已集成了插件,它解决大 ...

随机推荐

  1. 飘逸的python - hack输出流便于调试

    当项目有很多文件时,要找出控制台的输出是在哪里print出来的很麻烦,不过这事对于强大的python来说小菜一碟. 先上代码和效果,再说明. import sys,traceback class my ...

  2. date命令小结

    date命令是查看日期时间的常用命令,date MMDDhhmmYY.ss(修改顺序)用来更改时间 linux时间分为系统时间和硬件时间, [root@www doc]# clock--------- ...

  3. MATLAB快速注释方法

    觉得有用,未免以后忘记,收藏了.原文来自:http://i.azpala.com/2008/09/18/matlab-multi-line-comment/ A. %{ 若干语句 %} B. 多行注释 ...

  4. 细讲encodeURI和encodeURIComponent以及escape的区别与应用

    首先,我们都知道这三个东西都是用来编码的 先来说encodeURI()和encodeURIComponent() 这两个是在转换url时候用来编码解码用的. 有编码就会有解码, 解码就是decodeU ...

  5. [MSDN] GROUP BY (Transact-SQL)

    来源: https://msdn.microsoft.com/zh-cn/library/ms177673(v=sql.110).aspx 按 SQL Server 中一个或多个列或表达式的值将一组选 ...

  6. Ubuntu eclipse 命令补全失效 (转载)

    我的eclipse 3.4,从ibm网站上下载解压后使用.发觉自动补全功能(alt + /)失效. 解决的办法: 1.(eclipse)window --> preferences --> ...

  7. SAR ADC : 逐次逼近寄存器型(SAR)模数转换器(ADC)

    1.为实现二进制搜索算法,N位寄存器首先设置在中间刻度(即:100... .00,MSB设置为1).这样,DAC输出(VDAC)被设为VREF/2,VREF是提供给ADC的基准电压.然后,比较判断VI ...

  8. _ConnectionPtr.CreateInstance(__uuidof(Connection))“不支持此接口”错误解决

    最近在换了win7 64位的系统,今天突然发现以前写的ADO连接数据库的代码编译后在windows2003下会执行到: _ConnectionPtr.CreateInstance(__uuidof(C ...

  9. 实现div中图片的滚动

    今日闲来无事自己写了个图片滚动: 源码: <html><head> <meta charset="utf-8"/> <script typ ...

  10. easyui datagrid detailview嵌套datagrid的问题

    解决问题办法来自 http://www.coding123.net/article/20141113/easyui-datagrid-datailview-use-sub-datagrid-not-a ...