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. html5lib-python doc

    http://html5lib.readthedocs.org/en/latest/ By default, the document will be an xml.etree element ins ...

  2. JSP九大内置对象和四种属性范围解读

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文首先主要解说了JSP中四种属性范围的概念.用法与实例. 然后在这个基础之上又引入了九 ...

  3. BOOST::Signals2

    /* Andy is going to hold a concert while the time is not decided. Eric is a fans of Andy who doesn't ...

  4. Firebase远程更新应用

    能打造出色的应用不意味着一定能在商业上取得成功,两者之间还有许多工作要做,绝不能简单发布应用后就宣告“收工”.您需要能迅速根据用户反馈作出调整.测试新功能,以及向用户提供他们最关注的内容. Fireb ...

  5. NET中级课--文件,流,序列化1

    1.对于机器的角度来看,任何文件都是二进制的0和1. 2.   位:bit,一个1或0就是1位. 字节:byte,每8位一个字节.一个字节的范围就是00000000到1111111,换成10进制就是0 ...

  6. Android Studio 导入Eclipse工程

    eclipse:workspace对应多个project:而android studio是project对应多个module:故,在android studio中的工程project实际上是eclip ...

  7. 文摘:威胁建模(STRIDE方法)

    文摘,原文地址:https://msdn.microsoft.com/zh-cn/magazine/cc163519.aspx 威胁建模的本质:尽管通常我们无法证明给定的设计是安全的,但我们可以从自己 ...

  8. [Python]打开文件的模式

    Python中以sys.open()方法打开文件 import sys file = open("D:\\file.txt") 其中可在第二个参数的位置指定打开文件的模式 impo ...

  9. CDZSC_2015寒假新人(2)——数学 P

    P - P Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status ...

  10. c中关于#与##的简易使用

    #运算符用于在预编译时,将宏参数转换为字符串 eg. #include <stdio.h>#define CONVERT(f)(#f) void helloworld(){ printf( ...