源码基于:Android R
0. 前言
Watchdog字面上是“看门狗”的意思,有做过嵌入式低层的朋友应该知道,为了防止嵌入式系统MCU里的程序因为干扰而跑飞,专门在MCU里设计了一个定时器电路,叫做看门狗。当MCU正常工作的,每隔一段时间会输出一个信号给看门狗,也就是所谓的喂狗。如果程序跑飞,MCU在规定的时间内没法喂狗,这时看门狗就会直接触发一个reset信号,让CPU重新启动。
在Android系统的framework中,设计了一个系统服务Watchdog,它类似于一个软件看门狗,用来保护重要的系统服务。它的源代码位于:
frameworks/base/services/core/java/com/android/server/Watchdog.java
流程图:
1. 获取WatchDog 对象
public class Watchdog extends Thread { }
想要分析一个功能代码,可能先从本身的源头找起,对于Java 类首先看的就是类的定义以及构造构造函数啦!
从这里看 WatchDog 其实一个Thread,这个Thread 可能比较特殊而已,至于怎么特殊,下面会在SystemServer 分析的时候说明。那对于一个Thread,核心的操作部分就是run() 函数了,这个最重要的部分会放在最后解析。
再来看下WatchDog 的构造函数:
private Watchdog() { }
WatchDog 构造函数是private,对于外界获取对象的接口为:
public static Watchdog getInstance() { if (sWatchdog == null) { sWatchdog = new Watchdog(); } return sWatchdog; }
外界获取WatchDog 就是通过getInstance(),至于这个“外界”后面会补充。
2. WatchDog 的启动
/frameworks/base/services/java/com/android/server/SystemServer.java private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) { t.traceBegin("startBootstrapServices"); // Start the watchdog as early as possible so we can crash the system server // if we deadlock during early boot t.traceBegin("StartWatchdog"); final Watchdog watchdog = Watchdog.getInstance(); watchdog.start(); t.traceEnd(); ... t.traceBegin("InitWatchdog"); watchdog.init(mSystemContext, mActivityManagerService); t.traceEnd(); ... }
从第一节得知 WatchDog 实际是一个 thread,而且是以单例的形式存在,则需要确定其启动的地方。
这部分代码主要作用:
- 创建WatchDog 实例;
- 运行WatchDog thread;
- 在AMS 启动后,执行init() 函数,传入mSystemContext 和 AMS 实例;
由此,WatchDog 线程是在 system_server 进程中启动,专门用来给 system_server 中的其他线程喂狗。
3. 构造函数
private Watchdog() { //线程名为watchdog super("watchdog"); //将fg thread 单独提出作为主要的checker mMonitorChecker = new HandlerChecker(FgThread.getHandler(), "foreground thread", DEFAULT_TIMEOUT); mHandlerCheckers.add(mMonitorChecker); //创建主线程的checker mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()), "main thread", DEFAULT_TIMEOUT)); //创建UI thread 的checker mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(), "ui thread", DEFAULT_TIMEOUT)); //创建Io thread 的checker mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(), "i/o thread", DEFAULT_TIMEOUT)); //创建display thread 的checker mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(), "display thread", DEFAULT_TIMEOUT)); //创建animation thread 的checker mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(), "animation thread", DEFAULT_TIMEOUT)); //创建surface animation thread 的checker mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(), "surface animation thread", DEFAULT_TIMEOUT)); //fg thread的checker 中添加对binder 的checker addMonitor(new BinderThreadMonitor()); // 添加对watchdog 相关目录的监控 mOpenFdMonitor = OpenFdMonitor.create(); mInterestingJavaPids.add(Process.myPid()); // See the notes on DEFAULT_TIMEOUT. assert DB || DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;}
3.1 设定线程名
设WatchDog 的线程名为 watchdog
3.2 创建 HandlerChecker
WatchDog 中靠 HandlerChecker 来完成check 工作,每个 HandlerChecker 伴随一个Handler,即一个独立的 Looper 和 Thread。
WatchDog 在创建的时候指定对 FgThread、MainThread、UIThread、IoThread、DisplayThread、AnimationThread、SurfaceAnimationThread 等thread 的监控,当然后期可以通过接口动态添加到 check list(mHandlerCheckers) 中,例如通过接口 addThread(),该函数后面会详细剖析,可以查看第 5.2 节。
MainThread 是在 SystemServer 运行的时候创建的
frameworks/base/services/java/com/android/server/SystemServer.javaprivate void run() { … Looper.prepareMainLooper(); …}
其他的Thread 最终都是继承自HandlerThread(详细看 **Android HandlerThread 详解**)
HandlerChecker 有3 个参数分别是Handler 对象、name、以及触发watchdog 的最大时间间隔,详细的HandlerChecker 看下面第6节。
- 重要的几个 thread 在这里都有监听,它们都有专门的名字:
- android**.**fg
- android**.**ui
- android**.**io
- android**.**display
- android**.**anim
- android**.anim.**lf
3.3 添加对binder 的监控
private static final class BinderThreadMonitor implements Watchdog.Monitor { @Override public void monitor() { Binder.blockUntilThreadAvailable(); } }
monitor() 会调用到 native 层 IPCThreadState:
frameworks/native/libs/binder/IPCThreadState.cpp void IPCThreadState::blockUntilThreadAvailable(){ pthread_mutex_lock(&mProcess->mThreadCountLock); while (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads) { ALOGW("Waiting for thread to be free. mExecutingThreadsCount=%lu mMaxThreads=%lu\n", static_cast<unsigned long>(mProcess->mExecutingThreadsCount), static_cast<unsigned long>(mProcess->mMaxThreads)); pthread_cond_wait(&mProcess->mThreadCountDecrement, &mProcess->mThreadCountLock); } pthread_mutex_unlock(&mProcess->mThreadCountLock);}
BinderThreadMonitor 会被添加到 fg thread 中,主要是用于确认binder 是否有出现不够用的情况,例如,假设 binder 的max thread 为15个,超过15后就需要 check 是否存在 binder 阻塞。
3.4 创建OpenFdMonitor
public static OpenFdMonitor create() { // Only run the FD monitor on debuggable builds (such as userdebug and eng builds). if (!Build.IS_DEBUGGABLE) { return null; } final StructRlimit rlimit; try { rlimit = android.system.Os.getrlimit(OsConstants.RLIMIT_NOFILE); } catch (ErrnoException errno) { Slog.w(TAG, "Error thrown from getrlimit(RLIMIT_NOFILE)", errno); return null; } final File fdThreshold = new File("/proc/self/fd/" + (rlimit.rlim_cur - FD_HIGH_WATER_MARK)); return new OpenFdMonitor(new File("/data/anr"), fdThreshold);}
- 只有 debug 版本才做 fd monitor,例如 userdebug 或 eng;
- 主要是确认 /data/anr 路径的正常性;
3.5 mInterestingJavaPids
主要是触发WatchDog 或者 HALF time 的时候,将这里存的 pid 所对应的堆栈dump 出来。AMS 中每次启动一个进程时,都会调用 handleProcessStartedLocked() 最终调用 WatchDog**.**processStarted() 添加到 WatchDog 中,而process 也是有限定的,如下:
private static boolean isInterestingJavaProcess(String processName) { return processName.equals(StorageManagerService.sMediaStoreAuthorityProcessName) || processName.equals("com.android.phone"); } public void processStarted(String processName, int pid) { if (isInterestingJavaProcess(processName)) { Slog.i(TAG, "Interesting Java process " + processName + " started. Pid " + pid); synchronized (this) { mInterestingJavaPids.add(pid); } } }
4. init 函数
public void init(Context context, ActivityManagerService activity) { mResolver = context.getContentResolver(); mActivity = activity; context.registerReceiver(new RebootRequestReceiver(), new IntentFilter(Intent.ACTION_REBOOT), android.Manifest.permission.REBOOT, null); }
注册了reboot 的广播,软重启的操作是在这里进行的:
final class RebootRequestReceiver extends BroadcastReceiver { @Override public void onReceive(Context c, Intent intent) { if (intent.getIntExtra("nowait", 0) != 0) { rebootSystem("Received ACTION_REBOOT broadcast"); return; } Slog.w(TAG, "Unsupported ACTION_REBOOT broadcast: " + intent); } } void rebootSystem(String reason) { Slog.i(TAG, "Rebooting system because: " + reason); IPowerManager pms = (IPowerManager)ServiceManager.getService(Context.POWER_SERVICE); try { pms.reboot(false, reason, false); } catch (RemoteException ex) { } }
该软重启的 intent 中需要设定一个参数 nowait,且不为0.
5. 其他几个重要函数
5.1 addMonitor()
public void addMonitor(Monitor monitor) { synchronized (this) { mMonitorChecker.addMonitorLocked(monitor);//通过此接口会将monitor添加到fg thread中 } }
本函数是将 monitor 添加到 mMonitorChecker 中监控;
public interface Monitor { void monitor(); }
例如AMS 构造函数中:
Watchdog.getInstance().addMonitor(this);
5.2 addThread()
public void addThread(Handler thread) { addThread(thread, DEFAULT_TIMEOUT); } public void addThread(Handler thread, long timeoutMillis) {//通过此接口新建HandlerChecker synchronized (this) { final String name = thread.getLooper().getThread().getName(); mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis)); } }
通过本函数新建 HandlerChecker 并添加到 mHandlerCheckers 中,然后进行 check 该线程是否阻塞,默认timeout 为 60s;
例如 AMS 构造函数中:
Watchdog.getInstance().addThread(mHandler);
6. HandlerChecker类
在分析WatchDog 核心函数 run 之前,先来分析下核心处理类 HandlerChecker,所有 thread 的监听、处理都是在这个类中。
6.1 HandlerChecker 是一个Runnable
public final class HandlerChecker implements Runnable {
6.2 HandlerChecker的构造
HandlerChecker(Handler handler, String name, long waitMaxMillis) { mHandler = handler; mName = name; mWaitMax = waitMaxMillis; mCompleted = true;}
构造参数有三个:
- handler 用来发消息,通过handler 可以确定 thread;
- name 为 HandlerChecker 对应thread 的描述时使用,除了构造中的特殊的checker 有特殊的name,其他通过 addThread() 接口创建的 checker,都是使用线程名;
- waitMaxMillis 为监控时最大时长,默认为60s;
6.3 核心变量
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();private final ArrayList<Monitor> mMonitorQueue = new ArrayList<Monitor>();
一个HandlerChecker 中可以添加多个monitor,为了安全考虑,防止在monitor check 过程中添加新的monitor,将过程分两部分:
- 每次HandlerChecker 在schedule check的时候,只检查 mMonitors;
- schedule check 过程中,新添加的monitor 都临时存放在 mMonitorQueue 中;
详细看scheduleCheckLocked() 函数,即第 6.4 节。
6.4 scheduleCheckLocked
public void scheduleCheckLocked() { if (mCompleted) { //step 1 // Safe to update monitors in queue, Handler is not in the middle of work mMonitors.addAll(mMonitorQueue); mMonitorQueue.clear(); } if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) || (mPauseCount > 0)) { //step 2 mCompleted = true; return; } if (!mCompleted) { //step 3 // we already have a check in flight, so no need return; } mCompleted = false; //step 4 mCurrentMonitor = null; mStartTime = SystemClock.uptimeMillis(); mHandler.postAtFrontOfQueue(this);}
逻辑比较简单,见代码中注释,将逻辑分为4个部分:
- step1: 新的 schedule 时 mCompleted 为 true,会将 mMonitorQueue 中的 monitor copy到mMonitor 中,用以在 run() 函数中 check;
- step2:如果 handle 的 Looper 处于 polling 状态,且没有 monitor 添加进来,或者是HandlerChecker 处于pause 状态,不做 schedule check,直接指定 schedule 完成状态;
- step3:如果 HandlerChecker 中有 monitor 或者是 Looper 中有消息在处理,会跳过 step2, 如果是第一次执行 step3 的 case 是不会处理,如果 mCompleted 此时为false,那肯定是schedule 已经执行过一次,但是30s 的时候并没有执行完成,于是这里直接return,为了再给一次机会,再等 30s 等待 monitor 或者 msg 处理完成;
- step4:能进入 step4,是因为上一次的 schedule 顺利执行完成,通过mHandler.post() 函数准备执行 run() 函数,run() 如果顺利执行完成则会将mCompleted 置为true,不会进入 step3;
6.5 run() 函数
public void run() { final int size = mMonitors.size(); for (int i = 0 ; i < size ; i++) { synchronized (Watchdog.this) { mCurrentMonitor = mMonitors.get(i); } mCurrentMonitor.monitor(); } synchronized (Watchdog.this) { mCompleted = true; mCurrentMonitor = null; }}
通过一个循环分别调用每一个 monitor 的 monitor() 函数,如果其中一个阻塞了,则无法退出该循环,mCompleted 状态也不会变成 true,如果 mCompleted 为false,WatchDog 只能通过时间来进一步确认状态,在下面一节中会详细说明。
用 AMS 中的 monitor 来举例:
frameworks/base/services/core/java/com/android/server/am/AMS.java public void monitor() { synchronized (this) { } }
AMS 的 monitor 就是为了 check AMS 是否死锁了。
7. WatchDog 的run() 函数
上面的基本函数都大概解析完了,对于一个Thread 那最重要的肯定还是run() 函数,这里也是WatchDog 的监测机制所在,因为代码比较多,这里裁剪分析。
step 1:建立死循环
WatchDog 在整个系统运行过程中都需要存在的,除非了进程或是系统重启了,不然WatchDog 需要长期运行。
public void run() { boolean waitedHalf = false; while (true) { ... } }
step2:创建schedule
synchronized (this) { long timeout = CHECK_INTERVAL; for (int i=0; i<mHandlerCheckers.size(); i++) { HandlerChecker hc = mHandlerCheckers.get(i); hc.scheduleCheckLocked(); }
读取所有的 mHandlerCheckers,执行每一个 checker 中的 scheduleCheckLocked() 函数,下面会等待 30 s 的时间,然后在收集这些 checker 的状态。
注意两点:
- timeout 默认为30s(DEFAULT_TIMEOUT / 2),所有的 schedule 启动完会 wait 30s 等待所有的 schedule 都能顺利执行完;
- 启动schedule 会加上锁,与 HandlerChecker 的run() 函数互斥,这就要求,在wait 之前,HandlerChecker 的runnable 是无法运行的,下文在 wait() 的时候会释放this 对象的锁,runnable 才可以顺利执行(wait/synchronized 使用);
step3: 等待30s
long start = SystemClock.uptimeMillis(); while (timeout > 0) { if (Debug.isDebuggerConnected()) { debuggerWasConnected = 2; } try { wait(timeout); // Note: mHandlerCheckers and mMonitorChecker may have changed after waiting } catch (InterruptedException e) { Log.wtf(TAG, e); } if (Debug.isDebuggerConnected()) { debuggerWasConnected = 2; } timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start); }
这里等待30s,直到时间完全用完。此处调用 wait() 会临时释放 this 这把锁,这样所有的 checker 就会进入下一步处理,即 runnable() 处理。
step4:收集check 完成状态
final int waitState = evaluateCheckerCompletionLocked(); if (waitState == COMPLETED) { // The monitors have returned; reset waitedHalf = false; continue; } else if (waitState == WAITING) { // still waiting but within their configured intervals; back off and recheck continue; } else if (waitState == WAITED_HALF) { if (!waitedHalf) { Slog.i(TAG, "WAITED_HALF"); // We've waited half the deadlock-detection interval. Pull a stack // trace and wait another half. ArrayList<Integer> pids = new ArrayList<>(mInterestingJavaPids); ActivityManagerService.dumpStackTraces(pids, null, null, getInterestingNativePids(), null); waitedHalf = true; } continue; }
在 30s 时间等待完成后,通过 evaluateCheckerCompletionLocked() 函数来收集所有 checkers 的状态。
状态分:
- COMPLETED:顺利完成
- WAITING:有可能 HandlerChecker 中的 monitor 比较多,而部分 monitor 占用时间比较长,这就导致有些 monitor 执行不到30s时,wait 就结束了,这时候会返回WAITING 状态,继续等待下一个schedule;
- WATIED_HALF:同WAITING,会继续等待下一个schedule,不同于 WAITING,WAITED_HALF 会通过AMS 去 dump 一次 stack;
- OVERDUE:有 monitor 执行超过了60s,这个是不允许的,step5 会处理OVERDUE的情况
step5:处理OVERDUE 情况
blockedCheckers = getBlockedCheckersLocked(); subject = describeCheckersLocked(blockedCheckers);
最开始是统计出OVERDUE 的HandlerChecker,存入blockedCheckers 中,并统计这些HandlerChecker 的描述。
private ArrayList<HandlerChecker> getBlockedCheckersLocked() { ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>(); for (int i=0; i<mHandlerCheckers.size(); i++) { HandlerChecker hc = mHandlerCheckers.get(i); if (hc.isOverdueLocked()) { checkers.add(hc); } } return checkers; }
统计所有状态OVERDUE 的HandlerChecker。
private String describeCheckersLocked(List<HandlerChecker> checkers) { StringBuilder builder = new StringBuilder(128); for (int i=0; i<checkers.size(); i++) { if (builder.length() > 0) { builder.append(", "); } builder.append(checkers.get(i).describeBlockedStateLocked()); } return builder.toString(); }
统计所有符合条件的OVERDUE 的HandlerChecker 的描述,以逗号分割。
例如:
11-06 00:05:28.603 2197 2441 W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor com.android.server.am.ActivityManagerService on foreground thread (android.fg), Blocked in handler on ActivityManager (ActivityManager)
接着就是保存日志,包括一些运行时的堆栈信息,这些日志是我们解决Watchdog问题的重要依据。如果判断需要杀掉system_server进程,则给当前进程(system_server)发送signal 9,详细信息看 Android 系统中WatchDog 日志分析
8. 通过AMS引入WatchDog 监控的根本形式
Watchdog.getInstance().addMonitor(this); Watchdog.getInstance().addThread(mHandler);
来看下一直说的monitor 是什么东西:
public void monitor() { synchronized (this) { } }
这里其实就是确认AMS 中是否存在死锁,30秒后还是没有放出来这个锁,WatchDog 会在给一次机会,如果还是没有释放的话,会打印堆栈信息并且结束AMS 所在进程。
需要注意的是WatchDog 的监控,除了WatchDog 构造函数中的默认Thread,外界若需要添加监控,需要通过两种途径:
- addMonitor(Monitor monitor);
- addThread(Handler thread);
两个接口区别在于参数,一个传入的是monitor,例如WatchDog 内部的 BinderThreadMonitor 或AMS 的monitor,monitor() 的处理是在 fg thread 中;另一个传入的是Handler,通过第 5 节得知,addThread() 会创建一个HandlerChecker 对象单独监控;
那么,结合 HandlerChecker 的 **scheduleCheckLocked() **函数,可以认定,WatchDog 可以监控的形式有两种:
- monitor:HandlerChecker专门用来维护monitor,run() 中逐个确认monitor是否正常运行
- handler - msg:HandlerChecker 专门用来维护消息的监控,run() 中不会确认 monitor 的状态,而是直接将 mCompleted 置为 true,但是前提是Handler 中的 Looper 此时如果正在处理消息,这个消息不能阻塞此次 schedule 的 post,如果 Looper 中的消息处理超过 30s,那么HandlerChecker 中的 run() 只能等下一次 30s 时候才能有机会执行;
9. WatchDog 针对系统的 monitor 监控
- ActivityManagerService
- InputManagerService
- MediaProjectionManagerService
- MediaRouterService
- MediaSessionService
- PowerManagerService
- StorageManagerService
- TvRemoteService
- WindowManagerService
都是通过addMonitor() 添加到 fg thread 的 HandlerChecker 中。
10. WatchDog 中Handler msg监控
都是通过 Handler 的方式添加到各自的 HandlerChecker 中,有的是在 WatchDog 的构造的时候添加,有的是后期通过 addThread() 方式添加。
11. AMS WD 举例
第 8 节中列出了AMS 代码中 addMonitor() 和 addThread() 两种方式都使用了,所以在dump trace 的时候会列举 fg thread 的 stack trace 和 ActivityManager stack trace:
11-06 00:05:28.603 2197 2441 W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor com.android.server.am.ActivityManagerService on foreground thread (android.fg), Blocked in handler on ActivityManager (ActivityManager)11-06 00:05:28.603 2197 2441 W Watchdog: foreground thread stack trace:11-06 00:05:28.603 2197 2441 W Watchdog: at com.android.server.am.ActivityManagerService.monitor(ActivityManagerService.java:23862)11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.Watchdog$HandlerChecker.run(Watchdog.java:211)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Handler.handleCallback(Handler.java:790)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Handler.dispatchMessage(Handler.java:99)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Looper.loop(Looper.java:164)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.HandlerThread.run(HandlerThread.java:65)11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.ServiceThread.run(ServiceThread.java:46)11-06 00:05:28.604 2197 2441 W Watchdog: ActivityManager stack trace:11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.am.ActivityManagerService.idleUids(ActivityManagerService.java:23305)11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.am.ActivityManagerService$MainHandler.handleMessage(ActivityManagerService.java:2428)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Handler.dispatchMessage(Handler.java:106)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Looper.loop(Looper.java:164)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.HandlerThread.run(HandlerThread.java:65)11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.ServiceThread.run(ServiceThread.java:46)11-06 00:05:28.604 2197 2441 W Watchdog: *** GOODBYE
具体的可以看 Android 系统中WatchDog 日志分析
总结:
Android 中的WatchDog 主要是监测系统中重要服务,例如AMS、WMS 等,当注册的monitor 无法通过检测,或者是消息处理超时的时候就会触发WatchDog,最后可能会引起系统的重启。
下一篇博文 Android 系统中WatchDog 日志分析 中会结合实例详解run() 第 4 步保存日志。