理解 Native Crash处理流程

本文是基于Android 7.0源码,来分析Native Crash流程。

一、Native Crash

从系统全局来说,Crash分为Framework/App Crash, Native Crash,以及Kernel Crash。

  • 对于framework层或者app层的Crash(即Java层面Crash),那么往往是通过抛出未捕获异常而导致的Crash,这个内容在本文的姊妹篇理解Android Crash处理流程已详细介绍过。
  • 至于Kernel Crash,很多情况是发生Kernel panic,对于内核崩溃往往是驱动或者硬件出现故障。
  • Native Crash,即C/C++层面的Crash,这是介于系统framework层与Linux层之间的一层,这是本文接下来要讲解的内容。

如果你是从事Android系统开发或者架构相关工作,或者遇到需要解系统性的疑难杂症,再或者需要写JNI代码,则就有可能遇到Native Crash,了解系统Native Crash处理流程就很有必要。

接下来介绍介绍Android N的Native Crash处理流程,你没有看错,本文就是针对最新Android Nouget来分析的。Native crash的工作核心是由debuggerd守护进程来完成,在文章调试系列4:debuggerd源码篇),已经介绍过Debuggerdd的工作原理。

要了解Native Crash,首先从应用程序入口位于begin.S中的__linker_init入手。

1.1 begin.S

[-> arch/arm/begin.S]

1.2 __linker_init

[-> linker.cpp]

1.3 __linker_init_post_relocation

[-> linker.cpp]

1.4 debuggerd_init

[-> linker/debugger.cpp]

1.5 debuggerd_signal_handler

连接到bionic上的native程序(C/C++)出现异常时,kernel会发送相应的signal; 当进程捕获致命的signal,通知debuggerd调用ptrace来获取有价值的信息(发生crash之前)。

[-> linker/debugger.cpp]

1.6 send_debuggerd_packet

[-> linker/debugger.cpp]

该方法的主要功能:

  • 调用socket_abstract_client,建立于debuggerd的socket通道;
  • action = DEBUGGER_ACTION_CRASH的消息发送给debuggerd服务端;
  • 阻塞等待debuggerd服务端的回应数据。

接下来,看看debuggerd服务端接收到DEBUGGER_ACTION_CRASH的处理流程

二、debuggerd服务端

debuggerd 守护进程启动后,一直在等待socket client的连接。当native crash发送后便会向debuggerd发送action = DEBUGGER_ACTION_CRASH的消息。

2.1 do_server

[-> /debuggerd/debuggerd.cpp]

2.2 handle_request

[-> /debuggerd/debuggerd.cpp]

2.3 read_request

[-> /debuggerd/debuggerd.cpp]

read_request执行完成后,则从socket通道中读取到out_request。

2.4 worker_process

处于client发送过来的请求,server端通过子进程来处理

[-> /debuggerd/debuggerd.cpp]

整个过程比较复杂,下面只介绍attach_gdb=false的执行流程:

  1. 当DEBUGGER_ACTION_CRASH ,则调用open_tombstone并继续执行;
  2. 调用ptrace方法attach到目标进程;
  3. 调用BacktraceMap::Create来生成backtrace;
  4. 当DEBUGGER_ACTION_CRASH,则执行activity_manager_connect;
  5. 调用drop_privileges来取消特权模式;
  6. 通过perform_dump执行dump操作;
    • SIGBUS等致命信号,则调用engrave_tombstone(),这是核心方法
  7. 调用activity_manager_write,将进程crash情况告知AMS;
  8. 调用ptrace方法detach到目标进程;
  9. 当DEBUGGER_ACTION_CRASH,发送信号SIGKILL给目标进程tid

2.4.1 activity_manager_connect

[-> debuggerd.cpp]

该方法的功能是建立跟上层ActivityManager的socket连接。对于”/data/system/ndebugsocket”的服务端是在,NativeCrashListener.java方法中创建并启动的。

2.4.2 perform_dump

根据接收到不同的signal采取相应的操作

[-> debuggerd.cpp]

对于以下信号都是致命的信号:

  • SIGABRT:abort退出异常
  • SIGBUS:硬件访问异常
  • SIGFPE:浮点运算异常
  • SIGILL:非法指令异常
  • SIGSEGV:内存访问异常
  • SIGSTKFLT:协处理器栈异常
  • SIGTRAP:陷阱异常

另外,上篇文章已介绍过engrave_tombstone的功能内容,这里就不再累赘了。

2.4.3 activity_manager_write

[-> debuggerd.cpp]

debuggerd与AMS的NativeCrashListener建立socket连接后,再通过该方法发送数据,数据项包括pid、signal、dump信息。

2.4.4 send_signal

此处只是向目标进程发送SIGKILL信号,用于杀掉目标进程,文章理解杀进程的实现原理已详细讲述过发送SIGKILL信号的处理流程。

2.5 monitor_worker_process

该方法是运行在debuggerd父进程,用于监控子进程的执行情况。

2.6 小结

debuggerd服务端调用链:

handle_request方法中通过fork机制,创建子进程来执行worker_process,由于fork返回两次,返回到父进程则执行monitor_worker_process。

三、NativeCrashListener

3.1 startOtherServices

[-> SystemServer.java]

当开机过程中启动服务启动到阶段PHASE_ACTIVITY_MANAGER_READY(550),即服务可以广播自己的Intents,然后启动native crash的监听进程。

3.2 startObservingNativeCrashes

[-> ActivityManagerService.java]

NativeCrashListener继承于Thread,可见这是线程,通过调用start方法来启动线程开始工作。

3.3 NativeCrashListener

[-> NativeCrashListener.java]

该方法主要功能:

  1. 创建socket服务端:”/data/system/ndebugsocket”文件权限700,owned为system:system,debuggerd是以root权限运行,因此可以与该socket建立连接,但对于第三方App则没有权限;
  2. 等待socket客户端(即debuggerd)来建立连接;
  3. 调用consumeNativeCrashData来处理native crash信息;
  4. 应答debuggerd已经建立连接,并写入应答消息告知debuggerd进程。

3.4 consumeNativeCrashData

[-> NativeCrashListener.java]

读取debuggerd那端发送过来的数据,再通过NativeCrashReporter来把native crash事件报告给framework层。

3.5 NativeCrashReporter

[-> NativeCrashListener.java]

不论是Native crash还是framework crash最终都会调用到handleApplicationCrashInner(),该方法见文章理解Android Crash处理流程

3.6 小结

system_server进程启动过程中,调用startOtherServices来启动各种其他系统Service时,也正是这个时机会创建一个用于监听native crash事件的NativeCrashListener对象(继承于线程),通过socket机制来监听,等待即debuggerd与该线程创建连接,并处理相应事件。紧接着调用handleApplicationCrashInner来处理crash流程。

NativeCrashListener的主要工作:

  1. 创建socket服务端”/data/system/ndebugsocket”
  2. 等待socket客户端(即debuggerd)来建立连接;
  3. 调用consumeNativeCrashData来处理native crash信息;
  4. 应答debuggerd已经建立连接,并写入应答消息告知debuggerd进程。

四、总结

点击查看大图

native_crash

Native程序通过link连接后,当发生Native Crash时,则kernel会发送相应的signal,当进程捕获致命的signal,通知debuggerd调用ptrace来获取有价值的信息(这是发生在crash前)。

  1. kernel 发送signal给target进程(包含native代码);
  2. target进程通过debuggerd_signal_handler,捕获signal;
    • 建立于debuggerd进程的socket通道;
    • 将action = DEBUGGER_ACTION_CRASH的消息发送给debuggerd服务端;
    • 阻塞等待debuggerd服务端的回应数据。
  3. debuggerd作为守护进程,一直在等待socket client的连接,此时收到action = DEBUGGER_ACTION_CRASH的消息;
  4. 执行到handle_request时,通过fork创建子进程来执行各种dump相关操作;
  5. 新创建的进程,通过socket与system_server进程中的NativeCrashListener线程建立socket通道,并向其发送native crash信息;
  6. NativeCrashListener线程通过创建新的名为“NativeCrashReport”的子线程来执行AMS的handleApplicationCrashInner方法。

这个流程图只是从整体来概要介绍native crash流程,其中有两个部分是核心方法:

  • 其一是图中红色块perform_dump是整个debuggerd的核心工作,该方法内部调用engrave_tombstone,该方法的具体工作见文章调试系列4:debuggerd源码篇的功能内容,这个过程还需要与target进程通信来获取target进程更多信息。
  • 其二是AMS的handleApplicationCrashInner,该方法的工作见姊妹篇理解Android Crash处理流程

作者微博:@Gityuan

原文出处:Gityuan.com

1 收藏 1 评论

关于作者:gityuan

Android系统工程师,www.gityuan.com博主,个人新浪微博:http://weibo.com/gityuan 个人主页 · 我的文章 · 3 ·     

相关文章

可能感兴趣的话题



直接登录
最新评论
跳到底部
返回顶部