理解 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]

  • ENTRY(_start)
  • mov r0, sp
  • //入口地址 【见小节1.2】
  • bl __linker_init
  • /* linker init returns the _entry address in the main image */
  • mov pc, r0
  • END(_start)

1.2 __linker_init

[-> linker.cpp]

  • extern "C" ElfW(Addr) __linker_init(void* raw_args) {
  • KernelArgumentBlock args(raw_args);
  • ElfW(Addr) linker_addr = args.getauxval(AT_BASE);
  • ...
  • //【见小节1.3】
  • ElfW(Addr) start_address = __linker_init_post_relocation(args, linker_addr);
  • return start_address;
  • }

1.3 __linker_init_post_relocation

[-> linker.cpp]

  • static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(Addr) linker_base) {
  • ...
  • // Sanitize the environment.
  • __libc_init_AT_SECURE(args);
  • // Initialize system properties
  • __system_properties_init();
  • //【见小节1.4】
  • debuggerd_init();
  • ...
  • }

1.4 debuggerd_init

[-> linker/debugger.cpp]

  • __LIBC_HIDDEN__ void debuggerd_init() {
  • struct sigaction action;
  • memset(&action, 0, sizeof(action));
  • sigemptyset(&action.sa_mask);
  • //【见小节1.5】
  • action.sa_sigaction = debuggerd_signal_handler;
  • //SA_RESTART代表中断某个syscall,则会自动重新调用该syscall
  • //SA_SIGINFO代表信号附带参数siginfo_t结构体可传送到signal_handler函数
  • action.sa_flags = SA_RESTART | SA_SIGINFO;
  • //使用备用signal栈(如果可用),以便我们能捕获栈溢出
  • action.sa_flags |= SA_ONSTACK;
  • sigaction(SIGABRT, &action, nullptr);
  • sigaction(SIGBUS, &action, nullptr);
  • sigaction(SIGFPE, &action, nullptr);
  • sigaction(SIGILL, &action, nullptr);
  • sigaction(SIGPIPE, &action, nullptr);
  • sigaction(SIGSEGV, &action, nullptr);
  • #if defined(SIGSTKFLT)
  • sigaction(SIGSTKFLT, &action, nullptr);
  • #endif
  • sigaction(SIGTRAP, &action, nullptr);
  • }

1.5 debuggerd_signal_handler

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

[-> linker/debugger.cpp]

  • static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) {
  • if (!have_siginfo(signal_number)) {
  • info = nullptr; //SA_SIGINFO标识被意外清空,则info未定义
  • }
  • //防止debuggerd无法链接时,仍可以输出一些简要signal信息
  • log_signal_summary(signal_number, info);
  • //建立于debuggerd的socket通信连接 【见小节1.6】
  • send_debuggerd_packet(info);
  • //重置信号处理函数为SIG_DFL(默认操作)
  • signal(signal_number, SIG_DFL);
  • switch (signal_number) {
  • case SIGABRT:
  • case SIGFPE:
  • case SIGPIPE:
  • #if defined(SIGSTKFLT)
  • case SIGSTKFLT:
  • #endif
  • case SIGTRAP:
  • tgkill(getpid(), gettid(), signal_number);
  • break;
  • default: // SIGILL, SIGBUS, SIGSEGV
  • break;
  • }
  • }

1.6 send_debuggerd_packet

[-> linker/debugger.cpp]

  • static void send_debuggerd_packet(siginfo_t* info) {
  • // Mutex防止多个crashing线程同一时间来来尝试跟debuggerd进行通信
  • static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
  • int ret = pthread_mutex_trylock(&crash_mutex);
  • if (ret != 0) {
  • if (ret == EBUSY) {
  • __libc_format_log(ANDROID_LOG_INFO, "libc",
  • "Another thread contacted debuggerd first; not contacting debuggerd.");
  • //等待其他线程释放该锁,从而获取该锁
  • pthread_mutex_lock(&crash_mutex);
  • }
  • return;
  • }
  • //建立与debuggerd的socket通道
  • int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM | SOCK_CLOEXEC);
  • ...
  • debugger_msg_t msg;
  • msg.action = DEBUGGER_ACTION_CRASH;
  • msg.tid = gettid();
  • msg.abort_msg_address = reinterpret_cast<uintptr_t>(g_abort_message);
  • msg.original_si_code = (info != nullptr) ? info->si_code : 0;
  • //将DEBUGGER_ACTION_CRASH消息发送给debuggerd服务端
  • ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg)));
  • if (ret == sizeof(msg)) {
  • char debuggerd_ack;
  • //阻塞等待debuggerd服务端的回应数据
  • ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1));
  • int saved_errno = errno;
  • notify_gdb_of_libraries();
  • errno = saved_errno;
  • }
  • close(s);
  • }

该方法的主要功能:

  • 调用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]

  • static int do_server() {
  • ...
  • for (;;) {
  • sockaddr_storage ss;
  • sockaddr* addrp = reinterpret_cast<sockaddr*>(&ss);
  • socklen_t alen = sizeof(ss);
  • //等待客户端连接
  • int fd = accept4(s, addrp, &alen, SOCK_CLOEXEC);
  • if (fd == -1) {
  • continue; //accept失败
  • }
  • //处理native crash发送过来的请求【见小节2.2】
  • handle_request(fd);
  • }
  • return 0;
  • }

2.2 handle_request

[-> /debuggerd/debuggerd.cpp]

  • static void handle_request(int fd) {
  • android::base::unique_fd closer(fd);
  • debugger_request_t request;
  • memset(&request, 0, sizeof(request));
  • //读取client发送过来的请求【见小节2.3】
  • int status = read_request(fd, &request);
  • ...
  • //fork子进程来处理其余请求命令
  • pid_t fork_pid = fork();
  • if (fork_pid == -1) {
  • ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
  • } else if (fork_pid == 0) {
  • //子进程执行【见小节2.4】
  • worker_process(fd, request);
  • } else {
  • //父进程执行【见小节2.5】
  • monitor_worker_process(fork_pid, request);
  • }
  • }

2.3 read_request

[-> /debuggerd/debuggerd.cpp]

  • static int read_request(int fd, debugger_request_t* out_request) {
  • ucred cr;
  • socklen_t len = sizeof(cr);
  • //从fd获取client进程的pid,uid,gid
  • int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
  • ...
  • fcntl(fd, F_SETFL, O_NONBLOCK);
  • pollfd pollfds[1];
  • pollfds[0].fd = fd;
  • pollfds[0].events = POLLIN;
  • pollfds[0].revents = 0;
  • //读取tid
  • status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000));
  • debugger_msg_t msg;
  • memset(&msg, 0, sizeof(msg));
  • //从fd读取数据并保存到结构体msg
  • status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));
  • ...
  • out_request->action = static_cast<debugger_action_t>(msg.action);
  • out_request->tid = msg.tid;
  • out_request->pid = cr.pid;
  • out_request->uid = cr.uid;
  • out_request->gid = cr.gid;
  • out_request->abort_msg_address = msg.abort_msg_address;
  • out_request->original_si_code = msg.original_si_code;
  • if (msg.action == DEBUGGER_ACTION_CRASH) {
  • // native crash时发送过来的请求
  • char buf[64];
  • struct stat s;
  • snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid);
  • if (stat(buf, &s)) {
  • return -1; //tid不存在,忽略该显式dump请求
  • }
  • } else if (cr.uid == 0
  • || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) {
  • ...
  • } else {
  • return -1;
  • }
  • return 0;
  • }

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

2.4 worker_process

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

[-> /debuggerd/debuggerd.cpp]

  • static void worker_process(int fd, debugger_request_t& request) {
  • std::string tombstone_path;
  • int tombstone_fd = -1;
  • switch (request.action) {
  • case DEBUGGER_ACTION_CRASH:
  • //打开tombstone文件
  • tombstone_fd = open_tombstone(&tombstone_path);
  • if (tombstone_fd == -1) {
  • exit(1); //无法打开tombstone文件,则退出该进程
  • }
  • break;
  • ...
  • }
  • // Attach到目标进程
  • if (ptrace(PTRACE_ATTACH, request.tid, 0, 0) != 0) {
  • exit(1); //attach失败则退出该进程
  • }
  • ...
  • //生成backtrace
  • std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));
  • int amfd = -1;
  • std::unique_ptr<std::string> amfd_data;
  • if (request.action == DEBUGGER_ACTION_CRASH) {
  • //当发生native crash,则连接到AMS【见小节2.4.1】
  • amfd = activity_manager_connect();
  • amfd_data.reset(new std::string);
  • }
  • bool succeeded = false;
  • //取消特权模式
  • if (!drop_privileges()) {
  • _exit(1); //操作失败则退出
  • }
  • int crash_signal = SIGKILL;
  • //执行dump操作,【见小节2.4.2】
  • succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings,
  • &crash_signal, amfd_data.get());
  • if (!attach_gdb) {
  • //将进程crash情况告知AMS【见小节2.4.3】
  • activity_manager_write(request.pid, crash_signal, amfd, *amfd_data.get());
  • }
  • //detach目标进程
  • ptrace(PTRACE_DETACH, request.tid, 0, 0);
  • for (pid_t sibling : siblings) {
  • ptrace(PTRACE_DETACH, sibling, 0, 0);
  • }
  • if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) {
  • //发送信号SIGKILL给目标进程[【见小节2.4.4】
  • if (!send_signal(request.pid, request.tid, crash_signal)) {
  • ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno));
  • }
  • }
  • ...
  • }

整个过程比较复杂,下面只介绍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]

  • static int activity_manager_connect() {
  • android::base::unique_fd amfd(socket(PF_UNIX, SOCK_STREAM, 0));
  • if (amfd.get() < -1) {
  • return -1; ///无法连接到ActivityManager(socket失败)
  • }
  • struct sockaddr_un address;
  • memset(&address, 0, sizeof(address));
  • address.sun_family = AF_UNIX;
  • //该路径必须匹配NativeCrashListener.java中的定义
  • strncpy(address.sun_path, "/data/system/ndebugsocket", sizeof(address.sun_path));
  • if (TEMP_FAILURE_RETRY(connect(amfd.get(), reinterpret_cast<struct sockaddr*>(&address),
  • sizeof(address))) == -1) {
  • return -1; //无法连接到ActivityManager(connect失败)
  • }
  • struct timeval tv;
  • memset(&tv, 0, sizeof(tv));
  • tv.tv_sec = 1;
  • if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
  • return -1; //无法连接到ActivityManager(setsockopt SO_SNDTIMEO失败)
  • }
  • tv.tv_sec = 3;
  • if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
  • return -1; //无法连接到ActivityManager(setsockopt SO_RCVTIMEO失败)
  • }
  • return amfd.release();
  • }

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

2.4.2 perform_dump

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

top Created with Sketch.