一 概述
Watchdog 中文翻译是“看门狗”,有保护,监控的意思。最早引入 Watchdog 是在单片机系统中,由于单片机的工作环境容易受到外界磁场的干扰,导致程序“跑飞”,造成整个系统无法正常工作,因此,引入了一个“看门狗”,对单片机的运行状态进行实时监测,针对运行故障做一些保护处理,譬如让系统重启。这种 Watchdog 属于硬件层面,必须有硬件电路的支持。
Linux 也引入了 Watchdog,在 Linux 内核下,当 Watchdog 启动后,便设定了一个定时器,如果在超时时间内没有对 /dev/Watchdog 进行写操作,则会导致系统重启。通过定时器实现的 Watchdog 属于软件层面。
Android 在 framework 层设计了一个软件层面的 Watchdog,用于定期检测一些重要的系统服务,当出现故障(系统核心服务和重要线程处于 Blocked 状态)时,通常会让 Android 系统重启。由于这种机制的存在,就经常会出现 system_server 进程被 Watchdog 杀掉而发生手机重启的问题。
watchdog 的源码很简单,主要有以下两个功能:
- 监控 system_server 中几个关键的锁,原理是在 android_fg 线程中尝试加锁
- 监控几个常用线程的执行时间,原理是在这几个线程中执行任务
二 WatchDog初始化
2.1 startBootstrapServices
SystemServer.java
private void startBootstrapServices() {
......
//创建watchdog
final Watchdog watchdog = Watchdog.getInstance();
watchdog.start();
......
//待AMS启动后注册reboot广播
watchdog.init(mSystemContext, mActivityManagerService);
......
}
system_server 进程在启动的过程中初始化 WatchDog,主要有:
- 创建 watchdog 对象,该对象本身继承于 Thread
- 调用 start() 开始工作
- 注册 reboot 广播
2.2 Watchdog.getInstance
Watchdog.java
public static Watchdog getInstance() {
if (sWatchdog == null) {
sWatchdog = new Watchdog();//单例模式,创建实例对象
}
return sWatchdog;
}
2.3 创建Watchdog
Watchdog.java
public class Watchdog extends Thread {
//所有的HandlerChecker对象组成的列表
//This handler will be used to post message back onto the main thread
final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<>();
......
private Watchdog() {
super("watchdog");
//将前台线程加入队列
mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
"foreground thread", DEFAULT_TIMEOUT);
mHandlerCheckers.add(mMonitorChecker);
//将主线程加入队列
mHandlerCheckers.add(new HandlerChecker(
new Handler(Looper.getMainLooper()),
"main thread", DEFAULT_TIMEOUT));
//将ui线程加入队列
mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
"ui thread", DEFAULT_TIMEOUT));
//将i/o线程加入队列
mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
"i/o thread", DEFAULT_TIMEOUT));
//将display线程加入队列
mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
"display thread", DEFAULT_TIMEOUT));
// And the animation thread.
mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(),
"animation thread", DEFAULT_TIMEOUT));
// And the surface animation thread.
mHandlerCheckers.add(new HandlerChecker(
SurfaceAnimationThread.getHandler(),
"surface animation thread", DEFAULT_TIMEOUT));
//Initialize monitor for Binder threads
addMonitor(new BinderThreadMonitor());
mOpenFdMonitor = OpenFdMonitor.create();
// See the notes on DEFAULT_TIMEOUT.
assert DB ||
DEFAULT_TIMEOUT >
ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
}
......
}
Watchdog 是一个单例线程,继承于 Thread,创建的线程名为 ”watchdog”。在 SystemServer 启动过程中初始化 Watchdog。Watchdog 在初始化时,会构建很多 HandlerChecker,大致可以分为两类:
- Monitor Checker,用于检查 Monitor 对象可能发生的死锁,AMS,IMS,WMS PMS 等核心的系统服务都是 Monitor 对象
- Looper Checker,用于检查线程的消息队列是否长时间处于工作状态。Watchdog 自身的消息队列,ui,io, Display 这些全局的消息队列都是被检查的对象。此外,一些重要的线程的消息队列,也会加入到 Looper Checker中,譬如 AMS,WMS 这些是在对应的对象初始化时加入的
两类 HandlerChecker 的侧重点不同,Monitor Checker 预警我们不能长时间持有核心系统服务的对象锁,否则会阻塞很多函数的运行;Looper Checker 预警我们不能长时间的霸占消息队列,否则其他消息将得不到处理。这两类都会导致系统卡住 (System Not Responding)。
2.3.1 Watchdog.HandlerChecker
public final class HandlerChecker implements Runnable {
private final Handler mHandler;//Handler对象
private final String mName;//线程描述名
private final long mWaitMax;//最长等待时间
//记录着监控的服务
private final ArrayList<Monitor> mMonitors =
new ArrayList<Monitor>();
//添加监控服务队列
private final ArrayList<Monitor> mMonitorQueue =
new ArrayList<Monitor>();
private boolean mCompleted;//开始检查时先设置成false
private Monitor mCurrentMonitor;
private long mStartTime;//开始准备检查的时间点
private int mPauseCount;
HandlerChecker(Handler handler, String name, long waitMaxMillis) {
mHandler = handler;
mName = name;
mWaitMax = waitMaxMillis;
mCompleted = true;
}
void addMonitorLocked(Monitor monitor) {
mMonitorQueue.add(monitor);
}
public void scheduleCheckLocked() {
if (mCompleted) {
mMonitors.addAll(mMonitorQueue);
mMonitorQueue.clear();
}
if ((mMonitors.size() == 0 &&
mHandler.getLooper().getQueue().isPolling())
|| (mPauseCount > 0)) {
mCompleted = true;
return;
}
if (!mCompleted) {
// we already have a check in flight, so no need
return;
}
mCompleted = false;
mCurrentMonitor = null;
mStartTime = SystemClock.uptimeMillis();
mHandler.postAtFrontOfQueue(this);
}
......
@Override
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;
}
}
......
}
HandlerChecker 继承自 Runnable,里面维护着监控服务的 Monitor 对象列表,使用 addMonitor 方法将服务监听者加入列表。
2.3.2 Watchdog.addMonitor
现在我们来分析在 Watchdog 构造函数中调用的 addMonitor(new BinderThreadMonitor());
public void addMonitor(Monitor monitor) {
synchronized (this) {
mMonitorChecker.addMonitorLocked(monitor);
}
}
public final class HandlerChecker implements Runnable {
......
//添加监控服务队列
private final ArrayList<Monitor> mMonitorQueue =
new ArrayList<Monitor>();
......
HandlerChecker(Handler handler, String name, long waitMaxMillis) {
mHandler = handler;
mName = name;
mWaitMax = waitMaxMillis;
mCompleted = true;
}
void addMonitorLocked(Monitor monitor) {
//添加monitor到mMonitorQueue中
mMonitorQueue.add(monitor);
}
......
}
可知最终是把 BinderThreadMonitor 对象添加到 mMonitorQueue 中了。
Watchdog.java
private static final class BinderThreadMonitor implements Watchdog.Monitor {
public void monitor() {
Binder.blockUntilThreadAvailable();
}
}
blockUntilThreadAvailable 最终调用的是 IPCThreadState,等待有空闲的 binder 线程
IPCThreadState.cpp
void IPCThreadState::blockUntilThreadAvailable()
{
pthread_mutex_lock(&mProcess->mThreadCountLock);
while (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads) {
//等待正在执行的binder线程小于进程最大binder线程上限(16个)
pthread_cond_wait(&mProcess->mThreadCountDecrement,
&mProcess->mThreadCountLock);
}
pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
可见 addMonitor(new BinderThreadMonitor()) 最终的目标是将 Binder 线程添加到 android.fg 线程的handler(mMonitorChecker) 来检查是否工作正常。
2.4 init
Watchdog.java
public void init(Context context, ActivityManagerService activity) {
mActivity = activity;
//注册reboot广播接收者
context.registerReceiver(new RebootRequestReceiver(),
new IntentFilter(Intent.ACTION_REBOOT),
android.Manifest.permission.REBOOT, null);
}
2.4.1 RebootRequestReceiver
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);
}
}
2.4.2 rebootSystem
void rebootSystem(String reason) {
Slog.i(TAG, "Rebooting system because: " + reason);
IPowerManager pms = (IPowerManager)ServiceManager.getService(
Context.POWER_SERVICE);
try {
//通过PowerManager执行reboot操作
pms.reboot(false, reason, false);
} catch (RemoteException ex) {
}
}
最终是通过 PowerManagerService 来完成重启操作,具体的重启流程后续会单独讲述。
三 Watchdog检测机制
当调用 watchdog.start() 时,则进入线程 “watchdog” 的 run() 方法,该方法分成两部分:
- 前半部,用于监测是否触发超时
- 后半部,当触发超时则输出各种信息
3.1 run
public void run() {
boolean waitedHalf = false;
while (true) {
final List<HandlerChecker> blockedCheckers;
final String subject;
final boolean allowRestart;
int debuggerWasConnected = 0;
synchronized (this) {
long timeout = CHECK_INTERVAL;//CHECK_INTERVAL=30s
//每30s轮询所有的monitor
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
//执行所有的Checker的监控方法, 每个Checker记录当前的mStartTime
hc.scheduleCheckLocked();
}
if (debuggerWasConnected > 0) {
debuggerWasConnected--;
}
// positive on when to kill things.
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);
}
boolean fdLimitTriggered = false;
if (mOpenFdMonitor != null) {
fdLimitTriggered = mOpenFdMonitor.monitor();
}
if (!fdLimitTriggered) {//评估monitor完成状态,并做相应操作
//评估Checker状态
final int waitState = evaluateCheckerCompletionLocked();
if (waitState == COMPLETED) {
//已完成,跳过
waitedHalf = false;
continue;
} else if (waitState == WAITING) {
//waiting状态,但并未超过timeout
continue;
} else if (waitState == WAITED_HALF) {
if (!waitedHalf) {
Slog.i(TAG, "WAITED_HALF");
//首次进入等待时间过半的状态
//block 30s时候先dump一次system_server和一些native的 stack
ArrayList<Integer> pids = new ArrayList<Integer>();
pids.add(Process.myPid());
//输出system_server和3个native进程的traces
ActivityManagerService.dumpStackTraces(pids, null, null,
getInterestingNativePids());
waitedHalf = true;
//waitedHalf这个变量保证下一次过来还是当前状态不用dump堆栈,交给下面部分去dump.
}
continue;
}
// 如果状态是 overdue!,也就是超过60秒
//获取卡住60s的hanlerchecker
blockedCheckers = getBlockedCheckersLocked();
subject = describeCheckersLocked(blockedCheckers);
} else {
blockedCheckers = Collections.emptyList();
subject = "Open FD high water mark reached";
}
allowRestart = mAllowRestart;
}
//代码执行到这里说明此时system_server中的监控线程已经卡住并且超过60s,
//此时会dump堆栈并kill system_server 然后restart
EventLog.writeEvent(EventLogTags.WATCHDOG, subject);
ArrayList<Integer> pids = new ArrayList<>();
pids.add(Process.myPid());
if (mPhonePid > 0) pids.add(mPhonePid);
//dump即将被kill进程的堆栈
final File stack = ActivityManagerService.dumpStackTraces(
pids, null, null, getInterestingNativePids());
//多留一点时间保证dump信息可以保存完整
SystemClock.sleep(5000);
//触发内核来dump所有被block的线程,并输出所有CPU上堆栈到kernel log中
doSysRq('w');
doSysRq('l');
//Try to add the error to the dropbox
Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
public void run() {
if (mActivity != null) {
mActivity.addErrorToDropBox(
"watchdog", null, "system_server", null, null, null,
subject, null, stack, null);
}
}
};
dropboxThread.start();
try {
// wait up to 2 seconds for it to return.
dropboxThread.join(2000);
} catch (InterruptedException ignored) {}
IActivityController controller;
synchronized (this) {
controller = mController;
}
if (controller != null) {
Slog.i(TAG, "Reporting stuck state to activity controller");
try {
Binder.setDumpDisabled("Service dumps disabled due"+
"to hung system process.");
// 1 = keep waiting, -1 = kill system
int res = controller.systemNotResponding(subject);
if (res >= 0) {
Slog.i(TAG, "Activity controller requested to coninue to wait");
waitedHalf = false;
continue;
}
} catch (RemoteException e) {
}
}
// Only kill the process if the debugger is not attached.
if (Debug.isDebuggerConnected()) {
debuggerWasConnected = 2;
}
if (debuggerWasConnected >= 2) {
Slog.w(TAG, "Debugger connected: Watchdog is *not* "+
"killing the system process");
} else if (debuggerWasConnected > 0) {
Slog.w(TAG, "Debugger was connected: Watchdog is *not* " +
"killing the system process");
} else if (!allowRestart) {
Slog.w(TAG, "Restart not allowed: Watchdog is *not* "+
"killing the system process");
} else {
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
Slog.w(TAG, "*** GOODBYE!");
// kill 掉system_server
Process.killProcess(Process.myPid());
System.exit(10);
}
waitedHalf = false;
}
}
这个方法是 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,则 dump 更多信息
4.Watchdog 检测到异常的信息收集
- AMS.dumpStackTraces:输出Java和Native进程的栈信息
- doSysRq
- dropBox
收集完信息后便会杀死 system_server 进程。此处 allowRestart 默认值为 true,当执行 am hang 操作则设置不允许重启 (allowRestart =false), 则不会杀死 system_server 进程。
由此可见当触发一次 Watchdog,则必然会调用两次 AMS.dumpStackTraces,也就是说 system_server 和3个 Native 进程的 traces 信息会输出两遍,且时间间隔超过 30s。
3.2 scheduleCheckLocked
public final class HandlerChecker implements Runnable {
......
public void scheduleCheckLocked() {
if (mCompleted) {
// 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)) {
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 的监控线程的消息队列的最头部发送本 HandlerChecker 任务,并执行该 HandlerChecker 的 run() 方法,在该方法中调用 monitor(),执行完成后会设置 mCompleted = true。当 handler 消息池无法处理当前的消息,导致迟迟没有机会执行 monitor() 方法,则会触发 watchdog。
其中 postAtFrontOfQueue(this),该方法输入参数为 Runnable 对象,根据消息机制, 最终会回调 HandlerChecker 中的 run 方法,该方法会循环遍历所有的 Monitor 接口,具体的服务实现该接口的 monitor() 方法。
可能存在的问题:如果有其他消息不断地调用 postAtFrontOfQueue() 也可能导致 watchdog 没有机会执行;或者是每个 monitor 消耗一些时间,累加起来超过1分钟造成的 watchdog。这些都是非常规的 Watchdog。
3.3 evaluateCheckerCompletionLocked
private int evaluateCheckerCompletionLocked() {
int state = COMPLETED;
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
state = Math.max(state, hc.getCompletionStateLocked());
}
return state;
}
public int getCompletionStateLocked() {
if (mCompleted) {
return COMPLETED;
} else {
long latency = SystemClock.uptimeMillis() - mStartTime;
// mWaitMax默认是60s
if (latency < mWaitMax/2) {
return WAITING;
} else if (latency < mWaitMax) {
return WAITED_HALF;
}
}
return OVERDUE;
}
获取 mHandlerCheckers 列表中等待状态值最大的 state。
- COMPLETED = 0:等待完成
- WAITING = 1:等待时间小于 DEFAULT_TIMEOUT 的一半,即 30s
- WAITED_HALF = 2:等待时间处于 30s~60s 之间
- OVERDUE = 3:等待时间大于或等于 60s
3.4 getBlockedCheckersLocked
获取卡住 60s 的 hanlerchecker
private ArrayList<HandlerChecker> getBlockedCheckersLocked() {
ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>();
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
//将所有没有完成,且超时的checker加入队列
if (hc.isOverdueLocked()) {
checkers.add(hc);
}
}
return checkers;
}
boolean isOverdueLocked() {
return (!mCompleted) &&
(SystemClock.uptimeMillis() > mStartTime + mWaitMax);
}
3.5 describeCheckersLocked
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(", ");
}
// 输出所有的checker信息
builder.append(checkers.get(i).describeBlockedStateLocked());
}
return builder.toString();
}
public String describeBlockedStateLocked() {
if (mCurrentMonitor == null) {//非前台线程进入该分支
return "Blocked in handler on " + mName +
" (" + getThread().getName() + ")";
} else {//前台线程进入该分支
return "Blocked in monitor " +
mCurrentMonitor.getClass().getName()
+ " on " + mName + " (" + getThread().getName() + ")";
}
}
将所有执行时间超过1分钟的 handler 线程或者 monitor 都记录下来。
- Blocked in handler,意味着相应的线程处理当前消息时间超过1分钟
- Blocked in monitor,意味着相应的线程处理当前消息时间超过1分钟,或者 monitor 迟迟拿不到锁
四 总结
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 迟迟拿不到锁
以下情况,即使触发了 Watchdog,也不会杀掉 system_server 进程:
- monkey:设置 IActivityController,拦截 systemNotResponding 事件,比如 monkey
- hang:执行 am hang 命令,不重启
- debugger:连接 debugger 的情况,不重启
4.1 监控Handler线程
Watchdog 监控的线程有:
默认地 DEFAULT_TIMEOUT=60s,调试时才为 10s 方便找出潜在的 ANR 问题。
线程名 | 对应handler | 说明 | Timeout |
main | new Handler(Looper.getMainLooper()) | 当前主线程 | 1min |
android.fg | FgThread.getHandler | 前台线程 | 1min |
android.ui | UiThread.getHandler | UI线程 | 1min |
android.io | IoThread.getHandler | I/O线程 | 1min |
android.display | DisplayThread.getHandler | display线程 | 1min |
ActivityManager | AMS.MainHandler | AMS线程 | 1min |
PowerManagerService | PMS.PowerManagerHandler | PMS线程 | 1min |
PackageManager | PermissionManagerService.mHandler | PMS线程 | 1min |
PackageManager | PKMS.PackageHandler | PKMS线程 | 10min |
RollbackManagerServiceHandler | RollbackManagerService.mHandlerThread | RMS线程 | 10min |
在目前的 android 10.0 中 watchdog 会监控 system_server 进程中的以上 10 个线程:
- 前8个线程的 Looper 消息处理时间不得超过1分钟
- PackageManager 和 RollbackManagerServiceHandler 线程的处理时间不得超过10分钟
4.2 监控同步锁
能够被 Watchdog 监控的系统服务需要实现 Watchdog.Monitor 接口,并实现其中的 monitor() 方法。运行在 android.fg 线程,系统中实现该接口类主要有:
- ActivityManagerService
- WindowManagerService
- TvRemoteService
- InputManagerService
- PowerManagerService
- StorageManagerService
- BinderThreadMonitor
- MediaProjectionManagerService
- MediaRouterService
- MediaSessionService
4.3 输出信息
watchdog 在 check 过程中出现阻塞 1 分钟的情况,则会输出:
1.AMS.dumpStackTraces:输出 system_server 和3个native 进程的 traces
- 该方法会输出两次,第一次在超时 30s 的地方;第二次在超时 1min
2.doSysRq,触发 kernel 来 dump 所有阻塞线程,输出所有 CPU 的 backtrace 到 kernel log
- 通过向节点 /proc/sysrq-trigger 写入字符
3.dropBox,输出文件到 /data/system/dropbox,内容是 trace + blocked 信息
4.杀掉 system_server,进而触发 zygote 进程自杀,从而重启上层 framework