android进阶之Watchdog检查系统异常机制

Watchdog简介

Watchdog是Android系统提供的以后总检查系统异常的机制,正如其名,“看门狗”,它看的是Android框架层的几个核心服务。Watchdog一旦发现的AMS、WMS和PMS等核心服务的主线程阻塞,就清除systemserver进程,我们知道,一单SystemServer进程停止,Zygote进程就会自杀,系统就会重启。

Watchdog继承Thread,它是一个线程类,它的监控任务运行在独立的线程中,其中有两个非常重要的ArrayList变量,分别是mMonitors和mHandlerCheckers。变量mMonitors存放的是对象Monitor的子类,如AMS、PMS等。对于这类的监控主要是判断它们是否死锁。而对于变量mHandlerCheckers是ArrayList集合,里面存放的是HandlerChecker的对象,Watchdog主要是监控它里面重要的线程的handler是否阻塞,即监控重要线程的消息队列是否阻塞。mMonitors是一个HandlerChecker类型的对象。实际上,HandlerChecker类是Watchdog的核心,它负责对各个监控对象进行监控。具体的对应关系如下图所示:

Watchdog的启动

Watchdog在SystemServer系统服务进程中被初始化和启动,接下来看看SystemServer怎么把它初始化和启动起来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class SystemServer {
.....
public static void main(String[] args) {
new SystemServer().run();
}
.....
private void run() {
startBootstrapServices();
startCoreServices();
startOtherServices();
.....
}
private void startOtherServices() {
final Context context = mSystemContext;
........
final Watchdog watchdog = Watchdog.getInstance();
watchdog.init(context, mActivityManagerService);
.....
Watchdog.getInstance().start();
.....
}
......
}

当SystemServer进程启动之后,就会进入它的main()方法,而它的main()方法有调用了startOtherService()方法,这个方法就把Watchdog启动起来了。startOtherService()方法首先通过Watchdog的getInstance()方法获得Watchdog对象,使用的是单例模式。接着调用init()方法来做进一步操作,最后调用Watchdog的start()方法启动线程进行监控。

1
2
3
4
5
6
7
8
9
public class Watchdog extends Thread {
static final boolean DB = false;
//调试用,默认为false
static final long DEFAULT_TIMEOUT = DB ? 10*1000 : 60*1000;
static final long CHECK_INTERVAL = DEFAULT_TIMEOUT / 2;
//这两个变量非常重要,由于DB为false,所以DEFAULT_TIMEOUT为60秒,
//主要线程blocked了60秒,watchdog就会去kill进程systemServer。
//CHECK_INTERVAL为30秒,意思是每隔30秒去check一次
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private Watchdog() {
super("watchdog");
//监听foreground thread,NetworkManagerService、NativeDaemonConnector、
//MountService属于foreground thread
mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
"foreground thread", DEFAULT_TIMEOUT);
mHandlerCheckers.add(mMonitorChecker);
//监听SystemServer主线程
mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
"main thread", DEFAULT_TIMEOUT));
//监听UI线程,包括ActivityManagerService、WindowManagerService、PointerEventDispatcher和DisplayManagerService
mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
"ui thread", DEFAULT_TIMEOUT));
//监听IO线程,包括BluetoothManagerService、MountService、PacManager等
mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
"i/o thread", DEFAULT_TIMEOUT));
//监听display thread线程,包括DIsplayManagerService、InputManagerService、WindowManagerService
mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
"display thread", DEFAULT_TIMEOUT));
//monitor可用的binder线程
addMonitor(new BinderThreadMonitor());
}

接下来看看Watchdog的init()方法:

1
2
3
4
5
6
7
8
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);
}

init()方法的实现比较简单,主要是给mActivity对象赋值,mActivity是一个全局的AMS对象,init()方法中会注册重启广播接收器RebotRequestReceiver,用来负责接收系统内部发出的系统重启请求。

Watchdog的监听

Watchdog继承Thread,所以调用start()方法之后,就会进入Watchdog的run()方法,它来做监控工作。

Watchdog主要提供了addMonitor()方法来添加监控服务对象,而在添加这些服务对象到Watchdog监控之前,这些服务必须要实现Watchdog.Monitor接口。比如AMS就首先实现了Watchdog.Monitor接口,然后在它的构造方法里把自己添加到Watchdog中,让Watchdog检测自己是否死锁,代码如下:

1
2
3
4
5
public ActivityManagerService(Context systemContext) {
.......
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
}

通过addMonitor()方法和addThread()方法,分别把AMS和mHandler添加到Watchdog中。mHandler是一个AMS中的Handler对象,意思是Watchdog不仅要监控AMS是否死锁,还要监控mHandler分发消息的时候是否阻塞。接下来看看Watchdog的addMonitor()方法:

1
2
3
4
5
6
7
8
public void addMonitor(Monitor monitor) {
synchronized (this) {
if (isAlive()) {
throw new RuntimeException("Monitors can't be added once the Watchdog is running");
}
mMonitorChecker.addMonitor(monitor);
}
}

AMS实现了Watchdog.Monitor接口,所以这个monitor就是AMS对象。mMontorChecker是一个HandlerChecker对象,HandlerChecker类实现了java的Runnable类:

1
2
3
4
5
6
7
8
public final class HandlerChecker implements Runnable {
.......
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
......
public void addMonitor(Monitor monitor) {
mMonitors.add(monitor);
}
}

mMontors是一个Monitor类型的ArrayList,系统启动完成之后,系统服务都会添加到这个mMonitors中。AMS中还调用了Watchdog的addThread()方法,下面了解一下:

1
2
3
4
5
6
7
8
9
public void addThread(Handler thread, long timeoutMillis) {
synchronized (this) {
if (isAlive()) {
throw new RuntimeException("Threads can't be added once the Watchdog is running");
}
final String name = thread.getLooper().getThread().getName();
mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
}
}

mHandlerCheckers是一个HandlerChecker的ArrayList:

1
final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<>();

通过mHandlerCheckers的add()方法把Handler添加进去,从而监控那些重要的线程里的Handler是否阻塞。

当调用Watchdog.getInstance().start()时,则进入线程“watchdog”的run()方法, 该方法分成两部分:

  • 前半段用于监测是否触发超时
  • 后半段当触发超时时输出各种信息,最后杀死SystemServer进程

接下来进入Watchdog的run()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public void run() {
boolean waitedHalf = false;
while (true) {
final ArrayList<HandlerChecker> blockedCheckers;
final String subject;
final boolean allowRestart;
int debuggerWasConnected = 0;
synchronized (this) {
long timeout = CHECK_INTERVAL; //CHECK_INTERVAL=30s
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
//执行所有的Checker的监控方法, 每个Checker记录当前的mStartTime
hc.scheduleCheckLocked();
}
if (debuggerWasConnected > 0) {
debuggerWasConnected--;
}
long start = SystemClock.uptimeMillis();
//通过循环,保证执行30s才会继续往下执行
while (timeout > 0) {
if (Debug.isDebuggerConnected()) {
debuggerWasConnected = 2;
}
try {
wait(timeout); //触发中断,直接捕获异常,继续等待.
} catch (InterruptedException e) {
Log.wtf(TAG, e);
}
if (Debug.isDebuggerConnected()) {
debuggerWasConnected = 2;
}
timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
}
//评估Checker状态
final int waitState = evaluateCheckerCompletionLocked();
if (waitState == COMPLETED) {
waitedHalf = false;
continue;
} else if (waitState == WAITING) {
continue;
} else if (waitState == WAITED_HALF) {
if (!waitedHalf) {
//首次进入等待时间过半的状态
ArrayList<Integer> pids = new ArrayList<Integer>();
pids.add(Process.myPid());
//输出system_server和3个native进程的traces
ActivityManagerService.dumpStackTraces(true, pids, null, null,
NATIVE_STACKS_OF_INTEREST);
waitedHalf = true;
}
continue;
}
... //进入这里,意味着Watchdog已超时
}
...
}
}

该方法主要功能:

  1. 执行所有的Checker的监控方法scheduleCheckLocked()
    • 当mMonitor个数为0(除了android.fg线程之外都为0)且处于poll状态,则设置mCompleted = true;
    • 当上次check还没有完成, 则直接返回.
  2. 等待30s后, 再调用evaluateCheckerCompletionLocked来评估Checker状态;
  3. 根据waitState状态来执行不同的操作:
    • 当COMPLETED或WAITING,则相安无事;
    • 当WAITED_HALF(超过30s)且为首次, 则输出system_server和3个Native进程的traces;
    • 当OVERDUE, 则输出更多信息.

接下来看看scheduleCheckLocked()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public final class HandlerChecker implements Runnable {
...
public void scheduleCheckLocked() {
if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
mCompleted = true; //当目标looper正在轮询状态则返回。
return;
}
if (!mCompleted) {
return; //有一个check正在处理中,则无需重复发送
}
mCompleted = false;
mCurrentMonitor = null;
// 记录当下的时间
mStartTime = SystemClock.uptimeMillis();
//发送消息,插入消息队列最开头, 见下方的run()方法
mHandler.postAtFrontOfQueue(this);
}
public void run() {
final int size = mMonitors.size();
for (int i = 0 ; i < size ; i++) {
synchronized (Watchdog.this) {
mCurrentMonitor = mMonitors.get(i);
}
//回调具体服务的monitor方法
mCurrentMonitor.monitor();
}
synchronized (Watchdog.this) {
mCompleted = true;
mCurrentMonitor = null;
}
}
}

该方法主要功能: 向Watchdog的监控线程的Looper池的最头部执行该HandlerChecker.run()方法, 在该方法中调用monitor(),执行完成后会设置mCompleted = true. 那么当handler消息池当前的消息, 导致迟迟没有机会执行monitor()方法, 则会触发watchdog

其中postAtFrontOfQueue(this),该方法输入参数为Runnable对象,根据消息机制, 最终会回调HandlerChecker中的run方法,该方法会循环遍历所有的Monitor接口,具体的服务实现该接口的monitor()方法。而monitor()方法只是简单获取锁,如AMS的monitor方法:

1
2
3
public void monitor() {
synchronized (this) { }
}

因此,AMS判断是否死锁就是去看能不能拿到自己的锁

run()方法后面的实现则是当触发超时时输出各种信息,最后杀死SystemServer进程,此处就不继续贴出源码了。。。。

Watchdog总结

Watchdog是一个运行在system_server进程的名为”watchdog”的线程::

  • Watchdog运作过程,当阻塞时间超过1分钟则触发一次watchdog,会杀死system_server,触发上层重启;
  • mHandlerCheckers:记录所有的HandlerChecker对象的列表,包括foreground, main, ui, i/o, display线程的handler;
  • mHandlerChecker.mMonitors:记录所有Watchdog目前正在监控Monitor,所有的这些monitors都运行在foreground线程。
  • 有两种方式加入Watchdog的监控:
    • addThread():用于监测Handler对象,默认超时时长为60s.这种超时往往是所对应的handler线程消息处理得慢;
    • addMonitor(): 用于监控实现了Watchdog.Monitor接口的服务.这种超时可能是”android.fg”线程消息处理得慢,也可能是monitor迟迟拿不到锁;