文章来源:https://justinwei.blog.csdn.net/article/details/81021257
请转载朋友标明出处,请支持原创~~~
源码基于: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.java
private 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 步保存日志。