關鍵類 | 路徑(/frameworks/base/) |
---|---|
ActiveServices.java | services/core/java/com/android/server/am/ActiveServices.java |
ActivityManagerService.java | services/core/java/com/android/server/am/ActivityManagerService.java |
AppErrors.java | services/core/java/com/android/server/am/AppErrors.java |
ANR(Application Not Responding),應用程序無響應,簡單一個定義,卻涵蓋了不少 Android 系統的設計思想。java
首先,ANR 屬於應用程序的範疇,這不一樣於 SNR(System Not Respoding),SNR 反映的問題是系統進程(system_server)失去了響應能力,而 ANR 明確將問題圈定在應用程序。SNR 由 Watchdog 機制保證,ANR 由消息處理機制保證
,Android 在系統層實現了一套精密的機制來發現 ANR,核心原理是消息調度
和超時處理
。android
其次,ANR 機制主體實如今系統層
。全部與 ANR 相關的消息,都會通過系統進程(system_server)調度,而後派發到應用進程完成對消息的實際處理,同時,系統進程設計了不一樣的超時限制來跟蹤消息的處理。一旦應用程序處理消息不當,超時限制就起做用了,它收集一些系統狀態,例如:CPU/IO使用狀況、進程函數調用棧,而且報告用戶有進程無響應了(ANR 對話框)
。app
而後,ANR 問題本質是一個性能問題
。ANR 機制實際上對應用程序主線程的限制,要求主線程在限定的時間內處理完一些最多見的操做(啓動服務、處理廣播、處理輸入),若是處理超時,則認爲主線程已經失去了響應其餘操做的能力。主線程中的耗時操做
,例如:密集CPU運算、大量IO、複雜界面佈局等,都會下降應用程序的響應能力。ide
最後,部分 ANR 問題是很難分析的,有時候因爲系統底層的一些影響,致使消息調度失敗,出現問題的場景又難以復現。這類 ANR 問題每每須要花費大量的時間去了解系統的一些行爲,超出了 ANR 機制自己的範疇。函數
分析一些初級的 ANR 問題,只須要簡單理解最終輸出的日誌便可,但對於一些由系統問題(例如:CPU 負載太高、進程卡死)引起的 ANR,就須要對整個 ANR 機制有所瞭解,才能定位出問題的緣由。佈局
ANR 機制能夠分爲兩部分:post
✎ ANR 的監測:Android 對於不一樣的 ANR 類型(Broadcast,Service,InputEvent)都有一套監測機制。性能
✎ ANR 的報告:在監測到 ANR 之後,須要顯示 ANR 對話框、輸出日誌(發生 ANR 時的進程函數調用棧、CPU 使用狀況等)。ui
前面咱們說過,出現 ANR 以後一個直觀現象就是系統會展現出一個 ANR 對話框。this
谷歌文檔中對 ANR 產生的緣由是這麼描述的:
Android 系統中的應用被 ActivityManagerService
及 WindowManagerService
兩個系統服務監控着,系統會在以下兩種狀況展現出 ANR 的對話框!
✎ KeyDispatchTimeout ( 5 seconds ) :按鍵或觸摸事件在特定時間內無響應
✎ BroadcastTimeout ( 10 seconds ):BroadcastReceiver 在特定時間內沒法處理完成
✎ ServiceTimeout ( 20 seconds ) :Service 在特定的時間內沒法處理完成
Service 運行在應用程序的主線程,若是 Service 的執行時間超過 20 秒,則會引起 ANR。
當發生 Service ANR 時,通常能夠先排查一下在 Service 的生命週期函數中有沒有作耗時的操做
,例如複雜的運算、IO 操做等。若是應用程序的代碼邏輯查不出問題,就須要深刻檢查當前系統的狀態:CPU 的使用狀況、系統服務的狀態等,判斷當時發生 ANR 進程是否受到系統運行異常
的影響。
那麼,系統是如何檢測 Service 超時的呢?Android 是經過設置定時消息實現的。定時消息是由 AMS 的消息隊列處理的,AMS 有 Service 運行的上下文信息,因此在 AMS 中設置一套超時檢測機制也是合情合理的。
Service ANR 機制相對最爲簡單,主體實如今ActiveServices
中。
在 Service 的啓動流程中,Service 進程 attach 到 system_server 進程後會調用 realStartServiceLocked()
方法。
// frameworks/base/services/core/java/com/android/server/am/ActiveServices.java public final class ActiveServices { private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { // 發送 delay 消息(SERVICE_TIMEOUT_MSG) bumpServiceExecutingLocked(r, execInFg, "create"); boolean created = false; try { // 最終執行服務的 onCreate() 方法 app.thread.scheduleCreateService(r, r.serviceInfo, mAm. compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState); ... ... } } }
private final void bumpServiceExecutingLocked(...) { scheduleServiceTimeoutLocked(r.app); }
void scheduleServiceTimeoutLocked(ProcessRecord proc) { if (proc.executingServices.size() == 0 || proc.thread == null) { return; } Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_TIMEOUT_MSG); msg.obj = proc; // 當超時後仍沒有 remove 該 SERVICE_TIMEOUT_MSG 消息, // 經過 AMS.MainHandler 拋出一個定時消息。 mAm.mHandler.sendMessageDelayed(msg, proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT); }
上述方法經過 AMS.MainHandler
拋出一個定時消息 SERVICE_TIMEOUT_MSG
。
前臺進程中執行 Service,超時時間是 SERVICE_TIMEOUT(20 秒)
// How long we wait for a service to finish executing. static final int SERVICE_TIMEOUT = 20*1000;
後臺進程中執行 Service,超時時間是 SERVICE_BACKGROUND_TIMEOUT(200 秒)
// How long we wait for a service to finish executing. static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
當 Service 的生命週期結束時(不會 ANR),會調用 serviceDoneExecutingLocked()
方法,以前拋出的 SERVICE_TIMEOUT_MSG
消息在這個方法中會被清除。
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) { boolean inDestroying = mDestroyingServices.contains(r); if (r != null) { ... ... serviceDoneExecutingLocked(r, inDestroying, inDestroying); } } private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) { ... ... if (r.executeNesting <= 0) { if (r.app != null) { ... ... // 當前服務所在進程中沒有正在執行的service,清除 SERVICE_TIMEOUT_MSG 消息 if (r.app.executingServices.size() == 0) { mAm.mHandler.removeMessages( ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app); ... ... } ... ... }
若是沒有 Remove 掉 SERVICE_TIMEOUT_MSG 呢?接下來咱們看看對於 ANR 的處理邏輯。
在 system_server 進程中有一個 Handler 線程,名叫 ActivityManager
。
若是在超時時間內,SERVICE_TIMEOUT_MSG
沒有被清除,便會向該 Handler
線程發送一條信息 SERVICE_TIMEOUT_MSG
。
final class MainHandler extends Handler { ... ... @Override public void handleMessage(Message msg) { switch (msg.what) { ... ... case SERVICE_TIMEOUT_MSG: { mServices.serviceTimeout((ProcessRecord)msg.obj); } break; ... ... } }
void serviceTimeout(ProcessRecord proc) { String anrMessage = null; synchronized(mAm) { ... ... long nextTime = 0; // 尋找運行超時的 Service for (int i = proc.executingServices.size() - 1; i >= 0; i--) { ServiceRecord sr = proc.executingServices.valueAt(i); if (sr.executingStart < maxTime) { timeout = sr; break; } if (sr.executingStart > nextTime) { nextTime = sr.executingStart; } } // 判斷執行 Service 超時的進程是否在最近運行進程列表,若是不在,則忽略這個 ANR if (timeout != null && mAm.mLruProcesses.contains(proc)) { Slog.w(TAG, "Timeout executing service: " + timeout); StringWriter sw = new StringWriter(); PrintWriter pw = new FastPrintWriter(sw, false, 1024); pw.println(timeout); timeout.dump(pw, " "); pw.close(); mLastAnrDump = sw.toString(); mAm.mHandler.removeCallbacks(mLastAnrDumpClearer); mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS); anrMessage = "executing service " + timeout.shortName; ... ... } if (anrMessage != null) { // 當存在 timeout 的 service,則執行 appNotResponding mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage); } }
上述方法會找到當前進程已經超時的 Service,通過一些斷定後,決定要報告 ANR,最終調用 AMS.appNotResponding()
方法。
走到這一步,ANR 機制已經完成了監測報告任務,剩下的任務就是 ANR 結果的輸出,咱們稱之爲 ANR 的報告機制。ANR 的報告機制是經過 AMS.appNotResponding()
完成的,Broadcast 和 InputEvent 類型的 ANR 最終也都會調用這個方法。
接下來咱們看看 Android ANR 的信息收集過程!
// frameworks/base/services/core/java/com/android/server/am/AppErrors.java class AppErrors { final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation) { ... ... long anrTime = SystemClock.uptimeMillis(); if (ActivityManagerService.MONITOR_CPU_USAGE) { mService.updateCpuStatsNow(); // 更新 cpu 統計信息 } boolean showBackground = Settings.Secure. getInt(mContext.getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; boolean isSilentANR; synchronized (mService) { if (mService.mShuttingDown) { return; } else if (app.notResponding) { return; } else if (app.crashing) { return; } else if (app.killedByAm) { return; } else if (app.killed) { return; } // In case we come through here for the same app before completing // this one, mark as anring now so we will bail out. app.notResponding = true; // 記錄 ANR 到 EventLog EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid, app.processName, app.info.flags, annotation); // 將當前進程添加到 firstPids firstPids.add(app.pid); // Don't dump other PIDs if it's a background ANR isSilentANR = !showBackground && !isInterestingForBackgroundTraces(app); if (!isSilentANR) { int parentPid = app.pid; if (parent != null && parent.app != null && parent.app.pid > 0) { parentPid = parent.app.pid; } if (parentPid != app.pid) firstPids.add(parentPid); // 將 system_server 進程添加到 firstPids if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID); for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) { ProcessRecord r = mService.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) { // 將 persistent 進程添加到 firstPids firstPids.add(pid); } else if (r.treatLikeActivity) { firstPids.add(pid); } else { // 其餘進程添加到 lastPids lastPids.put(pid, Boolean.TRUE); } } } } } } // 記錄 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 對象 ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); ... ... // 輸出 traces 信息 File tracesFile = ActivityManagerService.dumpStackTraces( true, firstPids, (isSilentANR) ? null : processCpuTracker, (isSilentANR) ? null : lastPids, nativePids); String cpuInfo = null; if (ActivityManagerService.MONITOR_CPU_USAGE) { mService.updateCpuStatsNow(); synchronized (mService.mProcessCpuTracker) { cpuInfo = mService.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()); if (tracesFile == null) { Process.sendSignal(app.pid, Process.SIGNAL_QUIT); } ... ... // 將 traces 文件和 CPU 使用率信息保存到 dropbox,即 data/system/dropbox 目錄 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 的 ANR 狀態,病查詢錯誤報告 receiver makeAppNotRespondingLocked(app, activity != null ? activity.shortComponentName : null, annotation != null ? "ANR " + annotation : "ANR", info.toString()); // 彈出 ANR 對話框 Message msg = Message.obtain(); msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem); // 向 ui 線程發送,內容爲 SHOW_NOT_RESPONDING_MSG 的消息 mService.mUiHandler.sendMessage(msg); } } }
當發生 ANR 時, 會按順序依次執行:
✒ 一、輸出 ANR Reason
信息到 EventLog
,也就是說 ANR 觸發的時間點最接近的就是 EventLog
中輸出的 am_anr
信息;
✒ 二、收集並輸出重要進程列表中的各個線程的 traces
信息,該方法較耗時;
✒ 三、輸出當前各個進程的 CPU 使用狀況
以及 CPU 負載狀況
;
✒ 四、將 traces 文件
和 CPU 使用狀況信息
保存到 dropbox
,即 data/system/dropbox
目錄;
✒ 五、根據進程類型,來決定直接後臺殺掉
,仍是彈框告知用戶
。
ANR輸出重要進程的traces信息,這些進程包含:
✒ 一、firstPids 隊列:第一個是 ANR
進程,第二個是 system_server
,剩餘是全部 persistent
進程;
✒ 二、Native 隊列:是指 /system/bin/
目錄的 mediaserver
、sdcard
以及 surfaceflinger
進程;
✒ 三、lastPids 隊列: 是指 mLruProcesses
中的不屬於 firstPids
的全部進程。
繼續看看 dump 出 trace 信息的流程:
// ActivityManagerService.java public static File dumpStackTraces(boolean clearTraces, ... ,nativePids) { ... ... if (tracesDirProp.isEmpty()) { // 默認爲 data/anr/traces.txt String globalTracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null); tracesFile = new File(globalTracesPath); try { if (clearTraces && tracesFile.exists()) { tracesFile.delete(); // 刪除已存在的 traces 文件 } // 這裏會保證 data/anr/traces.txt 文件內容是全新的方式,而非追加 tracesFile.createNewFile(); // 建立 traces 文件 FileUtils.setPermissions(globalTracesPath, 0666, -1, -1); } catch (IOException e) { Slog.w(TAG, "Unable to prepare ANR traces file: " + tracesFile, e); return null; } } else { } // 輸出 trace 內容 dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids, useTombstonedForJavaTraces); return tracesFile; }
// ActivityManagerService.java private static void dumpStackTraces(String tracesFile, ...) { final DumpStackFileObserver observer; if (useTombstonedForJavaTraces) { observer = null; } else { observer = new DumpStackFileObserver(tracesFile); } // We must complete all stack dumps within 20 seconds. long remainingTime = 20 * 1000; try { if (observer != null) { observer.startWatching(); } // 首先,獲取 firstPids 進程的 stacks if (firstPids != null) { int num = firstPids.size(); for (int i = 0; i < num; i++) { final long timeTaken; if (useTombstonedForJavaTraces) { timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile, remainingTime); } else { timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime); } ... ... } } // 下一步,獲取 native 進程的 stacks if (nativePids != null) { for (int pid : nativePids) { ... ... // 輸出 native 進程的 trace Debug.dumpNativeBacktraceToFileTimeout( pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000)); final long timeTaken = SystemClock.elapsedRealtime() - start; ... ... } } // Lastly, dump stacks for all extra PIDs from the CPU tracker. if (extraPids != null) { ... ... } } } finally { if (observer != null) { observer.stopWatching(); } } }
觸發 ANR 時系統會輸出關鍵信息:
✒ 一、將 am_anr
信息,輸出到 EventLog
;
✒ 二、獲取重要進程 trace
信息,保存到 /data/anr/traces.txt
;
✒ 三、ANR reason
以及 CPU
使用狀況信息,輸出到 main log
;
✒ 四、再將 CPU使用狀況
和進程 trace 文件
信息,再保存到 /data/system/dropbox
。
當 Service 出現 ANR 時,最終調用到 AMS.appNotResponding()
方法。
✒ 一、對於前臺服務
,則超時爲 SERVICE_TIMEOUT = 20s
;
✒ 二、對於後臺服務
,則超時爲 SERVICE_BACKGROUND_TIMEOUT = 200s
;
✒ 三、Service 超時檢測機制:超過必定時間沒有執行完相應操做
來觸發延時消息
,則會觸發 ANR
;