美文网首页Android
[ANR] 发生ANR后的信息采集过程

[ANR] 发生ANR后的信息采集过程

作者: 尹学姐 | 来源:发表于2023-03-05 21:57 被阅读0次

    发生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源码为例,分步进行讲解。

    1. appNotResponding传入参数
     void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
                String parentShortComponentName, WindowProcessController parentProcess,
                boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
    }
    

    其中annotation就是shortMsg,表示ANR发生的原因。

    1. 先获取一次CPU信息
            long anrTime = SystemClock.uptimeMillis();
            if (isMonitorCpuUsage()) {
                mService.updateCpuStatsNow();
            }
    
    1. 设置annotation
              // Store annotation here as instance above will not be hit on all paths.
                setAnrAnnotation(annotation);
    
    1. 判断是否需要跳过
                // 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 杀掉
    • 进程已经被杀
    1. 设置notResponding的标记
    setNotResponding(true);
    
    1. 打印am_anr的EventLog
     EventLog.writeEvent(EventLogTags.AM_ANR, mApp.userId, pid, mApp.processName,
                        mApp.info.flags, annotation);
    
    1. 获取需要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 进程
    1. 生成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信息
    1. 收集需要 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文件,主要包含audioservercameraserver等。

    1. 开始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线程监听信号,下一篇文章再详细讲。

    1. 再次获取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());
    
    1. 保存到 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目录

    1. 如果是后台 ANR,直接杀进程
                if (isSilentAnr() && !mApp.isDebugging()) {
                    mApp.killLocked("bg anr", ApplicationExitInfo.REASON_ANR, true);
                    return;
                }
    
    1. 设置AppnotRespondingReport(到这里,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();
        }
    
    1. 唤醒 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丰富的信息。

    主要流程如下:

    1. am_anr信息,输出到EventLog(这是ANR发生的起点)
    2. 获取重要进程的trace信息,保存到/data/anr/traces.txt
      • Java进程:当前进程、parent进程、system_server、top 5的进程
      • Native进程:audioservercameraserver
    3. ANR reasonCPU使用情况,输出到 MainLog
    4. CPU使用情况及traces文件信息,保存到/data/system/dropbox
    5. 如果是后台ANR,直接杀进程
    6. 如果是前台ANR,设置notRespondingReport,激活 ANR 弹窗

    相关文章

      网友评论

        本文标题:[ANR] 发生ANR后的信息采集过程

        本文链接:https://www.haomeiwen.com/subject/sytsldtx.html