Android 7系统异常问题排查(三)Native层—Tombstone机制深度解析 系列目录第一篇异常机制全景图 | 第二篇Kernel Panic 与系统重启 | 第三篇Tombstone 机制 | 第四篇System Server Watchdog | 第五篇System Server 崩溃 | 第六篇ANR 机制 | 第七篇Java 层崩溃 | 第八篇Trace 机制 | 第九篇日志系统 | 第十篇实战方法论一、什么是 Tombstone你可能遇到过这些场景某个 native 进程悄无声息地消失只在/data/tombstones/留下一个二进制文件logcat 中出现Fatal signal 11 (SIGSEGV)但不知道崩溃发生在哪一行代码系统服务如 surfaceflinger、mediaserver突然重启需要定位崩溃原因Tombstone墓碑是 Native 进程因致命信号SIGSEGV、SIGABRT 等崩溃时由debuggerd守护进程自动生成的现场快照文件。存放在/data/tombstones/文件名tombstone_00到tombstone_09最多保留 10 个循环覆盖。Tombstone 包含崩溃瞬间的完整快照进程 PID/TID、信号信息、全寄存器、各线程调用栈、内存映射表、栈内存 dump、logcat 片段。二、Tombstone 生成流程整个流程涉及三个角色内核、debuggerd daemon、目标进程。目标进程发生致命错误 → 内核发信号(SIGSEGV等) → Bionic libc 信号处理器介入 → 连接 debuggerd daemon socket发送 PID/TID/UID → debuggerd fork 子进程 → ptrace 附加目标进程 → 收集寄存器、maps、backtrace、栈内存、logcat → 写入 /data/tombstones/tombstone_XX dropbox → ptrace detach → 目标进程被内核终止关键源码路径文件功能system/core/debuggerd/debuggerd.cpp主 daemonsocket 监听与请求分发system/core/debuggerd/backtrace.cpp调用栈收集bionic/libc/bionic/debugger.cppdebuggerd 客户端信号处理器system/core/libbacktrace/Unwind 库支持 ARM/ARM64/x86三、信号处理器的巧妙设计AOSP 7 中 Bionic libc 为致命信号注册了信号处理器当进程收到 SIGSEGV 等信号时会主动连接 debuggerd源码路径bionic/libc/bionic/debugger.cpp// 简化的信号处理流程staticvoiddebuggerd_signal_handler(intsignal_number,siginfo_t*info,void*context){// 1. 创建 socket 连接到 debuggerd daemonintsocket_fdsocket_local_client(debuggerd,...);// 2. 发送 crash 信息结构体debugger_msg_t msg;msg.actionDEBUGGER_ACTION_CRASH;msg.tidgettid();msg.abort_msg_address...;TEMP_FAILURE_RETRY(write(socket_fd,msg,sizeof(msg)));// 3. 进入阻塞等待 → debuggerd 通过 ptrace 控制本进程进行数据收集charack;TEMP_FAILURE_RETRY(read(socket_fd,ack,1));// 4. 信号处理器返回 → 内核重新发送信号 → 进程终止}关键设计信号处理器不处理信号而是主动将进程献祭给 debuggerd让它在进程真正死亡前完成现场采集。这种设计避免了在信号处理器中执行复杂的 dump 操作。四、debuggerd daemon 源码解析daemon 启动源码路径system/core/rootdir/init.rcservice debuggerd /system/bin/debuggerd class main主循环源码路径system/core/debuggerd/debuggerd.cppstaticintdo_server(){// 创建 socket 监听intssocket_local_server(SOCKET_NAME,...);// 启动 signal sender 子进程用于向目标进程发送信号start_signal_sender();ALOGI(debuggerd: starting\n);// 主循环等待连接for(;;){intfdaccept4(s,addrp,alen,SOCK_CLOEXEC);handle_request(fd);// 处理每个请求}}关键设计debuggerd 采用 fork-per-request 模型每个崩溃请求都由独立的子进程处理避免一个崩溃影响其他崩溃的处理。handle_request 处理流程源码路径system/core/debuggerd/debuggerd.cppstaticvoidhandle_request(intfd){debugger_request_t request;read_request(fd,request);// 读取 PID/TID/UID// 64位系统需要区分 32位/64位 进程if(is32bit(request.tid)){redirect_to_32(fd,request);// 转发给 32位 debuggerdreturn;}// fork 子进程处理pid_t fork_pidfork();if(fork_pid0){worker_process(fd,request);// 子进程实际处理}else{monitor_worker_process(fork_pid,request);// 父进程监控超时}}worker_process 核心处理源码路径system/core/debuggerd/debuggerd.cppstaticvoidworker_process(intfd,debugger_request_trequest){// 1. 打开 tombstone 文件inttombstone_fdopen_tombstone(tombstone_path);// 2. ptrace 附加目标进程ptrace_attach_thread(request.pid,request.tid);// 3. 附加所有兄弟线程ptrace_siblings(request.pid,request.tid,siblings);// 4. 创建 BacktraceMapBacktraceMap*backtrace_mapBacktraceMap::Create(request.pid);// 5. 连接 Activity Manager通知崩溃intamfdactivity_manager_connect();// 6. 降级权限drop_privileges();// 7. 执行 dumpperform_dump(request,fd,tombstone_fd,backtrace_map,siblings,...);// 8. 通知 Activity Manageractivity_manager_write(request.pid,crash_signal,amfd,amfd_data);// 9. detach 并让目标进程继续会被内核杀死ptrace(PTRACE_DETACH,request.tid,0,0);send_signal(request.pid,request.tid,crash_signal);}关键设计worker_process 在完成所有需要权限的操作ptrace attach、连接 AMS后会主动降级权限drop_privileges减少安全风险。五、调用栈收集源码路径system/core/debuggerd/backtrace.cppvoiddump_backtrace(intfd,BacktraceMap*map,pid_t pid,pid_t tid,conststd::setpid_tsiblings,std::string*amfd_data){log_t log;log.tfdfd;dump_process_header(log,pid);// 输出 PID、时间、ABIdump_thread(log,map,pid,tid);// 输出崩溃线程堆栈// 输出所有兄弟线程堆栈for(pid_t sibling:siblings){dump_thread(log,map,pid,sibling);}dump_process_footer(log,pid);}staticvoiddump_thread(log_t*log,BacktraceMap*map,pid_t pid,pid_t tid){// 获取线程名charpath[PATH_MAX];snprintf(path,sizeof(path),/proc/%d/comm,tid);// ...// 创建 Backtrace 对象并 unwindstd::unique_ptrBacktracebacktrace(Backtrace::Create(pid,tid,map));if(backtrace-Unwind(0)){dump_backtrace_to_log(backtrace.get(),log, );}}关键设计Backtrace::Create 会根据目标进程的架构ARM/ARM64/x86选择合适的 unwind 方式通过读取/proc/pid/maps获取内存映射信息。Unwind 方式方法原理前提条件基于帧指针 (FP)通过 R11(ARM)/x29(ARM64) 遍历栈帧链表-fno-omit-frame-pointer基于 .ARM.exidxARM 专用异常索引表ARM 架构默认基于 .eh_frame解析 ELF 中的 DWARF 调试信息-funwind-tables六、Tombstone 文件格式逐段精读6.1 Build FingerprintBuild fingerprint: Android/aosp_arm64/generic:7.0/... ABI: arm64 pid: 12345, tid: 12345, name: surfaceflinger /system/bin/surfaceflinger signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0signal 11SIGSEGV 信号code 1 (SEGV_MAPERR)访问了未映射的地址fault addr 0x0空指针访问6.2 寄存器 DumpARM64x0 0000000000000000 x1 0000007f8c3a4b10 ... sp 0000007f8ca0f000 lr 0000007f8c3a4b40 pc 0000007f8c3a4b50关键寄存器寄存器定位价值x0第一个参数x00 可能表示传入空指针sp栈指针当前线程栈顶lr返回地址Link Registerpc崩溃发生时的精确指令地址最重要6.3 Backtrace#00 pc 0000000000004b50 /system/lib64/libsurfaceflinger.so (MyClass::process32) #01 pc 0000000000004c20 /system/lib64/libsurfaceflinger.so (handleMessage64) #02 pc 0000000000012340 /system/lib64/libutils.so (Looper::pollInner156)格式#帧号 pc 偏移(相对基址) 库路径 (函数名函数内偏移)6.4 Memory Map0000007f8c000000-0000007f8c3a5000 /system/lib64/libsurfaceflinger.so配合 backtrace 偏移可计算实际地址基址 0x7f8c000000 偏移 0x4b50 实际地址 0x7f8c004b50七、Tombstone 还原工具addr2line —— 最基础aarch64-linux-android-addr2line-elibsurfaceflinger.so-f-C0x4b50ndk-stack —— 最常用adb logcat|ndk-stack-sym./symbols/ ndk-stack-sym./symbols/-dumptombstone_00AOSP 自带 stack 脚本adb logcat-d|./development/scripts/stack ./development/scripts/stacktombstone_00objdump 反汇编辅助aarch64-linux-android-objdump-d-Clibsurfaceflinger.so|grep-A30MyClass::process八、常见 Native Crash 速查表信号编号含义常见原因SIGSEGV11段错误空指针、野指针、缓冲区溢出、栈溢出SIGABRT6异常终止abort()、assert() 失败、__builtin_trap()SIGBUS7总线错误未对齐访问、mmap 失败后访问SIGILL4非法指令执行数据段、CPU 不支持该指令SIGFPE8浮点异常除零、整数溢出SIGSYS31非法系统调用seccomp 过滤SIGSEGV 两种 si_codesi_code含义定位方向SEGV_MAPERR (1)地址未映射空指针、已释放内存SEGV_ACCERR (2)权限错误写只读内存、执行数据段九、实战定位流程1. adb pull /data/tombstones/tombstone_00 2. 查看 signal 和 fault addr确定崩溃类型 3. 查看 backtrace #00找到崩溃源头的函数名和偏移 4. 找到对应 .so 的带符号版本out/.../symbols/system/lib64/xxx.so 5. ndk-stack -sym ./symbols/ -dump tombstone_00 还原完整堆栈 6. 定位到源码行分析空指针/非法访问的根因 7. objdump 反汇编确认必要时十、总结Tombstone 是 Native 崩溃的法医报告记录了进程死亡瞬间的所有关键状态。debuggerd 的 ptrace 机制使其无需侵入目标进程通过内核的 ptrace 能力旁观式采集数据。PC 寄存器 backtrace 是定位的黄金组合PC 告诉我们在哪条指令崩的backtrace 告诉我们怎么走到这里的。还原工具链addr2line精确 ndk-stack便捷 stack 脚本快速。信号类型决定排查方向SIGSEGV 找空指针/野指针SIGABRT 找主动 abortSIGBUS 找对齐问题。下一篇我们将进入 Framework 层深入分析System Server Watchdog机制——当系统卡死时Watchdog 如何检测并触发系统重启。本文基于 AOSP 7Android Nougat源码编写。后续版本8.0引入了 crash_dump 进程替代 debuggerd 子进程模式机制有所不同但思路一致。