anr日志生成與捕獲方式分析

一、ANR日志生成過程

以Input ANR為例來分析下anr日志的生成過程:

input觸發(fā)anr之后會通過InputManagerService執(zhí)行notifyANR,最終交由ActivityManagerService來處理,ActivityManagerService執(zhí)行appNotResponding是ANR處理的核心位置,通過AppErrors,最終在ActivityManagerService分別干了三件事:

  • 寫trace到/data/anr/traces.txt。
  • 寫trace和cpu usage信息到/data/system/dropbox/,然后發(fā)送響應廣播。
  • 發(fā)送ANR廣播,執(zhí)行ANR彈窗。

詳細四大組件+Input觸發(fā)ANR流程參考之前文章:Android ANR(二)-觸發(fā)原理

二、ANR日志收集方式

2.1 低版本:FileObserver+ProcessErrorStateInfo

ProcessErrorStateInfo 獲取cause reason

ActivityManager am = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.ProcessErrorStateInfo> processErrorList = am.getProcessesInErrorState();
if (processErrorList != null) {
    for (ActivityManager.ProcessErrorStateInfo errorStateInfo : processErrorList) {
        if (errorStateInfo.pid == pid && errorStateInfo.condition == ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING) {
          //這兩部分構成 cause reason
           Log.d("xcrashtest", "shortMsg: " + errorStateInfo.shortMsg);
           Log.d("xcrashtest", "longMsg: " + errorStateInfo.longMsg);
           return true;
       }
    }

這是AMS對外暴露的api,從AMS的mLruProcesses中過濾出crash和anr異常的進程,返回對應的錯誤信息,詳細邏輯如下:

ActivityManagerService.java

public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
    enforceNotIsolatedCaller("getProcessesInErrorState");
   // assume our apps are happy - lazy create the list
   List<ActivityManager.ProcessErrorStateInfo> errList = null;
   final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
           Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
   int userId = UserHandle.getUserId(Binder.getCallingUid());
   synchronized (this) {
        // iterate across all processes
       for (int i=mLruProcesses.size()-1; i>=0; i--) {
            ProcessRecord app = mLruProcesses.get(i);
           if (!allUsers && app.userId != userId) {
                continue;
           }
            if ((app.thread != null) && (app.crashing || app.notResponding)) {
                // This one's in trouble, so we'll generate a report for it
               // crashes are higher priority (in case there's a crash *and* an anr)
               ActivityManager.ProcessErrorStateInfo report = null;
               if (app.crashing) {
                    report = app.crashingReport;
               } else if (app.notResponding) {
                    report = app.notRespondingReport;
               }
                if (report != null) {
                    if (errList == null) {
                        errList = new ArrayList<ActivityManager.ProcessErrorStateInfo>(1);
                   }
                    errList.add(report);
               } else {
                    Slog.w(TAG, "Missing app error report, app = " + app.processName +
                            " crashing = " + app.crashing +
                            " notResponding = " + app.notResponding);
               }
            }
        }
    }
    return errList;
}

這里如果是anr,report = app.notRespondingReport,notRespondingReport初始化的地方在AppErrors.appNotResponding中調(diào)用的makeAppNotRespondingLocked。

FileObserver來監(jiān)聽/data/anr/目錄下對應的trace文件,來讀取相關的trace信息。

FileObserver的使用:

fileObserver = new FileObserver("/data/anr/", CLOSE_WRITE) {
   public void onEvent(int event, String path) {
                //監(jiān)聽回調(diào)處理anr
      }
   }
};

fileObserver.startWatching();//啟動監(jiān)聽
fileObserver.stopWatching();//停止監(jiān)聽

FileObserver的startWatching是交給內(nèi)部的ObserverThread來處理的,最終執(zhí)行的startWatching是個native方法:

static jint android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd, jstring pathString, jint mask)
{
    int res = -1;
#if defined(__linux__)
    if (fd >= 0)
    {
        const char* path = env->GetStringUTFChars(pathString, NULL);
        //fd :inotify_init的返回值
        //path:要監(jiān)控的文件路徑
        //mask:監(jiān)聽文件的哪些事件
        //res: 表示對那個文件的監(jiān)視
        res = inotify_add_watch(fd, path, mask);
        env->ReleaseStringUTFChars(pathString, path);
    }
#endif
   return res;
}

inotify是文件系統(tǒng)變化通知機制,在監(jiān)聽到文件系統(tǒng)變化后,會向相應的應用程序發(fā)送事件。
該方案因為高版本文件權限的問題,目前只支持<=21的版本。
SELinux(或SEAndroid)將app劃分為主要三種類型(根據(jù)user不同,也有其他的domain類型):

  • untrusted_app:第三方app,沒有android平臺簽名,沒有system權限
  • platform_app:有android平臺簽名,沒有system權限
  • system_app:有android平臺簽名和system權限
2.2 高版本:native 注冊 SIGNAL_QUIT 信號,ANR發(fā)生時接收回調(diào)去收集ANR信息

高版本能使用的原因是捕獲SIGNAL_QUIT只能基于ART。
接收回調(diào)之后,art dump出trace信息,具體anr日志抓取可以參考xcrash的源碼: xc_trace.c xc_trace_dumper線程。

對ANR 日志的獲取主流方案就是如上兩種,Xcrash 和Bugly都是使用的這兩種方式。

2.3 ANR-WatchDog

ANR-WatchDog是仿Android WatchDog機制起個單獨線程向主線程發(fā)送一個變量+1操作,自我休眠自定義ANR的閾值,休眠過后判斷變量是否+1完成,如果未完成則告警。有個明顯問題是,發(fā)送+1 消息的時機可能錯過ANR現(xiàn)場,從而抓不到現(xiàn)場堆棧信息。

這方案只是提供了一個新思路,并不能穩(wěn)定獲取到anr線程堆棧信息。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容