WatchDog工做原理

Android系統中,有硬件WatchDog用於定時檢測關鍵硬件是否正常工做,相似地,在framework層有一個軟件WatchDog用於按期檢測關鍵系統服務是否發生死鎖事件。android

watchdog的源碼很簡單,主要有兩個功能oop

1監控system_server中幾個關鍵的鎖,原理就是在android_fg線程中嘗試加鎖post

2監控幾個經常使用線程的執行時間,原理就是在這幾個線程中執行任務ui

WatchDog初始化

在SystemServer.startOtherServices()方法進行初始化this

system_server進程啓動的過程當中初始化WatchDog,主要進行三個動做:lua

  • 建立watchdog對象,該對象自己繼承於Thread;
  • 註冊reboot廣播;
  • 調用start()開始工做。

1.建立Watchdog對象spa

public class Watchdog extends Thread {
    //全部的HandlerChecker對象組成的列表,HandlerChecker對象類型
    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));
        addMonitor(new BinderThreadMonitor());
    }

}

Watchdog繼承於Thread,建立的線程名爲」watchdog」。mHandlerCheckers隊列包括、 主線程,fg, ui, io, display線程的HandlerChecker對象。線程

2.HandlerChecker對象debug

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 boolean mCompleted; //開始檢查時先設置成false
    private Monitor mCurrentMonitor;
    private long mStartTime; //開始準備檢查的時間點

    HandlerChecker(Handler handler, String name, long waitMaxMillis) {
        mHandler = handler;
        mName = name;
        mWaitMax = waitMaxMillis;
        mCompleted = true;
    }
}

HandlerChecker繼承自Runnable對象,裏面維護着監控服務Monitor對象列表,使用Watchdog.addMonitor方法將服務監聽者加入列表調試

   addMonitor(new BinderThreadMonitor());

這裏監控Binder線程, 將monitor添加到HandlerChecker的成員變量mMonitors列表中。將BinderThreadMonitor對象加入該線程。

private static final class BinderThreadMonitor implements Watchdog.Monitor {
    public void monitor() {
        Binder.blockUntilThreadAvailable();
    }
}

blockUntilThreadAvailable最終調用的是IPCThreadState,監控是否有可用的binder進程。

3.Watchdog start

當調用Watchdog.getInstance().start()時,則進入線程「watchdog」的run()方法, 該方法分紅兩部分:

  • 用於監測是否觸發超時;
  • 當觸發超時則輸出各類信息。

4.Watchdog run

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[見小節3.2]
                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狀態【見小節3.3】
            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【見小節4.2】
                    ActivityManagerService.dumpStackTraces(true, pids, null, null,
                            NATIVE_STACKS_OF_INTEREST);
                    waitedHalf = true;
                }
                continue;
            }
            ... //進入這裏,意味着Watchdog已超時【見小節4.1】
        }
        ...
    }
}

public static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
    "/system/bin/mediaserver",
    "/system/bin/sdcard",
    "/system/bin/surfaceflinger"
};

該方法主要功能:

  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, 則輸出更多信息.

由此,可見當觸發一次Watchdog, 則必然會調用兩次AMS.dumpStackTraces, 也就是說system_server和3個Native進程的traces 的traces信息會輸出兩遍,且時間間隔超過30s.

5.scheduleCheckLocked

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()方法。

可能的問題,若是有其餘消息不斷地調用postAtFrontOfQueue()也可能致使watchdog沒有機會執行;或者是每一個monitor消耗一些時間,累加起來超過1分鐘形成的watchdog. 這些都是很是規的Watchdog.

Watchdog處理流程

public void run() {
    while (true) {
        synchronized (this) {
            ...
            //獲取被阻塞的checkers 【見小節4.1.1】
            blockedCheckers = getBlockedCheckersLocked();
            // 獲取描述信息 【見小節4.1.2】
            subject = describeCheckersLocked(blockedCheckers);
            allowRestart = mAllowRestart;
        }

        EventLog.writeEvent(EventLogTags.WATCHDOG, subject);

        ArrayList<Integer> pids = new ArrayList<Integer>();
        pids.add(Process.myPid());
        if (mPhonePid > 0) pids.add(mPhonePid);
        //第二次以追加的方式,輸出system_server和3個native進程的棧信息【見小節4.2】
        final File stack = ActivityManagerService.dumpStackTraces(
                !waitedHalf, pids, null, null, NATIVE_STACKS_OF_INTEREST);

        //系統已被阻塞1分鐘,也不在意多等待2s,來確保stack trace信息輸出
        SystemClock.sleep(2000);

        if (RECORD_KERNEL_THREADS) {
            //輸出kernel棧信息【見小節4.3】
            dumpKernelStackTraces();
        }

        //觸發kernel來dump全部阻塞線程【見小節4.4】
        doSysRq('l');

        //輸出dropbox信息【見小節4.5】
        Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
            public void run() {
                mActivity.addErrorToDropBox(
                        "watchdog", null, "system_server", null, null,
                        subject, null, stack, null);
            }
        };
        dropboxThread.start();

        try {
            dropboxThread.join(2000); //等待dropbox線程工做2s
        } catch (InterruptedException ignored) {
        }

        IActivityController controller;
        synchronized (this) {
            controller = mController;
        }
        if (controller != null) {
            //將阻塞狀態報告給activity controller,
            try {
                Binder.setDumpDisabled("Service dumps disabled due to hung system process.");
                //返回值爲1表示繼續等待,-1表示殺死系統
                int res = controller.systemNotResponding(subject);
                if (res >= 0) {
                    waitedHalf = false;
                    continue; //設置ActivityController的某些狀況下,可讓發生Watchdog時繼續等待
                }
            } catch (RemoteException e) {
            }
        }

        //當debugger沒有attach時,才殺死進程
        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);
            //遍歷輸出阻塞線程的棧信息
            for (int i=0; i<blockedCheckers.size(); i++) {
                Slog.w(TAG, blockedCheckers.get(i).getName() + " stack trace:");
                StackTraceElement[] stackTrace
                        = blockedCheckers.get(i).getThread().getStackTrace();
                for (StackTraceElement element: stackTrace) {
                    Slog.w(TAG, " at " + element);
                }
            }
            Slog.w(TAG, "*** GOODBYE!");
            //殺死進程system_server【見小節4.6】
            Process.killProcess(Process.myPid());
            System.exit(10);
        }
        waitedHalf = false;
    }
}

Watchdog檢測到異常的信息收集工做:

  • AMS.dumpStackTraces:輸出Java和Native進程的棧信息;
  • WD.dumpKernelStackTraces:輸出Kernel棧信息;
  • doSysRq
  • dropBox

收集完信息後便會殺死system_server進程。此處allowRestart默認值爲true, 當執行am hang操做則設置不容許重啓(allowRestart =false), 則不會殺死system_server進程.

將全部執行時間超過1分鐘的handler線程或者monitor都記錄下來.

  • 當輸出的信息是Blocked in handler,意味着相應的線程處理當前消息時間超過1分鐘;
  • 當輸出的信息是Blocked in monitor,意味着相應的線程處理當前消息時間超過1分鐘,或者monitor遲遲拿不到鎖;

對於觸發watchdog時,生成的dropbox文件的tag是system_server_watchdog,內容是traces以及相應的blocked信息。

當殺死system_server進程,從而致使zygote進程自殺,進而觸發init執行重啓Zygote進程,這便出現了手機framework重啓的現象。

 

總結

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的狀況, 不重啓;
  • 對於Looper Checker而言,會判斷線程的消息隊列是否處於空閒狀態。 若是被監測的消息隊列一直閒不下來,則說明可能已經阻塞等待了很長時間

  • 對於Monitor Checker而言,會調用實現類的monitor方法,譬如上文中提到的AMS.monitor()方法, 方法實現通常很簡單,就是獲取當前類的對象鎖,

  • 若是當前對象鎖已經被持有,則monitor()會一直處於wait狀態,直到超時,這種狀況下,極可能是線程發生了死鎖

監控Handler線程(Looper Checker)

Watchdog監控的線程有:

(默認地DEFAULT_TIMEOUT=60s,調試時才爲10s方便找出潛在的ANR問題)

目前watchdog會監控system_server進程中的以上8個線程:

  • 前7個線程的Looper消息處理時間不得超過1分鐘;
  • PackageManager線程的處理時間不得超過10分鐘;

監控同步鎖(Monitor Checker)

可以被Watchdog監控的系統服務都實現了Watchdog.Monitor接口,並實現其中的monitor()方法。運行在android.fg線程, 系統中實現該接口類主要有:

  • ActivityManagerService
  • WindowManagerService
  • InputManagerService
  • PowerManagerService
  • NetworkManagementService
  • MountService
  • NativeDaemonConnector
  • BinderThreadMonitor
  • MediaProjectionManagerService
  • MediaRouterService
  • MediaSessionService
  • BinderThreadMonitor

輸出信息

watchdog在check過程當中出現阻塞1分鐘的狀況,則會輸出:

  1. AMS.dumpStackTraces:輸出system_server和3個native進程的traces
    • 該方法會輸出兩次,第一次在超時30s的地方;第二次在超時1min;
  2. WD.dumpKernelStackTraces,輸出system_server進程中全部線程的kernel stack;
    • 節點/proc/%d/task獲取進程內全部的線程列表
    • 節點/proc/%d/stack獲取kernel的棧
  3. doSysRq, 觸發kernel來dump全部阻塞線程,輸出全部CPU的backtrace到kernel log;
    • 節點/proc/sysrq-trigger
  4. dropBox,輸出文件到/data/system/dropbox,內容是trace + blocked信息
  5. 殺掉system_server,進而觸發zygote進程自殺,從而重啓上層framewor
相關文章
相關標籤/搜索