Android發(fā)生ANR后的信息采集過程
發(fā)生ANR后
系統(tǒng)會為我們提供一些信息,便于我們分析問題,如生成trace文件,在log中打印CPU信息等。
這篇文章,我們來看看ANR發(fā)生之后,系統(tǒng)會提供給我們哪些信息,以及這些信息是如何采集和輸出的。
系統(tǒng)提供的信息
系統(tǒng)提供給我們的信息,主要有:
EventLog
中會打印"am_anr"
的日志MainLog
中會打印ANR
發(fā)生的進程、原因、CPU
負載等信息/data/anr
路徑下會生成一個trace
文件,打印出主要進程的堆棧信息dropbox
會保存trace
文件和CPU
負載信息data/system/dropbox
目錄
采集信息源碼
發(fā)生 ANR 后,不管是哪種類型的 ANR,系統(tǒng)都會調用到appNotResponding
方法中,進行信息的采集工作。
這個方法所在的位置,在不同的Android版本有區(qū)別。舊版本是在AMS
中,新版本是在ProcessErrorStateRecord
類中。
我們以新版本的appNotResponding
源碼為例,分步進行講解。
appNotResponding
傳入參數
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo, String parentShortComponentName, WindowProcessController parentProcess, boolean aboveSystem, String annotation, boolean onlyDumpSelf) { }
其中annotation就是shortMsg,表示ANR發(fā)生的原因。
- 先獲取一次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
文件的具體細節(jié),以及SignalCatcher
線程監(jiān)聽信號,下一篇文章再詳細講。
- 再次獲取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 才能查詢到進程是否發(fā)生 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 的進程列表??梢酝ㄟ^循環(huán)調用該方法,判斷進程是否發(fā)生 ANR。
不過這個判斷不會很準,因為只有當發(fā)生 ANR 的進程的notRespondingReport
生成后,才會返回該進程。由前面的分析克制,生成notRespondingReport
的時機,是在dump trace
完成之后,彈出ANR彈窗之前。
以下幾種情況會導致我們無法獲取到進程的ErrorState
:
- 用戶可能在我們調用方法之間,殺掉進程
- 對于oppo和vivo手機,發(fā)生ANR后會自動殺死進程,幾乎沒辦法拿到
總結
當 ANR 發(fā)生時,系統(tǒng)會調用appNotResponding
方法,修改進程的ErrorState
狀態(tài),同時dump豐富的信息。
主要流程如下:
- 將
am_anr
信息,輸出到EventLog
(這是ANR發(fā)生的起點) - 獲取重要進程的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 彈窗
以上就是Android發(fā)生ANR后的信息采集過程的詳細內容,更多關于Android ANR信息采集的資料請關注腳本之家其它相關文章!
相關文章
Android ListView中動態(tài)添加RaidoButton的實例詳解
這篇文章主要介紹了Android ListView中動態(tài)添加RaidoButton的實例詳解的相關資料,需要的朋友可以參考下2017-08-08規(guī)避Android開發(fā)中內存泄漏陷阱的解決方案
在Android開發(fā)中,內存泄漏是一個常見但容易被忽視的問題,它會導致應用程序占用過多的內存資源,最終影響應用的性能和用戶體驗,本文將深入探討Android常見的內存泄漏問題,并提供優(yōu)化指南,需要的朋友可以參考下2024-05-05