Android 7系统异常问题排查(四)Framework层(上)—System Server Watchdog
系列目录第一篇异常机制全景图 | 第二篇Kernel Panic 与系统重启 | 第三篇Tombstone 机制 | 第四篇System Server Watchdog | 第五篇System Server 崩溃 | 第六篇ANR 机制 | 第七篇Java 层崩溃 | 第八篇Trace 机制 | 第九篇日志系统 | 第十篇实战方法论一、为什么需要 System Server Watchdog你可能遇到过这些场景手机使用中突然卡死按什么键都没反应几十秒后自动重启系统界面消失短暂黑屏后又恢复充电时设备无响应过一会儿自动重启这些现象的背后可能是 System Server 中的关键线程被阻塞了。内核 watchdog能检测到 CPU 级别的死锁但无法检测到用户态的逻辑死锁——比如AMS 持有一个锁后等待 Binder 调用返回但目标进程也因等待 AMS 而阻塞某个关键线程在while(true)中无限循环IO 操作阻塞过久如 eMMC 异常System Server Watchdog正是为了解决这类用户态卡死问题而设计的。二、Watchdog 源码架构源码路径frameworks/base/services/core/java/com/android/server/Watchdog.java2.1 两大检测机制Watchdog 通过两种方式检测系统健康状态检测机制检测对象超时阈值原理Monitor关键服务的锁持有状态60s尝试获取被监控的锁若超时说明锁被长期持有HandlerChecker关键线程的 Handler 消息处理60s向目标线程 Handler 发送空消息等待处理完成publicfinalclassWatchdogextendsThread{// ...staticfinallongDEFAULT_TIMEOUTDB?10*1000:60*1000;// 60秒staticfinallongCHECK_INTERVALDEFAULT_TIMEOUT/2;// 30秒staticfinalintCOMPLETED0;staticfinalintWAITING1;staticfinalintWAITED_HALF2;staticfinalintBLOCKED3;finalArrayListHandlerCheckermHandlerCheckersnewArrayList();finalHandlerCheckermMonitorChecker;// 专门用于 Monitor 检查// ...}关键设计DEFAULT_TIMEOUT是 60 秒而非 30 秒——CHECK_INTERVAL30 秒是检测间隔DEFAULT_TIMEOUT60 秒才是超时阈值。2.2 被监控的 HandlerChecker 线程publicfinalclassWatchdogextendsThread{// ...finalArrayListHandlerCheckermHandlerCheckersnewArrayList();finalHandlerCheckermMonitorChecker;publicWatchdog(){super(watchdog);// Monitor 检查器在 FgThread 上运行mMonitorCheckernewHandlerChecker(FgThread.getHandler(),foreground thread,DEFAULT_TIMEOUT);mHandlerCheckers.add(mMonitorChecker);// 以下线程的 Handler 都被监控mHandlerCheckers.add(newHandlerChecker(newHandler(Looper.getMainLooper()),main thread,DEFAULT_TIMEOUT));mHandlerCheckers.add(newHandlerChecker(UiThread.getHandler(),ui thread,DEFAULT_TIMEOUT));mHandlerCheckers.add(newHandlerChecker(IoThread.getHandler(),i/o thread,DEFAULT_TIMEOUT));mHandlerCheckers.add(newHandlerChecker(DisplayThread.getHandler(),display thread,DEFAULT_TIMEOUT));}}关键设计共 5 个 HandlerChecker——foreground兼做 Monitor 载体、main、ui、i/o、display。Monitor 被添加到mMonitorChecker中在 FgThread 上执行锁检查。2.3 被监控的 Monitor 锁各系统服务通过Watchdog.getInstance().addMonitor()注册自己的锁检查点// Watchdog.javapublicfinalclassWatchdogextendsThread{// ...publicvoidaddMonitor(Monitormonitor){synchronized(this){if(isAlive()){thrownewRuntimeException(Monitors must be added before starting);}mMonitorChecker.addMonitor(monitor);}}}各服务在onStart()中注册AMS、WMS、PowerManagerService 等都通过addMonitor()将自己的锁注册为健康检查点。Watchdog 通过尝试获取这些锁来判断对应服务是否处于死锁状态。三、运行机制详解3.1 HandlerChecker 数据结构publicfinalclassWatchdogextendsThread{// ...publicfinalclassHandlerCheckerimplementsRunnable{privatefinalHandlermHandler;privatefinalStringmName;privatefinallongmWaitMax;privatefinalArrayListMonitormMonitorsnewArrayList();privatebooleanmCompleted;privateMonitormCurrentMonitor;privatelongmStartTime;HandlerChecker(Handlerhandler,Stringname,longwaitMaxMillis){mHandlerhandler;mNamename;mWaitMaxwaitMaxMillis;mCompletedtrue;}}}关键设计每个 HandlerChecker 持有目标线程的 Handler、名称、超时阈值和一组 Monitor。mCompleted标记上一轮检查是否完成。3.2 scheduleCheckLocked —— 发送心跳publicfinalclassWatchdogextendsThread{// ...publicfinalclassHandlerCheckerimplementsRunnable{// ...publicvoidscheduleCheckLocked(){// 如果没有 Monitor 且 Looper 是 polling 模式跳过检查if(mMonitors.size()0mHandler.getLooper().getQueue().isPolling()){mCompletedtrue;return;}// 如果上一轮还没完成不重复发送if(!mCompleted){return;}mCompletedfalse;mStartTimeSystemClock.uptimeMillis();mHandler.postAtFrontOfQueue(this);// 向目标线程发送检查任务}}}关键设计通过postAtFrontOfQueue(this)将检查任务插入目标线程消息队列头部。如果目标线程的消息队列被阻塞比如持有锁后在等 Binder这个任务就无法被处理。3.3 HandlerChecker.run() —— Monitor 执行publicfinalclassWatchdogextendsThread{// ...publicfinalclassHandlerCheckerimplementsRunnable{// ...publicvoidrun(){finalintsizemMonitors.size();for(inti0;isize;i){synchronized(Watchdog.this){mCurrentMonitormMonitors.get(i);}mCurrentMonitor.monitor();// 尝试获取锁}synchronized(Watchdog.this){mCompletedtrue;mCurrentMonitornull;}}}}关键设计monitor()方法内部会synchronized获取目标服务的锁。如果锁被其他线程长期持有monitor()就会阻塞导致mCompleted无法被设为true。3.4 Watchdog.run() —— 主循环publicfinalclassWatchdogextendsThread{// ...Overridepublicvoidrun(){booleanwaitedHalffalse;while(true){ArrayListMonitormonitors;ArrayListHandlerCheckerhandlerCheckers;synchronized(this){longtimeoutCHECK_INTERVAL;// 30秒longstartSystemClock.uptimeMillis();// 对所有 HandlerChecker 发送心跳for(intimHandlerCheckers.size()-1;i0;i--){mHandlerCheckers.get(i).scheduleCheckLocked();}// 等待 CHECK_INTERVAL30秒// 期间如果有 checker 提前完成会 notifywhile(waitStateCOMPLETED){// ...wait(timeout);// ...}}// ... 后续检查逻辑}}}3.5 超时处理流程Watchdog 每 30 秒CHECK_INTERVAL执行一次检测 │ ├─ 第一次检测30s 后 │ ├─ 所有 checker 完成 → 正常继续下一轮 │ └─ 有 checker 未完成但未超时 → 正常给 60s 宽限 │ ├─ 第二次检测60s 后即又过了 30s │ ├─ 所有 checker 完成 → 正常 │ ├─ 有 checker 超过 60s 未完成WAITED_HALF │ │ ├─ 收集线程堆栈dumpStackTraces │ │ ├─ 写入日志 │ │ └─ 继续等待给系统一次自救机会 │ │ │ └─ 有 checker 仍然阻塞BLOCKED │ ├─ 收集所有阻塞 checker 的描述 │ ├─ dumpStackTraces 保存完整堆栈 │ ├─ 写入 DropBox (system_server_watchdog) │ ├─ Slog.w(TAG, *** WATCHDOG KILLING SYSTEM PROCESS: ...) │ └─ Process.killProcess(Process.myPid()) → 系统自杀关键设计Watchdog 的超时判定是渐进式的——先等 30 秒检查一次如果未完成再等 30 秒共 60 秒超过 60 秒才确认阻塞并自杀。中间的WAITED_HALF状态会先 dump 堆栈用于诊断。四、Watchdog 触发后的日志产物4.1 DropBox 条目通过dumpsys dropbox --print可以查看Tag: system_server_watchdog Subject: Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor ... --- pid 1234 at 2024-01-01 12:00:00 --- Cmd line: system_server main prio5 tid1 Blocked | groupmain sCount1 dsCount0 obj0x12c0e0a0 self0x7f8c3a4000 | sysTid1234 nice-2 cgrpdefault sched0/0 handle0x7f8c3a4b50 at com.android.server.am.ActivityManagerService.monitor(AMS.java:12345) - waiting to lock 0x12345678 (a com.android.server.am.ActivityManagerService) held by thread 15 ...4.2 /data/anr/traces.txtWatchdog 超时也会 dump 所有线程的堆栈到/data/anr/traces.txt格式与 ANR 的 traces 一致。五、典型 Watchdog 场景与定位场景 1系统服务死锁现象手机使用中突然卡死几十秒后自动重启。日志特征main prio5 tid1 Blocked waiting to lock 0x12345678 held by Binder:1234_5 tid15 ... Binder:1234_5 tid15 Blocked waiting to lock 0x87654321 held by main tid1分析经典死锁——main 线程持有锁 A 等锁 BBinder 线程持有锁 B 等锁 A。定位方法从dumpsys dropbox system_server_watchdog --print提取堆栈找出两个线程各自持有的锁和等待的锁画出锁依赖图找到循环依赖修改代码统一锁的获取顺序或使用tryLock超时机制场景 2IO 阻塞现象存储空间不足或 eMMC 异常时频繁 Watchdog 重启。日志特征main prio5 tid1 Runnable at android.os.FileUtils.readTextFile(...) at com.android.server.pm.PackageManagerService.writeLp(...)分析主线程在等待 IO 写入完成但 eMMC 响应异常缓慢。定位方法结合内核日志dmesg查看是否有mmc0: timeout等 eMMC 错误。场景 3Binder 线程池耗尽现象系统逐渐变慢最终 Watchdog 触发。日志特征所有 Binder 线程都在等待某个远程服务响应。分析Binder 线程池所有线程都被占用新的请求无法被处理形成级联阻塞。定位方法查看所有 Binder 线程的堆栈找到它们都在等待哪个进程/服务。六、Watchdog 的局限性局限说明检测粒度粗60 秒超时阈值无法发现短时卡顿被动检测只能发现已经卡死的状态无法预警无法定位根因只能告诉你哪里卡住了不能告诉你为什么卡住Monitor 可能误报如果锁本身设计就是长时间持有会触发误报七、总结Watchdog 是 System Server 的心跳监护仪每 30 秒检测一次关键线程和锁的健康状态超时阈值为 60 秒。5 个 HandlerChecker 覆盖关键线程foreground、main、ui、i/o、displayMonitor 在 foreground 线程上执行。渐进式超时处理30 秒检查一次超过 60 秒确认阻塞后自杀重启中间会先 dump 堆栈用于诊断。日志产物DropBox (system_server_watchdog) /data/anr/traces.txt。常见根因服务间死锁、IO 阻塞、Binder 线程池耗尽。下一篇将介绍 System Server 崩溃的另一种形态——进程直接崩溃而非 Watchdog 超时触发。本文基于 AOSP 7Android Nougat源码编写。