Android ANR 信息收集的 "破案" 故事:系统如何追踪卡死现场

一、ANR 报警:程序卡死的 "110" 呼叫

想象 Android 系统是一个繁忙的城市,各个应用程序就像忙碌的工厂。当一个工厂(应用)超过规定时间没响应(比如前台服务超过 20 秒没干完活),就会触发 "城市报警中心"AMS(ActivityManagerService)的警报系统。

java

scss 复制代码
// AMS接到报警的第一反应
final void appNotResponding(ProcessRecord app, ...) {
    // 记录报警时间和基本信息到"城市日志"EventLog
    EventLog.writeEvent(EventLogTags.AM_ANR, app.pid, app.processName, ...);
    // 组建"专案组":先把卡死工厂和关键部门加入调查名单
    firstPids.add(app.pid); // 卡死的工厂
    firstPids.add(MY_PID); // 城市管理中心system_server
    for (ProcessRecord r : mLruProcesses) {
        if (r.persistent) firstPids.add(r.pid); // 重要的"国企"进程
        else lastPids.put(r.pid, true); // 其他"民营企业"进程
    }
}

关键比喻

  • EventLog 相当于 110 接警记录,精确记录报警时间
  • firstPids 是 "重点嫌疑名单",包括卡死应用和核心系统进程
  • lastPids 是 "普通围观群众" 名单,后续按需调查

二、现场勘查:多团队协作收集证据

1. 组建 "证据收集小组"

当 AMS 决定调查后,会启动一个 "证据收集小组",专门负责获取各个进程的 "工作记录"(堆栈信息):

java

arduino 复制代码
// 证据收集总负责人
public static File dumpStackTraces(...) {
    String tracesPath = "/data/anr/traces.txt"; // 证据档案柜
    File tracesFile = new File(tracesPath);
    tracesFile.createNewFile(); // 清空旧档案,准备新记录
    
    // 先调查"重点嫌疑名单"
    for (int pid : firstPids) {
        Process.sendSignal(pid, Process.SIGNAL_QUIT); // 给工厂发信号:交工作记录!
        Thread.sleep(200); // 等200ms收报告
    }
    
    // 再调查"Native特种部门"(如显卡、媒体部门)
    String[] nativeProcs = {"mediaserver", "surfaceflinger"};
    int[] pids = Process.getPidsForCommands(nativeProcs);
    for (int pid : pids) {
        Debug.dumpNativeBacktraceToFile(pid, tracesPath); // 用特殊工具获取Native部门记录
    }
    
    // 最后调查"CPU高负荷工厂"(前5名)
    for (ProcessCpuTracker.Stats stats : processCpuTracker.getWorkingStats()) {
        if (lastPids.containsKey(stats.pid) && numProcs < 5) {
            Process.sendSignal(stats.pid, Process.SIGNAL_QUIT); // 高负荷工厂也交记录
            numProcs++;
        }
    }
}

2. Native 部门的特殊调查手段

当需要调查 Native 进程(如显卡驱动、媒体引擎)时,系统会派出 "特种调查员"debuggerd,通过 socket 发送专门的调查命令:

c

运行

arduino 复制代码
// debugger.c中的Native调查流程
int dump_backtrace_to_file(pid_t tid, int fd) {
    int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, 0);
    // 通过socket向debuggerd发送"获取堆栈"命令
    while (read(sock_fd, buffer, sizeof(buffer)) > 0) {
        write(fd, buffer, n); // 把调查结果写入档案柜
    }
}

关键比喻

  • SIGNAL_QUIT 信号相当于给 Java 工厂发 "交工作日志" 通知
  • debuggerd 相当于特种调查队,专门处理 Native 部门的调查
  • traces.txt 是 "案件档案柜",所有证据都汇总到这里

三、证据分析:CPU 使用率与现场报告

1. CPU"工作效率" 统计

在收集堆栈的同时,系统会统计各个工厂的 "工作效率"(CPU 使用率),看看是不是因为某个工厂太繁忙导致卡死:

java

scss 复制代码
// 两次更新CPU统计,计算时间段内的效率
updateCpuStatsNow(); // 第一次记录
// 中间收集堆栈...
updateCpuStatsNow(); // 第二次记录

// 生成效率报告
String cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);
String loadInfo = processCpuTracker.printCurrentLoad();
info.append("CPU负载: ").append(loadInfo);
info.append("进程效率: ").append(cpuInfo);

2. 证据归档:重要证据存入 "证据库"

所有收集到的堆栈和 CPU 报告,最终会被存入系统的 "证据库"dropbox,方便后续分析:

java

scss 复制代码
// 把证据存入dropbox
addErrorToDropBox("anr", app, app.processName, 
                  tracesFile, cpuInfo, ...);

关键比喻

  • CPU 统计相当于工厂的 "考勤记录",查看是否有超负荷工作
  • dropbox 相当于 "中央证据库",所有重要案件证据都会存档
  • 证据包括:卡死原因(Reason)、各工厂日志(堆栈)、工作效率(CPU)

四、结案流程:根据情况决定处理方式

1. 后台工厂:直接 "停业整顿"

如果是后台不重要的工厂(用户不关心的应用),系统会直接让它 "停业整顿"(杀死进程):

java

kotlin 复制代码
// 后台且用户不关心的工厂,直接杀死
if (!showBackground && !app.isInterestingToUserLocked()) {
    app.kill("bg anr", true);
    return;
}

2. 前台工厂:弹出 "停业通知"

如果是前台重要工厂(用户正在使用的应用),系统会弹出 "停业通知"(ANR 对话框),让用户选择处理方式:

java

ini 复制代码
// 准备通知内容
Message msg = Message.obtain();
msg.what = SHOW_NOT_RESPONDING_MSG;
msg.obj = map;
map.put("app", app);
// 发送给UI线程,显示ANR对话框
mUiHandler.sendMessage(msg);

五、破案总结:ANR 调查的完整流程

  1. 报警阶段:应用超时触发 AMS 报警,记录 EventLog

  2. 组建专案组:确定重点调查对象(firstPids、lastPids)

  3. 现场勘查

    • 给 Java 工厂发 SIGNAL_QUIT 获取工作日志(堆栈)
    • 用 debuggerd 调查 Native 特种部门
    • 调查 CPU 高负荷的前 5 个工厂
  4. 效率分析:统计各工厂 CPU 使用率,分析是否超负荷

  5. 证据归档:所有证据存入 dropbox,方便后续分析

  6. 结案处理:后台工厂直接关闭,前台工厂通知用户

关键技术点对比

工厂类型 调查手段 对应命令
Java 工厂 发送 SIGNAL_QUIT kill -3 [pid]
Native 工厂 debuggerd 特殊调查 debuggerd -b [pid]

通过这个 "破案" 故事,我们可以理解 Android 系统在 ANR 发生时,如何像侦探一样收集现场证据,分析问题原因,并记录关键信息以便后续排查。整个过程的核心就是围绕 "收集各进程堆栈" 和 "分析 CPU 使用情况" 展开,最终将证据归档到 dropbox 供开发者分析。

相关推荐
BD_Marathon12 小时前
【MySQL】函数
android·数据库·mysql
西西学代码12 小时前
安卓开发---耳机的按键设置的UI实例
android·ui
maki07717 小时前
虚幻版Pico大空间VR入门教程 05 —— 原点坐标和项目优化技巧整理
android·游戏引擎·vr·虚幻·pico·htc vive·大空间
千里马学框架17 小时前
音频焦点学习之AudioFocusRequest.Builder类剖析
android·面试·智能手机·车载系统·音视频·安卓framework开发·audio
fundroid21 小时前
掌握 Compose 性能优化三步法
android·android jetpack
TeleostNaCl21 小时前
如何在 IDEA 中使用 Proguard 自动混淆 Gradle 编译的Java 项目
android·java·经验分享·kotlin·gradle·intellij-idea
旷野说1 天前
Android Studio Narwhal 3 特性
android·ide·android studio
maki0771 天前
VR大空间资料 01 —— 常用VR框架对比
android·ue5·游戏引擎·vr·虚幻·pico
xhBruce1 天前
InputReader与InputDispatcher关系 - android-15.0.0_r23
android·ims
领创工作室1 天前
安卓设备分区作用详解-测试机红米K40
android·java·linux