发生ANR后,系统会为我们提供一些信息,便于我们分析问题,如生成trace文件,在log中打印CPU信息等。
这篇文章,我们来看看ANR发生之后,系统会提供给我们哪些信息,以及这些信息是如何采集和输出的。
系统提供的信息
系统提供给我们的信息,主要有:
-
EventLog
中会打印"am_anr"
的日志 -
MainLog
中会打印ANR
发生的进程、原因、CPU
负载等信息 -
/data/anr
路径下会生成一个trace
文件,打印出主要进程的堆栈信息 -
dropbox
会保存trace
文件和CPU
负载信息data/system/dropbox
目录
采集信息源码
发生 ANR 后,不管是哪种类型的 ANR,系统都会调用到appNotResponding
方法中,进行信息的采集工作。
这个方法所在的位置,在不同的Android版本有区别。旧版本是在AMS
中,新版本是在ProcessErrorStateRecord
类中。
我们以新版本的appNotResponding
源码为例,分步进行讲解。
-
appNotResponding
传入参数
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
String parentShortComponentName, WindowProcessController parentProcess,
boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
}
其中annotation就是shortMsg,表示ANR发生的原因。
- 先获取一次CPU信息
long anrTime = SystemClock.uptimeMillis();
if (isMonitorCpuUsage()) {
mService.updateCpuStatsNow();
}
- 设置annotation
// Store annotation here as instance above will not be hit on all paths.
setAnrAnnotation(annotation);
- 判断是否需要跳过
// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
if (mService.mAtmInternal.isShuttingDown()) {
Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
return;
} else if (isNotResponding()) {
Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
return;
} else if (isCrashing()) {
Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
return;
} else if (mApp.isKilledByAm()) {
Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
return;
} else if (mApp.isKilled()) {
Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
return;
}
有几种情况会跳过:
- 如果正在关机中
- 如果已经被标记为
notResponding
,正在处理 ANR 中 - 进程正在 crash 处理中
- 进程已经被 AMS 杀掉
- 进程已经被杀
- 设置notResponding的标记
setNotResponding(true);
- 打印am_anr的EventLog
EventLog.writeEvent(EventLogTags.AM_ANR, mApp.userId, pid, mApp.processName,
mApp.info.flags, annotation);
- 获取需要dump的所有进程id
// 先把当前进程添加到firstPids
firstPids.add(pid);
// 如果是silentAnr,则不需要dump其他进程,silentAnr主要是后台anr
isSilentAnr = isSilentAnr();
if (!isSilentAnr && !onlyDumpSelf) {
// 将parentPid加入firstPids
int parentPid = pid;
if (parentProcess != null && parentProcess.getPid() > 0) {
parentPid = parentProcess.getPid();
}
if (parentPid != pid) firstPids.add(parentPid);
// MY_PID是system_server的pid,将system_server加入firstPids
if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
final int ppid = parentPid;
// 所有进程,按lru的顺序排列
mService.mProcessList.forEachLruProcessesLOSP(false, r -> {
if (r != null && r.getThread() != null) {
int myPid = r.getPid();
if (myPid > 0 && myPid != pid && myPid != ppid && myPid != MY_PID) {
if (r.isPersistent()) {
firstPids.add(myPid); // 将persisitent进程加入firstPids
} else if (r.mServices.isTreatedLikeActivity()) {
firstPids.add(myPid); // treatedLikeActivity
} else {
lastPids.put(myPid, Boolean.TRUE); // 其他进程加入lastPids
}
}
}
});
}
}
加入firstPids的进程:
- 当前进程
- parent 进程
- system_server 进程
- 生成mainlog
StringBuilder info = new StringBuilder();
info.setLength(0);
info.append("ANR in ").append(mApp.processName);
if (activityShortComponentName != null) {
info.append(" (").append(activityShortComponentName).append(")");
}
info.append("\n");
info.append("PID: ").append(pid).append("\n");
if (annotation != null) {
info.append("Reason: ").append(annotation).append("\n");
}
if (parentShortComponentName != null
&& parentShortComponentName.equals(activityShortComponentName)) {
info.append("Parent: ").append(parentShortComponentName).append("\n");
}
if (errorId != null) {
info.append("ErrorId: ").append(errorId.toString()).append("\n");
}
info.append("Frozen: ").append(mApp.mOptRecord.isFrozen()).append("\n");
初始化一个StringBuilder对象info,用来记录输出到mainLog里的内容。
主要包含如下内容:
- ANR in xxx
- PID: xxx
- Reason: xxx (shortMsg)
- Parent: xxx (可能没有)
- ErrorId: xxx (可能没有)
- Frozen: 是否frozen
- CPU信息
- 收集需要 dump 的native pids
String[] nativeProcs = null;
if (isSilentAnr || onlyDumpSelf) {
for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
// 写死在watchdog中的native进程,主要包含audioserver、cameraserver等。
if (NATIVE_STACKS_OF_INTEREST[i].equals(mApp.processName)) {
nativeProcs = new String[] { mApp.processName };
break;
}
}
} else {
nativeProcs = NATIVE_STACKS_OF_INTEREST;
}
int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
ArrayList<Integer> nativePids = null;
if (pids != null) {
nativePids = new ArrayList<>(pids.length);
for (int i : pids) {
nativePids.add(i);
}
}
NATIVE_STACKS_OF_INTEREST
写死在WatchDog.java
文件,主要包含audioserver
、cameraserver
等。
- 开始dump trace文件
// 生成ProcessCpuTracker
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
nativePids, tracesFileException, offsets, annotation, criticalEventLog);
dump trace
文件的具体细节,以及SignalCatcher
线程监听信号,下一篇文章再详细讲。
- 再次获取CPU信息,并打印mainLog
if (isMonitorCpuUsage()) {
mService.updateCpuStatsNow();
mService.mAppProfiler.printCurrentCpuState(report, anrTime);
info.append(processCpuTracker.printCurrentLoad());
info.append(report);
}
info.append(processCpuTracker.printCurrentState(anrTime));
// 打印mainLog,tag为ActivityManager
Slog.e(TAG, info.toString());
- 保存到 dropbox
mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
parentShortComponentName, parentPr, null, report.toString(), tracesFile,
null, new Float(loadingProgress), incrementalMetrics, errorId);
把traces文件、CPU使用率等信息,保存到dropbox,即data/system/dropbox目录
- 如果是后台 ANR,直接杀进程
if (isSilentAnr() && !mApp.isDebugging()) {
mApp.killLocked("bg anr", ApplicationExitInfo.REASON_ANR, true);
return;
}
- 设置
App
的notRespondingReport
(到这里,AMS 才能查询到进程是否发生 ANR)
synchronized (mProcLock) {
// 设置AppNotRespondingReport
makeAppNotRespondingLSP(activityShortComponentName,
annotation != null ? "ANR " + annotation : "ANR", info.toString());
mDialogController.setAnrController(anrController);
}
private void makeAppNotRespondingLSP(String activity, String shortMsg, String longMsg) {
setNotResponding(true);
if (mService.mAppErrors != null) {
// 设置NotRespondingReport
mNotRespondingReport = mService.mAppErrors.generateProcessError(mApp,
ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
activity, shortMsg, longMsg, null);
}
startAppProblemLSP();
mApp.getWindowProcessController().stopFreezingActivities();
}
- 唤醒 ANR 弹窗
if (mService.mUiHandler != null) {
// 唤醒AppNotResponding弹窗
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);
mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
}
到这里,AppNotResponding
的流程就讲完了。
从AMS获取App的ErrorState
AMS提供一个public的接口,用于查询所有进程的ErrorState
。
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
synchronized (mProcLock) {
// 遍历所有进程
mProcessList.forEachLruProcessesLOSP(false, app -> {
// 获取进程的mErrorState
final ProcessErrorStateRecord errState = app.mErrorState;
final boolean crashing = errState.isCrashing();
final boolean notResponding = errState.isNotResponding();
if ((app.getThread() != null) && (crashing || notResponding)) {
ActivityManager.ProcessErrorStateInfo report = null;
if (crashing) {
// 如果是crashing,需要获取CrashingReport
report = errState.getCrashingReport();
} else if (notResponding) {
// 如果是notResponding,需要获取notRespondingReport
report = errState.getNotRespondingReport();
}
if (report != null) {
if (errList[0] == null) {
errList[0] = new ArrayList<>(1);
}
errList[0].add(report);
} else {
Slog.w(TAG, "Missing app error report, app = " + app.processName +
" crashing = " + crashing +
" notResponding = " + notResponding);
}
}
});
}
return errList[0];
}
这个方法的作用,主要是找到出现 Crash 或 ANR 的进程列表。可以通过循环调用该方法,判断进程是否发生 ANR。
不过这个判断不会很准,因为只有当发生 ANR 的进程的notRespondingReport
生成后,才会返回该进程。由前面的分析克制,生成notRespondingReport
的时机,是在dump trace
完成之后,弹出ANR弹窗之前。
以下几种情况会导致我们无法获取到进程的ErrorState
:
- 用户可能在我们调用方法之间,杀掉进程
- 对于oppo和vivo手机,发生ANR后会自动杀死进程,几乎没办法拿到
总结
当 ANR 发生时,系统会调用appNotResponding
方法,修改进程的ErrorState
状态,同时dump丰富的信息。
主要流程如下:
- 将
am_anr
信息,输出到EventLog
(这是ANR发生的起点) - 获取重要进程的trace信息,保存到
/data/anr/traces.txt
-
Java
进程:当前进程、parent
进程、system_server
、top 5的进程 -
Native
进程:audioserver
、cameraserver
等
-
- 将
ANR reason
和CPU
使用情况,输出到MainLog
- 将
CPU
使用情况及traces
文件信息,保存到/data/system/dropbox
- 如果是后台ANR,直接杀进程
- 如果是前台ANR,设置
notRespondingReport
,激活 ANR 弹窗