理解 Android ANR 的信息收集过程

基于Android 6.0源码, 分析当发生ANR时系统的处理过程

  • frameworks/base/core/java/android/os/Debug.java
  • frameworks/base/core/jni/android_os_Debug.cpp
  • system/core/libcutils/debugger.c

一. ANR场景

无论是四大组件或者进程等只要发生ANR,最终都会调用AMS.appNotResponding()方法,下面从这个方法说起。

以下场景都会触发调用AMS.appNotResponding方法:

  • Service Timeout:比如前台服务在20s内未执行完成;
  • BroadcastQueue Timeout:比如前台广播在10s内未执行完成
  • InputDispatching Timeout: 输入事件分发超时5s,包括按键和触摸事件。

二. appNotResponding处理流程

1. AMS.appNotResponding

  • final void appNotResponding(ProcessRecord app, ActivityRecord activity,
  • ActivityRecord parent, boolean aboveSystem, final String annotation) {
  • ...
  • updateCpuStatsNow(); //第一次 更新cpu统计信息
  • synchronized (this) {
  • //PowerManager.reboot() 会阻塞很长时间,因此忽略关机时的ANR
  • if (mShuttingDown) {
  • return;
  • } else if (app.notResponding) {
  • return;
  • } else if (app.crashing) {
  • return;
  • }
  • //记录ANR到EventLog
  • EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
  • app.processName, app.info.flags, annotation);
  • // 将当前进程添加到firstPids
  • firstPids.add(app.pid);
  • int parentPid = app.pid;
  • //将system_server进程添加到firstPids
  • if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
  • for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
  • ProcessRecord r = mLruProcesses.get(i);
  • if (r != null && r.thread != null) {
  • int pid = r.pid;
  • if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
  • if (r.persistent) {
  • firstPids.add(pid); //将persistent进程添加到firstPids
  • } else {
  • lastPids.put(pid, Boolean.TRUE); //其他进程添加到lastPids
  • }
  • }
  • }
  • }
  • }
  • // 记录ANR输出到main log
  • StringBuilder info = new StringBuilder();
  • info.setLength(0);
  • info.append("ANR in ").append(app.processName);
  • if (activity != null && activity.shortComponentName != null) {
  • info.append(" (").append(activity.shortComponentName).append(")");
  • }
  • info.append("\n");
  • info.append("PID: ").append(app.pid).append("\n");
  • if (annotation != null) {
  • info.append("Reason: ").append(annotation).append("\n");
  • }
  • if (parent != null && parent != activity) {
  • info.append("Parent: ").append(parent.shortComponentName).append("\n");
  • }
  • //创建CPU tracker对象
  • final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
  • //输出traces信息【见小节2】
  • File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker,
  • lastPids, NATIVE_STACKS_OF_INTEREST);
  • updateCpuStatsNow(); //第二次更新cpu统计信息
  • //记录当前各个进程的CPU使用情况
  • synchronized (mProcessCpuTracker) {
  • cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);
  • }
  • //记录当前CPU负载情况
  • info.append(processCpuTracker.printCurrentLoad());
  • info.append(cpuInfo);
  • //记录从anr时间开始的Cpu使用情况
  • info.append(processCpuTracker.printCurrentState(anrTime));
  • //输出当前ANR的reason,以及CPU使用率、负载信息
  • Slog.e(TAG, info.toString());
  • //将traces文件 和 CPU使用率信息保存到dropbox,即data/system/dropbox目录
  • addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
  • cpuInfo, tracesFile, null);
  • synchronized (this) {
  • ...
  • //后台ANR的情况, 则直接杀掉
  • if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
  • app.kill("bg anr", true);
  • return;
  • }
  • //设置app的ANR状态,病查询错误报告receiver
  • makeAppNotRespondingLocked(app,
  • activity != null ? activity.shortComponentName : null,
  • annotation != null ? "ANR " + annotation : "ANR",
  • info.toString());
  • //重命名trace文件
  • String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
  • if (tracesPath != null && tracesPath.length() != 0) {
  • //traceRenameFile = "/data/anr/traces.txt"
  • File traceRenameFile = new File(tracesPath);
  • String newTracesPath;
  • int lpos = tracesPath.lastIndexOf (".");
  • if (-1 != lpos)
  • // 新的traces文件= /data/anr/traces_进程名_当前日期.txt
  • newTracesPath = tracesPath.substring (0, lpos) + "_" + app.processName + "_" + mTraceDateFormat.format(new Date()) + tracesPath.substring (lpos);
  • else
  • newTracesPath = tracesPath + "_" + app.processName;
  • traceRenameFile.renameTo(new File(newTracesPath));
  • }
  • //弹出ANR对话框
  • Message msg = Message.obtain();
  • HashMap<String, Object> map = new HashMap<String, Object>();
  • msg.what = SHOW_NOT_RESPONDING_MSG;
  • msg.obj = map;
  • msg.arg1 = aboveSystem ? 1 : 0;
  • map.put("app", app);
  • if (activity != null) {
  • map.put("activity", activity);
  • }
  • //向ui线程发送,内容为SHOW_NOT_RESPONDING_MSG的消息
  • mUiHandler.sendMessage(msg);
  • }
  • }

当发生ANR时, 会按顺序依次执行:

  1. 输出ANR Reason信息到EventLog. 也就是说ANR触发的时间点最接近的就是EventLog中输出的am_anr信息;
  2. 收集并输出重要进程列表中的各个线程的traces信息,该方法较耗时; 【见小节2】
  3. 输出当前各个进程的CPU使用情况以及CPU负载情况;
  4. 将traces文件和 CPU使用情况信息保存到dropbox,即data/system/dropbox目录
  5. 根据进程类型,来决定直接后台杀掉,还是弹框告知用户.

ANR输出重要进程的traces信息,这些进程包含:

  • firstPids队列:第一个是ANR进程,第二个是system_server,剩余是所有persistent进程;
  • Native队列:是指/system/bin/目录的mediaserver,sdcard 以及surfaceflinger进程;
  • lastPids队列: 是指mLruProcesses中的不属于firstPids的所有进程。

2. AMS.dumpStackTraces

```java
public static File dumpStackTraces(boolean clearTraces, ArrayList firstPids,
ProcessCpuTracker processCpuTracker, SparseArray lastPids, String[] nativeProcs) {
//默认为 data/anr/traces.txt
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
if (tracesPath == null || tracesPath.length() == 0) {
return null;
}

  • File tracesFile = new File(tracesPath);
  • try {
  • //当clearTraces,则删除已存在的traces文件
  • if (clearTraces && tracesFile.exists()) tracesFile.delete();
  • //创建traces文件
  • tracesFile.createNewFile();
  • FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1);
  • } catch (IOException e) {
  • return null;
top Created with Sketch.