对应最后执行ANR记录的方法是:
ActiveServices.serviceTimeout()BroadcastQueue.AppNotResponding.run().appNotResponding()AMS.appNotRespondingViaProvider()AMS.inputDispatchingTimedOut()理解为什么会出发ANR,必须明白四大组件的启动流程。这个我们以后分析。
可以将anr目录下的文件放到电脑上进行查看 adb pull data/anr .
属性系统可以通过adb shell getprop dalvik.vm.stack-trace-file这种方式查找对应的属性值
当触发ANR之后会调用AppErrors.appNotResponding()方法
final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation) { ArrayList<Integer> firstPids = new ArrayList<Integer>(5); SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20); ... //记录ANR时间 long anrTime = SystemClock.uptimeMillis(); //更新CPU状态 if (ActivityManagerService.MONITOR_CPU_USAGE) { mService.updateCpuStatsNow(); } //特定场景下忽略ANR synchronized (mService) { if (mService.mShuttingDown) { Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation); return; } else if (app.notResponding) { Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation); return; } else if (app.crashing) { Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation); return; } } //为了防止多次对相同app的anr执行重复代码,在此处标注记录,属于上面的特定情况种的一种 app.notResponding = true; //记录ANR信息到Event Log中 EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid, app.processName, app.info.flags, annotation); //添加当前app到firstpids列表中 firstPids.add(app.pid); //如果可能添加父进程到firstpids列表种 int parentPid = app.pid; ... // 将ANR信息存在info变量中,后续打印到LOGCAT,这部分的信息会以ActivityManager为Tag打印出来,包含了ANR的进程,出现原因以及当时的CPU状态,这些对分析ANR是非常重要的信息 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"); } //将ANR信息输出到traces文件,分为两种,一种带native层信息,一种不带 ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true; String[] nativeProcs = NATIVE_STACKS_OF_INTEREST; // don't dump native PIDs for background ANRs File tracesFile = null; if (isSilentANR) { //这里返回了一个文件,这里的文件路径是:`/data/anr/traces.txt` //查找方法:adb shell getprop dalvik.vm.stack-trace-file tracesFile = mService.dumpStackTraces(true, firstPids, null, lastPids, null); } else { tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids, nativeProcs); } //再次更新CPU信息,并且输出到SystemLog中 String cpuInfo = null; if (ActivityManagerService.MONITOR_CPU_USAGE) { mService.updateCpuStatsNow(); synchronized (mService.mProcessCpuTracker) { cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime); } info.append(processCpuTracker.printCurrentLoad()); info.append(cpuInfo); } info.append(processCpuTracker.printCurrentState(anrTime)); Slog.e(TAG, info.toString()); //上面的信息已经对应的ANR信息写入/data/anr/traces.txt中 //给底层发送信号Process.SIGNAL_QUIT=3 if (tracesFile == null) { Process.sendSignal(app.pid, Process.SIGNAL_QUIT); } //将traces文件 和 CPU使用率信息保存到dropbox,即data/system/dropbox目录 //命名:system_server/system_app/data_app + type+...比如下面 //data_app_anr@1501989621992.txt.gz //data_app_crash@1501989671926.txt mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, cpuInfo, tracesFile, null); synchronized (mService) { mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid); //如果是后台ANR则直接杀掉结束 if (isSilentANR) { app.kill("bg anr", true); return; } //设置app的not响应状态,并查找errorReportReceiver makeAppNotRespondingLocked(app, activity != null ? activity.shortComponentName : null, annotation != null ? "ANR " + annotation : "ANR", info.toString()); //弹出ANR对话框 Message msg = Message.obtain(); HashMap<String, Object> map = new HashMap<String, Object>(); msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_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的消息 mService.mUiHandler.sendMessage(msg); } }我们来小节一下上面发生了什么:
立刻更新了CPU的信息 java /** 2721 cpu (total|1|6),(user|1|6),(system|1|6),(iowait|1|6),(irq|1|6),(softirq|1|6) */ public static final int CPU = 2721; 给event_log中写入值 忽略一些anr在event_log中打印am_anr的信息,这个是anr立刻发生的记录将ANR信息存在info变量中,后续打印到LOGCAT,这部分的信息会以ActivityManager为Tag打印出来,包含了ANR的进程,出现原因以及当时的CPU状态,这些对分析ANR是非常重要的信息将ANR信息输出到data/anr/traces文件没有输出到traces文件的时候,给底层发送一个rocess.SIGNAL_QUIT=3信号将traces文件 和 CPU使用率信息保存到dropbox,即data/system/dropbox目录如果是后台ANR则直接杀掉结束弹出ANR对话框小结:
收集发生anr进程的调用栈
发生anr的进程anr进程的父进程(anr进程是由于AMS生成,AMS在system_server进程中,system_server进程是anr的父进程)mLruProcesses中所有的persistent进程收集Native进程的调用栈
"/system/bin/audioserver""/system/bin/cameraserver""/system/bin/drmserver""/system/bin/mediadrmserver""/system/bin/mediaserver""/system/bin/sdcard""/system/bin/surfaceflinger""media.codec" // system/bin/mediacodec"media.extractor" // system/bin/mediaextractor"com.android.bluetooth" // Bluetooth service收集lastPids进程的stacks 收集前五名注意收集信息等待的时间
主要是通过给底层发送DEBUGGER_ACTION_DUMP_BACKTRACE来请求dump的sock_fd句柄,底层调用dump_backtraces()来获取信息,从而写入data/anr/traces.txt文件中
当发生anr的时候,距离ANR最近的时间是am_anr这个日志的时间,然后会打印各种信息有底层dump的,有进程的调用栈信息等等。最后将trances.txt写入data/system/dropbox目录下,并且重命名,规则见上文。
其中Process.sendSignal(stats.pid, Process.SIGNAL_QUIT);发出退出进程信号