安卓分享

Android ANR 实现机制详解

2022-08-29  本文已影响0人  super可乐

一 ANR 概述

ANR(Application Not Responding),即应用程序无响应,Android 系统指定某些事件需要在规定时间内完成,如果超过预定时间还能未能得到有效响应,就会造成 ANR。具体表现为,应用位于前台时,系统会向用户显示一个对话框,如下图所示。用户可以选择“wait”让程序继续运行,也可以选择“Close app”强制关闭。

二 ANR 触发场景

系统发生 ANR 时会在 system_server 进程调用 AppNotRespondingDialog.show() 方法,弹出对话框提示用户,对话框的调用链如下:

ProcessRecord.appNotResponding()
└── mService.mUiHandler.sendMessage(SHOW_NOT_RESPONDING_UI_MSG)
    └── AMS.UiHandler.handleMessage()
        └── AppErrors.handleShowAnrUi()
             └── AppNotRespondingDialog.show()

其中 AppErrors.appNotResponding() 方法是弹出 ANR 对话框的唯一入口,查看其调用关系如如下图:

总结有以下 4 种场景的超时会引起 ANR:

  1. Service Timeout: 组件 Service 执行超时

  2. Broadcast Receiver Timeout:组件 BroadcastReceiver 执行超时

  3. ContentProvider Timeout:组件 ContentProvider 执行超时

  4. InputDispatching Timeout: 按键或触摸事件在特定时间内无响应

以下是 Android 原生系统对不同类型的超时阈值设置,各手机厂商或芯片厂商也会对此值进行自行定制。

类型 TimeOut (sec)
Service 前台:20,后台:200
Broadcast 前台:10,后台:60
ContentProvider 10
InputDispatching 5

三 ANR 实现机制

3.1 ANR 触发流程

触发 ANR 的过程大致可分为三个步骤: 埋下炸弹 ->执行任务 ->引爆炸弹(或拆除炸弹)

其主体实现在系统层:

  1. 前文所述 4 种场景相关的事件都会经过系统进程(system_server) 调度,设置定时监控(即埋下炸弹)
  2. 然后,system_server 进程将任务派发到应用进程完成对消息的实际处理(执行任务)
  3. 最后,执行任务时间过长,在定时器超时前 system_server 还未收到任务完成的通知,触发 ANR(炸弹爆炸)

在第 3 步中,如果任务在规定时间内执行完成,通知 system_server 进程移除监控,则炸弹被拆除,不会发生 ANR 异常。

3.2 Service超时机制源码解读

以 Service 超时为例,从源码来分析其超时机制引发 ANR 的流程。

Service Timeout 是位于 system_server 进程 -> ActivityManager 线程中 AMS.MainHandler 收到 SERVICE_TIMEOUT_MSG消息时触发。

对于 Service 有两类:

Service ANR 检测

Android 是通过设置定时消息实现的检测 Service 超时的。定时消息是由 AMS 的消息队列处理的(system_server 的 ActivityManager 线程)。AMS 有 Service 运行的上下文信息。

Service ANR 主体实现在 ActiveServices 中。 当 Service 的生命周期开始时,bumpServiceExecutingLocked() 会被调用,紧接着会调用 scheduleServiceTimeoutLocked()

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

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;
    mAm.mHandler.sendMessageDelayed(msg,
            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

当 Service 的生命周期结束时,会调用 serviceDoneExecutingLocked() 方法,之前抛出的 SERVICE_TIMEOUT_MSG 消息在这个方法中会被清除。 如果在超时时间内,SERVICE_TIMEOUT_MSG 没有被清除,那么,AMS.MainHandler 就会响应这个消息

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

case SERVICE_TIMEOUT_MSG: {
    mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;

serviceTimeout 在 ActiveServices 启动打印信息

frameworks/base/services/core/java/com/android/server/am/ActiveServicesjava

if (timeout != null && mAm.mProcessList.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.shortInstanceName;
}

// 打印 ANR 信息
if (anrMessage != null) {
    mAm.mAnrHelper.appNotResponding(proc, anrMessage);
}

通过 ANRHelper 打印 ANR 信息

frameworks/base/services/core/java/com/androidserver/am/AnrHelper.java

void appNotResponding(ProcessRecord anrProcess, String activityShortComponentName,
        ApplicationInfo aInfo, String parentShortComponentName,
        WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
    synchronized (mAnrRecords) {
        mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
                parentShortComponentName, parentProcess, aboveSystem, annotation));
    }
    startAnrConsumerIfNeeded();
}

Service 的 ANR 机制总结 通过定时消息跟踪 Service 的运行,当定时消息被响应时,说明 Service 还没有运行完成,这就意味着 Service ANR。

四 ANR 问题分析流程

ANR 问题主要有两种原因:应用自身的问题系统异常导致的问题。在分析 ANR 问题的时候,最主要的就是要确定是哪个原因导致的

ANR 问题一般的分析步骤如下:

  1. 分析 trace.txt:查看是否有明显的异常,比如死锁、SystemServer 异常持锁等
    1. 死锁堆栈: 观察 Trace 堆栈,确认是否有明显问题,如主线程是否与其他线程发生死锁,如果是进程内部发生了死锁,只需找到与当前线程死锁的线程,问题即可解决
    2. 业务堆栈: 观察 Trace 堆栈,发现当前主线程堆栈正在执行业务逻辑,这时候需要分析对应的代码,查看是否真的有问题
      1. 重要:如果业务方觉得这里没有问题,需要进一步分析,因为 Trace 堆栈可能并不是真正耗时的地方,需要结合其他信息一起分析
    3. IPC Block 堆栈: 观察通过 Trace 堆栈,发现主线程堆栈是在跨进程(Binder)通信,这时候可以根据其他 Log、Binder Info 等信息,来分析 IPC 信息
      1. 大部分 IPC 都是在跟 SystemServer,如果没有 BinderInfo,可以搜索对应的接口关键字,在 SystemServer 进程查找是否有相关的堆栈
      2. 重要:如果业务方觉得这里没有问题,需要进一步分析,因为 Trace 堆栈可能并不是真正耗时的地方,需要结合其他信息一起分析
    4. 系统堆栈: 通过观察 Trace,发现当前堆栈只是简单的系统堆栈,比如 NativePollOnce,想要搞清楚是否发生严重耗时,以及进一步的分析定位
      1. 重要:大部分比较难分析的 ANR 基本上应用主线程堆栈都是 NativePollOnce 这个状态,之所以出现这种状态,可能有下面几个原因
        1. 确实没有消息在处理,可能进程被冻结,或者 No Focused Window 这种 ANR
        2. 刚好处理完某一个耗时消息,系统抓堆栈的时候,已经晚了,耗时的状态没有抓到
        3. 线程调度的原因,主线程没有机会执行
  2. 分析 Event Log:看具体的 ANR 时间(搜索 am_anr),看看是否跟 ANR log 能够对上,以确定 ANR Log 是否有效,如果 ANR Log 有效,分析 ANR Log,提取有用信息:pid、tid、死锁等,遇到 ANR 问题,摆在我们面前的 trace 是不是第一案发现场,如果 ANR 发生的输出的信息很多,当时的 CPU 和 I/O 资源比较紧张,那么这段日志输出的时间点可能会延迟 10 秒到 20 秒都有可能,所以我们有时候需要提高警惕。不过正常情况下,EventLog 中的 am_anr 的输出时间是最早的,也是最接近 ANR 时间的 (提取有效信息到单独文件中)
  3. 分析 Android Log:看 MainLog(Android Log) 或者 SystemLog 查看 ANR 详细信息(搜索 ANR in),提取有效的信息 (提取有效信息到单独文件中)
    1. 发生 ANR 的时间
    2. 打印 ANR 日志的进程
    3. 发生 ANR 的进程
    4. 发生 ANR 的原因
    5. CPU 负载
    6. Memory 负载
    7. CPU 使用统计时间段
    8. 各进程的 CPU 使用率
      1. 总的 CPU 使用率
      2. 缺页次数 fault
        1. xxx minor 表示高速缓存中的缺页次数,可以理解为进程在做内存访问
        2. xxx major 表示内存的缺页次数,可以理解为进程在做 IO 操作
    9. CPU 使用汇总
  4. 配合 Main Log(Android Log) 和 EventLog 把 CPU 开始和结束的时间点内的所有有用信息提取出来到一个 文件中,搜索的主要关键字:pid,进程名,WindowManager、ActivityManager(关键字参考下一节的关键 Log 那里)
    1. 收集关键操作场景,比如解锁、安装应用、亮灭屏、应用启动等
    2. 收集异常和系统关键 Log
      1. 系统变慢 :比如 Slow operation、Slow dispatch、Slow delivery、dvm_lock_sample、binder_sample
      2. 进程变化 :am_kill、am_proc_died、lowmemorykiller、ANR、应用启动关系等
      3. 系统信息 :cpu info、meminfo、binder info(是否满了) 、iowait (是否过高)
      4. 消息监控:ANR 前的 ANR Message 打印,Block Message 信息,应用自己代码执行逻辑推断出的 Message 耗时等
    3. 收集 ANR 进程的所有关键线程的运行情况、线程优先级等
    4. 根据第四步提取出来的关键信息文件,进一步理出系统当时的情况、状态(推荐 vscode 或者 notepad ++ ,有 线索就全局搜索)),比如
      1. 是处于低内存频繁杀进程?
      2. 重启第一次解锁系统繁忙
      3. 还是短时间内多个应用启动系统繁忙
      4. 还是应用自己的逻辑等待?
    5. 针对不同的 ANR 类型,提取不同的信息
  5. 不行就加 Log 复现

参考文献

Android ANR 分析

彻底理解安卓应用无响应机制

作者:话唠扇贝
链接:https://juejin.cn/post/7128397546099048478

上一篇下一篇

猜你喜欢

热点阅读