崩溃日志收集框架 - native crash收集流程分析

一、基础铺垫

信号机制:


整个流程理解:

程序在cpu上运行的时候发生了错误,cpu发送中断指令,程序陷入内核,内核添加信号到进程的信号队列,之后程序转到用户态,进行信号检测,检查到信号队列中有新信号,进行信号处理。

用户态信号怎么处理?

应用程序被内核加载起来后,并不是先跑程序代码,而是先跑linker,linker初始化过程target进程注册信号处理函数。而通过System.loadLibrary加载的so文件,最终由linker链接到bionic,当native程序出现异常时,kernel会发送相应signal过来,target进程捕获signal,然后与debuggerd建立socket通信,交由debuggerd来执行相关dump操作。

注:
linker:Android系统加载动态链接的链接器。
debuggerd:一个init孵化的daemon进程,主要负责将进程运行时的信息dump到文件或者控制台中。

二、native crash收集流程

代码:android 6.0

2.1 信号处理函数注册:

信号注册流程

应用程序入口begin.S通过linker初始化,调用debuggerd的debuggers_init执行信号处理函数的注册,在debuggers_signal_handler中通过sigaction,注册要接收的信号。同时通过send_debuggerd_packet建立target进程与debuggerd的socket通信连接。
注册点:

__LIBC_HIDDEN__ void debuggerd_init() {
  struct sigaction action;
  memset(&action, 0, sizeof(action));
  sigemptyset(&action.sa_mask);
  action.sa_sigaction = debuggerd_signal_handler;
  action.sa_flags = SA_RESTART | SA_SIGINFO;
 
  // Use the alternate signal stack if available so we can catch stack overflows.
  action.sa_flags |= SA_ONSTACK;
 
  sigaction(SIGABRT, &action, NULL);//调用abort函数生成的信号,表示程序异常
  sigaction(SIGBUS, &action, NULL);//非法地址,包括内存地址对齐出错,比如访问一个4字节的整数, 但其地址不是4的倍数
  sigaction(SIGFPE, &action, NULL);//计算错误,比如除0、溢出
  sigaction(SIGILL, &action, NULL);//执行了非法指令,或者试图执行数据段,堆栈溢出
  sigaction(SIGPIPE, &action, NULL);//管道破裂,通常在进程间通信产生
  sigaction(SIGSEGV, &action, NULL);//非法内存操作,与 SIGBUS不同,他是对合法地址的非法访问,    比如访问没有读权限的内存,向没有写权限的地址写数据
#if defined(SIGSTKFLT)
  sigaction(SIGSTKFLT, &action, NULL);//协处理器堆栈错误
#endif
  sigaction(SIGTRAP, &action, NULL);//断点时产生,由debugger使用
}

信号量定义:

signal.h

#define SIGHUP 1  // 终端连接结束时发出(不管正常或非正常)
#define SIGINT 2  // 程序终止(例如Ctrl-C)
#define SIGQUIT 3 // 程序退出(Ctrl-\)
#define SIGILL 4 // 执行了非法指令,或者试图执行数据段,堆栈溢出
#define SIGTRAP 5 // 断点时产生,由debugger使用
#define SIGABRT 6 // 调用abort函数生成的信号,表示程序异常
#define SIGIOT 6 // 同上,更全,IO异常也会发出
#define SIGBUS 7 // 非法地址,包括内存地址对齐出错,比如访问一个4字节的整数, 但其地址不是4的倍数
#define SIGFPE 8 // 计算错误,比如除0、溢出
#define SIGKILL 9 // 强制结束程序,具有最高优先级,本信号不能被阻塞、处理和忽略
#define SIGUSR1 10 // 未使用,保留
#define SIGSEGV 11 // 非法内存操作,与 SIGBUS不同,他是对合法地址的非法访问,    比如访问没有读权限的内存,向没有写权限的地址写数据
#define SIGUSR2 12 // 未使用,保留
#define SIGPIPE 13 // 管道破裂,通常在进程间通信产生
#define SIGALRM 14 // 定时信号,
#define SIGTERM 15 // 结束程序,类似温和的 SIGKILL,可被阻塞和处理。通常程序如    果终止不了,才会尝试SIGKILL
#define SIGSTKFLT 16  // 协处理器堆栈错误
#define SIGCHLD 17 // 子进程结束时, 父进程会收到这个信号。
#define SIGCONT 18 // 让一个停止的进程继续执行
#define SIGSTOP 19 // 停止进程,本信号不能被阻塞,处理或忽略
#define SIGTSTP 20 // 停止进程,但该信号可以被处理和忽略
#define SIGTTIN 21 // 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号
#define SIGTTOU 22 // 类似于SIGTTIN, 但在写终端时收到
#define SIGURG 23 // 有紧急数据或out-of-band数据到达socket时产生
#define SIGXCPU 24 // 超过CPU时间资源限制时发出
#define SIGXFSZ 25 // 当进程企图扩大文件以至于超过文件大小资源限制
#define SIGVTALRM 26 // 虚拟时钟信号. 类似于SIGALRM,     但是计算的是该进程占用的CPU时间.
#define SIGPROF 27 // 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间
#define SIGWINCH 28 // 窗口大小改变时发出
#define SIGIO 29 // 文件描述符准备就绪, 可以开始进行输入/输出操作
#define SIGPOLL SIGIO // 同上,别称
#define SIGPWR 30 // 电源异常
#define SIGSYS 31 // 非法的系统调用

2.2 debuggerd收集奔溃信息

当发生crash时,kernel发送相应signal给target进程,因为target进程注册过了接收信号,因此能捕获signal进行处理,因为target进程与debuggerd建立了socket连接,此时会将action = DEBUGGER_ACTION_CRASH的消息发送给debuggerd服务端,debuggerd处理流程如下图:

debuggerd收集native奔溃信息流程

debuggerd处理过程简析:

  • 接收target进程发送过来的socket请求,fork子进程来处理任务。
  • 子进程创建tombstone文件,并dump收集相关信息。包括设备基本信息、backtrace、stack、系统logcat等。
  • socket到system_server进程走crash流程。
$:/data/tombstones # ls -al
-rw-r-----  1 tombstoned system 623950 2020-06-24 00:06 tombstone_00
-rw-r-----  1 tombstoned system 630274 2020-06-24 10:52 tombstone_01

一份tombstone文件内容:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
//基本信息
Build fingerprint: 'Xiaomi/cepheus/cepheus:10/QKQ1.190825.002/9.10.29:user/release-keys'
Revision: '0'
ABI: 'arm64'
Timestamp: 2020-06-24 00:06:13+0800
pid: 27232, tid: 27232, name: crashhandlerprj  >>> com.stan.crashhandlerprj <<<
uid: 10258

//信号信息
signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
Abort message: 'FORTIFY: strcat: prevented write past end of 0-byte buffer'
    x0  0000000000000000  x1  0000000000006a60  x2  0000000000000006  x3  0000007fe28391e0
    x4  0000800000808080  x5  0000800000808080  x6  0000800000808080  x7  0000000000000018
     ...

backtrace:
      #00 pc 0000000000073430  /apex/com.android.runtime/lib64/bionic/libc.so (abort+160) (BuildId: 084c8a81b8c78e19cd9a1ff6208e77cf)
      #01 pc 0000000000099594  /apex/com.android.runtime/lib64/bionic/libc.so (__fortify_fatal(char const*, ...)+120) (BuildId: 084c8a81b8c78e19cd9a1ff6208e77cf)
      #02 pc 000000000009a390  /apex/com.android.runtime/lib64/bionic/libc.so (__strcat_chk+84) (BuildId: 084c8a81b8c78e19cd9a1ff6208e77cf)
      ...

stack:
         0000007fe2839140  0000007fe2839268  [stack]
         0000007fe2839148  0000000000000000
         0000007fe2839150  0000007fe28391b0  [stack]
         ...

memory near x1:
    0000000000006a40 ---------------- ----------------  ................
    0000000000006a50 ---------------- ----------------  ................
    0000000000006a60 ---------------- ----------------  ................
    ...

memory map (2210 entries):
    00000000'12c00000-00000000'12dbffff rw-         0    1c0000  [anon:dalvik-main space (region space)]
    00000000'12dc0000-00000000'135fffff ---         0    840000  [anon:dalvik-main space (region space)]
    00000000'13600000-00000000'13bbffff ---         0    5c0000  [anon:dalvik-main space (region space)]
   ...

//系统logcat
--------- tail end of log main
06-24 00:06:08.910 27232 27232 I FeatureParser: can't find cepheus.xml in assets/device_features/,it may be in /system/etc/device_features
06-24 00:06:08.916 27232 27232 E libc    : Access denied finding property "ro.vendor.df.effect.conflict"
06-24 00:06:08.910 27232 27232 W crashhandlerprj: type=1400 audit(0.0:512880): avc: denied { read } for name="u:object_r:vendor_displayfeature_prop:s0" dev="tmpfs" ino=28719 scontext=u:r:untrusted_app:s0:c2,c257,c512,c768
...

//fd信息
open files:
    fd 0: /dev/null (unowned)
    fd 1: /dev/null (unowned)
    fd 2: /dev/null (unowned)
    fd 3: socket:[4540882] (unowned)
    …

2.3 ActivityManagerService走应用crash流程

AMS走应用crash流程

到ActivityManagerService就是走app的死亡流程了,包括crash信息写入dropbox,然后走死亡流程:handlerAppCrashLocked函数主要工作内容包括:杀进程、同时清理进程的四大组件,最终弹出crash对话框。 这部分与上一篇文章写的java crash流程一致。

$:/data/system/dropbox # ls -al
//java crash文件
-rw-------  1 system system   963 2020-07-03 16:28 data_app_crash@1593764897731.txt

//native crash文件 有两个
-rw-------  1 system system 12826 2020-07-03 16:28 SYSTEM_TOMBSTONE@1593764922307.txt.gz
-rw-------  1 system system  2449 2020-07-03 16:28 data_app_native_crash@1593764922301.txt.gz

data_app_crash@1593764897731.txt内容:

Process: com.stan.crashhandlerprj
PID: 29409
UID: 10258
Flags: 0x30e8bf46
Package: com.stan.crashhandlerprj v1 (1.0)
Foreground: Yes
Build: Xiaomi/cepheus/cepheus:10/QKQ1.190825.002/9.10.29:user/release-keys
java.lang.ArithmeticException: divide by zero
   at com.stan.crashhandlerprj.MainActivity.onClick(MainActivity.java:39)
   at android.view.View.performClick(View.java:7160)
   at android.view.View.performClickInternal(View.java:7137)
   at android.view.View.access$3500(View.java:810)
   at android.view.View$PerformClick.run(View.java:27418)
   at android.os.Handler.handleCallback(Handler.java:883)
   at android.os.Handler.dispatchMessage(Handler.java:100)
   at android.os.Looper.loop(Looper.java:221)
   at android.app.ActivityThread.main(ActivityThread.java:7540)
   at java.lang.reflect.Method.invoke(Native Method)
   at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)

而data_app_native_crash@1593764922301.txt.gz和SYSTEM_TOMBSTONE@1593764922307.txt.gz的内容基本就是tombstone的内容。

至此native crash处理流程就分析完了。

参考:
理解Native Crash处理流程
debuggerd守护进程
Android平台Native代码的崩溃捕获机制及实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345