『Android Q 源碼分析』- Android 10.0 WatchDog源碼解析

概覽

(本文系統源碼基於Andoroid 10.0.0-r16)
Watchdog的中文叫「看門狗」,最先引入Watchdog是在單片機系統中,因爲單片機的工做環境容易受到外界磁場的干擾,致使程序「跑飛」,形成整個系統沒法正常工做,所以,引入了一個「看門狗」,對單片機的運行狀態進行實時監測,針對運行故障作一些保護處理,譬如讓系統重啓。這種Watchdog屬於硬件層面,必須有硬件電路的支持。java

Linux也引入了Watchdog,在Linux內核下,當Watchdog啓動後,便設定了一個定時器,若是在超時時間內沒有對/dev/Watchdog進行寫操做,則會致使系統重啓,經過定時器實現的Watchdog屬於軟件層面android

Android設計了一個軟件層面Watchdog,用於保護一些重要的系統服務,當出現故障時,一般會讓Android系統重啓。因爲這種機制的存在,就常常會出現一些system_server進程被Watchdog殺掉而發生系統重啓的問題。 原理就是每到達必定的時間,獲取正在監控的鎖(Monitor.monitor()),可否得到鎖從而判斷是否產生了死鎖。git

Watchdog流程

Watchdog流程

Watchdog初始化

首先咱們能夠看到WatchDog是繼承於Thread類,而實際上WatchDog也是單線程運行的。github

public class Watchdog extends Thread {}
複製代碼

接下來咱們能夠看下Watchdog的構造函數: frameworks/base/services/core/java/com/android/server/Watchdog.java數組

private Watchdog() {
    // 設置線程名字爲watchdog
    super("watchdog");
    // Initialize handler checkers for each common thread we want to check.
    // 請注意,咱們目前沒有檢查後臺線程,由於它可能會持有較長時間運行的操做,
    // 而不能保證那裏的操做的及時性。

    // FgThread monitor
    mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
            "foreground thread", DEFAULT_TIMEOUT);
    mHandlerCheckers.add(mMonitorChecker);
    
    // main thread monitor
    mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
            "main thread", DEFAULT_TIMEOUT));
            
    // UiThread monitor
    mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
            "ui thread", DEFAULT_TIMEOUT));
            
    // IoThread monitor
    mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
            "i/o thread", DEFAULT_TIMEOUT));
            
    // DisplayThread monitor
    mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
            "display thread", DEFAULT_TIMEOUT));
    
    // AnimationThread monitor
    mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(),
            "animation thread", DEFAULT_TIMEOUT));
    
    // SurfaceAnimationThread monitor
    mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(),
            "surface animation thread", DEFAULT_TIMEOUT));

    // BinderThreadMonitor 主要用於檢測binder線程是否達到鏈接上限16個
    // (正常的是16,這裏是SystemServer的進程,因此是31) >= 31個則阻塞線程等待mThreadCountDecrement喚醒
    addMonitor(new BinderThreadMonitor());
    
    // 用於對fd數量進行檢測 只會在userdebug或者eng版本開啓
    // 值是廠商設置的 達到這個值 - 12 就會在/data/anr/ dump fd的信息
    mOpenFdMonitor = OpenFdMonitor.create();

    assert DB ||
            DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
}
複製代碼

能夠看出咱們在建立出Watchdog的時候先把一些重要的線程加到Watchdog的mHandlerCheckers裏面,當Watchdog執行run()方法的時候就會依次對mHandlerCheckers裏面的HandlerChecker對象進行輪詢,查看是否有超時的線程。有的話通常都是重啓來解決。markdown

那咱們在哪裏調用這個設置爲private的構造函數呢?就是在本類的getInstance()多線程

public static Watchdog getInstance() {
    if (sWatchdog == null) {
        sWatchdog = new Watchdog();
    }

    return sWatchdog;
}
複製代碼

咱們如今再看深一層,那究竟誰調用了getInstance()方法呢?咱們動用ctrl + 鼠標點一下就彈出一系列的調用者。查看一番以後,能夠觀察到是SystemServer調用了咱們Watchdog的getInstance()方法進行初始化。 frameworks/base/services/java/com/android/server/SystemServer.java併發

private void startBootstrapServices() {
    final Watchdog watchdog = Watchdog.getInstance();
    watchdog.start();
    // ...
}
複製代碼

在調用了初始化的getInstance()以後就立刻調用了watchdog的start()方法,其實也就是父類Thread的start()方法,這時候咱們的「看門狗」就跑起來了(執行了run()方法),並且能夠看到watchdog是在startBootstrapServices()中的第一句就進行初始化了,SystemServer中初始化服務的順序是這樣的:app

  1. startBootstrapServices();
  2. startCoreServices();
  3. startOtherServices();

這說明了watchdog在衆多的服務中,優先級仍是很高的,那有沒有同窗會想到是爲何呢?答案就是不少服務的超時結束機制都是依賴watchdog提供的,因此watchdog必需要比其餘服務早初始化。ide

咱們再來看一下WatchDog的init()方法,由於依賴於ActivityManagerService,因此咱們將在ActivityManagerService初始化以後再執行這個方法。

public void init(Context context, ActivityManagerService activity) {
    mActivity = activity;
    context.registerReceiver(new RebootRequestReceiver(),
            new IntentFilter(Intent.ACTION_REBOOT),
            android.Manifest.permission.REBOOT, null);
}
複製代碼

能夠看到這裏經過context註冊了一個廣播接收器,而這個RebootRequestReceiver用來接收重啓的廣播來進行手機重啓的,是Watchdog中定義的一個內部類,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);
    }
}

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) {
    }
}
複製代碼

HandlerChecker

咱們再來看看在Watchdog中相對比較重要的HandlerChecker內部類是長什麼樣子的。

/** * Used for checking status of handle threads and scheduling monitor callbacks. */
public final class HandlerChecker implements Runnable {
    // 須要檢測的線程對應的handler
    private final Handler mHandler;
    // 進行日誌打印的時候知道是哪一個handler出現問題
    private final String mName;
    // 超時時間
    private final long mWaitMax;
    // 檢測的Monitor數組
    private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
    // 還未加入mMonitors中的Monitor 
    // 等待mCompleted爲true的時候才一次性加入mMonitors中
    private final ArrayList<Monitor> mMonitorQueue = new ArrayList<Monitor>();
    // 是否檢查完成的標誌位 空閒的話默認爲true
    private boolean mCompleted;
    // 目前正在檢查的Monitor
    private Monitor mCurrentMonitor;
    // 檢查開始的時間
    private long mStartTime;
    // mPauseCount > 0則說明這個HandlerChecker處於暫停狀態
    private int mPauseCount;

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

    // 註釋1
    void addMonitorLocked(Monitor monitor) {
        // We don't want to update mMonitors when the Handler is in the middle of checking
        // all monitors. We will update mMonitors on the next schedule if it is safe
        mMonitorQueue.add(monitor);
    }

    // 註釋2
    public void scheduleCheckLocked() {
        if (mCompleted) {
            // Safe to update monitors in queue, Handler is not in the middle of work
            mMonitors.addAll(mMonitorQueue);
            mMonitorQueue.clear();
        }
        // 待檢測的mMonitors爲零且looper正在從隊列中輪詢任務 
        // 或者正在暫停的狀況下 不要進行一些數據的重置
        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();
        // 往handler前面插入任務(插隊)
        mHandler.postAtFrontOfQueue(this);
    }

    // 是否超時
    boolean isOverdueLocked() {
        return (!mCompleted) && (SystemClock.uptimeMillis() > mStartTime + mWaitMax);
    }
    
    // 經過mStartTime、mWaitMax和mCompleted 進行狀態的判斷
    // 前 mWaitMax/2 秒爲WAITING狀態,後mWaitMax/2爲WAITED_HALF狀態
    // 大於mWaitMax爲超時狀態也就是OVERDUE狀態
    public int getCompletionStateLocked() {
        if (mCompleted) {
            return COMPLETED;
        } else {
            long latency = SystemClock.uptimeMillis() - mStartTime;
            if (latency < mWaitMax/2) {
                return WAITING;
            } else if (latency < mWaitMax) {
                return WAITED_HALF;
            }
        }
        return OVERDUE;
    }

    public Thread getThread() {
        return mHandler.getLooper().getThread();
    }

    public String getName() {
        return mName;
    }

    // 獲取Blocked狀態的描述
    // 在哪一個名字的HandlerChecker中 或者 當前handler執行哪一個monitor 
    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() + ")";
        }
    }

    @Override
    public void run() {
        // 一旦咱們到達這裏,咱們確保即便咱們調用#addMonitorLocked也不會改變mMonitors,
        // 由於咱們首先將新的監視器添加到mMonitorQueue中,
        // 並在下一次mCompleted爲真時將它們移到mMonitors中,此時咱們已經完成了這個方法的執行。
        // 因此這裏用了兩個數組分別存儲Monitor
        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
    public void pauseLocked(String reason) {
        mPauseCount++;
        // Mark as completed, because there's a chance we called this after the watchog
        // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
        // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
        mCompleted = true;
        Slog.i(TAG, "Pausing HandlerChecker: " + mName + " for reason: "
                + reason + ". Pause count: " + mPauseCount);
    }

    // 繼續這個HandlerChecker
    public void resumeLocked(String reason) {
        if (mPauseCount > 0) {
            mPauseCount--;
            Slog.i(TAG, "Resuming HandlerChecker: " + mName + " for reason: "
                    + reason + ". Pause count: " + mPauseCount);
        } else {
            Slog.wtf(TAG, "Already resumed HandlerChecker: " + mName);
        }
    }
}
複製代碼

從註釋1開始,能夠看到大部分方法後綴都加上了Locked,這是由於在Watchdog調用的時候都會加上synchronize鎖,保證不會發生多線程併發致使的問題。
而註釋2的scheduleCheckLocked()方法在Watchdog中以間隔30s執行一次。

如下是實現了Monitor接口的類: 實現了Monitor的接口的類

實現了Monitor的接口的類

WatchDog中比較重要的幾個函數

// 設置activityController 用處在後面Watchdog的run()方法會介紹到
public void setActivityController(IActivityController controller) {
    synchronized (this) {
        mController = controller;
    }
}

// 設置watchdog超時後是否能重啓手機
public void setAllowRestart(boolean allowRestart) {
    synchronized (this) {
        mAllowRestart = allowRestart;
    }
}

// 在FgThread中添加對monitor的監控
public void addMonitor(Monitor monitor) {
    synchronized (this) {
        mMonitorChecker.addMonitorLocked(monitor);
    }
}

// 添加須要監控的handler 超時時間默認爲60s
public void addThread(Handler thread) {
    addThread(thread, DEFAULT_TIMEOUT);
}

// 添加須要監控的handler 能夠自定義超時時間
public void addThread(Handler thread, long timeoutMillis) {
    synchronized (this) {
        final String name = thread.getLooper().getThread().getName();
        mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
    }
}

// 暫停當前正在運行的線程的監視操做。在執行可能錯誤觸發watchdog的長時間運行的操做以前很是有用。
// 每一個調用都須要一個匹配的{@link #resumeWatchingCurrentThread}調用。
public void pauseWatchingCurrentThread(String reason) {
    synchronized (this) {
        for (HandlerChecker hc : mHandlerCheckers) {
            if (Thread.currentThread().equals(hc.getThread())) {
                hc.pauseLocked(reason);
            }
        }
    }
}

// 恢復watchdog的檢測狀態
public void resumeWatchingCurrentThread(String reason) {
    synchronized (this) {
        for (HandlerChecker hc : mHandlerCheckers) {
            if (Thread.currentThread().equals(hc.getThread())) {
                hc.resumeLocked(reason);
            }
        }
    }
}

// 計算HandlerChecker的狀態 只要一個handler中有其中一個HandlerChecker超時了 
// 那Watchdog就超時了
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;
}

// 獲取哪一個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;
}

// 獲取每一個超時HandlerChecker的超時緣由,並組成String返回
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();
}
複製代碼

WatchDog.run()

接下來咱們就看一下Watchdog的核心代碼,run()方法

@Override
public void run() {
    // 是否在等待的前半段時間 true則爲等待的後半段時間
    boolean waitedHalf = false;
    // 死循環
    while (true) {
        final List<HandlerChecker> blockedCheckers;
        // 超時緣由 用於日誌打印
        final String subject;
        // 是否容許system_server重啓 默認爲true
        // 能夠經過watchdog.setAllowRestart()從新設置值
        final boolean allowRestart;
        // 調試進程鏈接數 有的話會賦值爲2
        int debuggerWasConnected = 0;
        synchronized (this) {
            // CHECK_INTERVAL爲30s
            long timeout = CHECK_INTERVAL;
            // 逐一執行每一個HandlerChecker的scheduleCheckLocked()方法
            for (int i=0; i<mHandlerCheckers.size(); i++) {
                HandlerChecker hc = mHandlerCheckers.get(i);
                hc.scheduleCheckLocked();
            }
            
            if (debuggerWasConnected > 0) {
                debuggerWasConnected--;
            }

            
            // 這裏用uptimeMillis() 是由於只會在設備喚醒的時候計算超時,
            // 設備休眠計算時間會致使錯誤重啓
            long start = SystemClock.uptimeMillis();
            while (timeout > 0) {
                if (Debug.isDebuggerConnected()) {
                    debuggerWasConnected = 2;
                }
                try {
                    // 等待30s
                    wait(timeout);
                    // Note: mHandlerCheckers and mMonitorChecker may have changed after waiting
                } catch (InterruptedException e) {
                    Log.wtf(TAG, e);
                }
                if (Debug.isDebuggerConnected()) {
                    debuggerWasConnected = 2;
                }
                // 有可能wait到一半的時候發生了InterruptedException 致使時間沒有走完
                // 只要沒有消耗完timeout的值 就繼續等待
                timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
            }

            // fd是否達到了限制的數量
            boolean fdLimitTriggered = false;
            if (mOpenFdMonitor != null) {
                fdLimitTriggered = mOpenFdMonitor.monitor();
            }
            
            // fd數量限制沒有達到
            if (!fdLimitTriggered) {
                final int waitState = evaluateCheckerCompletionLocked();
                // 都完成了的話 繼續下個循環 本次循環結束
                if (waitState == COMPLETED) {
                    waitedHalf = false;
                    continue;
                } else if (waitState == WAITING) {
                    // 在等待的前半段時間 繼續下個循環 本次循環結束
                    continue;
                } else if (waitState == WAITED_HALF) {
                    if (!waitedHalf) {
                        Slog.i(TAG, "WAITED_HALF");
                        // 咱們等了一半的死鎖探測時間。
                        // 獲取堆棧跟蹤並等待另外一半。
                        ArrayList<Integer> pids = new ArrayList<Integer>();
                        pids.add(Process.myPid());
                        ActivityManagerService.dumpStackTraces(pids, null, null,
                            getInterestingNativePids());
                        waitedHalf = true;
                    }
                    continue;
                }

                // 到這裏的時候已經有handler超時了
                // 獲取超時的HandlerChecker
                blockedCheckers = getBlockedCheckersLocked();
                // 獲取超時的HandlerChecker的信息
                subject = describeCheckersLocked(blockedCheckers);
            } else {
                // fd數量限制達到了
                blockedCheckers = Collections.emptyList();
                subject = "Open FD high water mark reached";
            }
            allowRestart = mAllowRestart;
        }

        // 若是咱們到這裏,這意味着系統極可能掛起。
        // 首先從系統進程的全部線程收集堆棧跟蹤。而後殺死這個進程,
        // 這樣系統纔會從新啓動。
        EventLog.writeEvent(EventLogTags.WATCHDOG, subject);

        ArrayList<Integer> pids = new ArrayList<>();
        pids.add(Process.myPid());
        if (mPhonePid > 0) pids.add(mPhonePid);
        
        // 打印java線程和native線程堆棧
        final File stack = ActivityManagerService.dumpStackTraces(
                pids, null, null, getInterestingNativePids());

        // 掛起5s確保堆棧能寫入到文件中
        SystemClock.sleep(5000);

        // 讓kernel dump所有的blocked線程 和 cpu信息
        doSysRq('w');
        doSysRq('l');

        // 嘗試把error加到dropbox下,但假設ActivityManager本身會死鎖
        // 當這種狀況發生時, 致使如下語句死鎖,watchdog做爲一個總體將會失效
        Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
                public void run() {
                    // 若是其中一條被觀察的線程在Watchdog init()方法執行前被掛起
                    // 咱們則沒有一個有效的AMS,因此不能把log打印存儲到dropbox路徑下
                    if (mActivity != null) {
                        mActivity.addErrorToDropBox(
                                "watchdog", null, "system_server", null, null, null,
                                subject, null, stack, null);
                    }
                    StatsLog.write(StatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);
                }
            };
        dropboxThread.start();
        try {
            // 等待2s 讓dropboxThread返回
            dropboxThread.join(2000);  
        } catch (InterruptedException ignored) {}

        IActivityController controller;
        synchronized (this) {
            controller = mController;
        }
        // 若是ActivityController不爲null 
        if (controller != null) {
            Slog.i(TAG, "Reporting stuck state to activity controller");
            try {
                // 因爲掛起system process而禁用dump 防止controller在報告錯誤的時候被掛起
                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) {
            }
        }

        // 只有在沒有debugger鏈接的狀況下才會殺死進程。
        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!");
            // 結束進程 watchdog存在於system_server進程之下 
            // 由於watchdog就是在system_server初始化的
            Process.killProcess(Process.myPid());
            System.exit(10);
        }

        waitedHalf = false;
    }
}
複製代碼

文章到這裏就完了,基本上只是對參考文章進行代碼的上更新(Android Q),結構大體同樣。僅做爲學習記錄用途。

參考文章

Android 系統中的 WatchDog 詳解
Watchdog機制以及問題分析

相關文章
相關標籤/搜索