Android中的軟體Watchdog

來源:互聯網
上載者:User

標籤:

由於Android的SystemServer內有一票重要Service,所以在進程內有一個軟體實現的Watchdog機制,用於監視SystemServer中各Service是否正常工作。如果超過一定時間(預設30秒),就dump現場便於分析,再逾時(預設60秒)就重啟SystemServer保證系統可用性。同時logcat中會列印類似下面資訊:

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), Blocked in handler on WindowManager thread (WindowManager)


主要實現代碼位於/frameworks/base/services/core/java/com/android/server/Watchdog.java和/frameworks/base/core/jni/android_server_Watchdog.cpp。大體的架構很簡單。Watchdog是SystemServer中的獨立線程,它隔一定時間間隔會向各監視線程調度一次檢查操作。這個檢查操作當中會調用登入的Monitor對象。如果Monitor對象上產生死結,或是關鍵線程hang住,那麼該檢查必定不能按時結束,這樣就被Watchdog檢查到。


先來看看總體類圖。因為是唯一的,Watchdog實現為Singleton。其中維護HandlerChecker數組,對應要檢查的線程。HandlerChecker數組中有Monitor數組,對應要檢查的Monitor對象。要接受檢查的對象需要實現Monitor介面。



初始化是從SystemServer的startOtherServices()中開始的,其大體流程如下:


首先,在SystemServer.java中,會建立Watchdog並啟動。

472            Slog.i(TAG, "Init Watchdog");473            final Watchdog watchdog = Watchdog.getInstance();474            watchdog.init(context, mActivityManagerService);…1120                Watchdog.getInstance().start();
在Watchdog的建構函式中,會為每個要檢查的線程建立HandlerChecker,並加到mHandlerCheckers這個隊列中。首先是FgThread。它繼承自ServiceThread,是一個單例,負責那些常規的前台操作,它不應該被背景操作所阻塞。在Watchdog.java中:
215        mMonitorChecker = new HandlerChecker(FgThread.getHandler(),216                "foreground thread", DEFAULT_TIMEOUT);217        mHandlerCheckers.add(mMonitorChecker);

接下來,對於System Server的主線程,UI線程,IO線程和Display線程,分別做相同操作,這坨線程和FgThread一樣都繼承自ServiceThread。在init()函數中,接下來會調用registerReceiver()來註冊系統重啟的BroadcastReceiver。在收到系統重啟廣播時會執行RebootRequestReceiver的onReceive()函數,繼而調用rebootSystem()重啟系統。它允許其它模組(如CTS)通過發廣播來讓系統重啟。


然後,各個需要被Watchdog監視的Service需要將自己進行註冊。它們都實現了Watchdog.Monitor介面,其中主要是monitor()函數。例如ActivityManagerService:

2150        Watchdog.getInstance().addMonitor(this);2151        Watchdog.getInstance().addThread(mHandler);
其中addMonitor()將自身放到foreground thread的HandlerChecker的monitor隊列中,addThread()根據當前線程的Handler建立HandlerChecker並放到mHandlerCheckers隊列中。monitor()的實現一般很簡單,只是嘗試去獲得鎖再釋放鎖。如果有deadlock,就會卡住無法返回。
18767    public void monitor() {18768        synchronized (this) { }18769    }

回到SystemServer中,通過Watchdog的start()方法啟動Watchdog線程,Watchdog.run()被執行。


Watchdog的主體是一個迴圈。在每一次迴圈中,所有HandlerChecker的scheduleCheckLocked()函數被調用。其中主要是往被監視線程的Looper中放入HandlerChecker對象,HandlerChecker本身作為Runnable,是線程的可執行體。因此當被監視線程把它從Looper中拿出來後,它的run()函數被調用。然後,Watchdog.run()等待最長30秒後,調用evaluateCheckerCompletionLocked()檢查各HandlerChecker結果狀態。一個HandlerChecker結果狀態有四種,COMPLETED(0),WAITING(1), WAITED_HALF(2)和OVERDUE(3)。分別代表目標Looper已處理monitor,延時小於30秒,延時大於30秒小於60秒,延時大於60秒。最終的總狀態是它們的最大值(也就是意味著最壞情況的那種情況)。如果總狀態是COMPLETED,WAITING或WAITED_HALF,則進入迴圈下一輪。注意如果是WAITED_HALF,也就是說等了大於30秒,需要調用AMS.dumpStackTraces()來dump棧。如果狀態為WAITED_HALF,進入下一輪迴圈後,又會等待最長30秒。

假設有線程阻塞,對於阻塞線程的HandlerChecker,它的延遲超過60秒,導致總狀態為OVERDUE。這裡會調用getBlockedCheckersLocked()和describeCheckersLocked()列印出是哪個Handler阻塞了。在Eventlog中打出資訊後,把當前pid和相關進程pid放入待殺列表。然後和上面一樣調用AMS.dumpStackTraces()列印棧。之後等待2秒等stacktrace寫完。如有需要還會調用dumpKernelStackTraces()將kernel部分的stacktrace打出來。本質上是讀取/proc/[pid]/task下的線程的和相應的/proc/[tid]/stack檔案。然後調用doSyncRq()通知kernel把阻塞線程資訊和backtrace列印出來(通過寫/proc/sysrq-trigger)。之後會建立專門的線程來將資訊寫入到Dropbox,該線程會執行AMS.addErrorToDropBox()。Dropbox是Android中的一套日誌記錄系統,作用是將系統出錯資訊記錄下來並且保留一段時間,避免被覆蓋。當出現crash, wtf, lowmem或者Watchdog被觸發都會通過Dropbox來記錄。

425            Thread dropboxThread = new Thread("watchdogWriteToDropbox") {426                    public void run() {427                        mActivity.addErrorToDropBox(428                                "watchdog", null, "system_server", null, null,429                                subject, null, stack, null);430                    }431                };432            dropboxThread.start();433            try {434                dropboxThread.join(2000);  // wait up to 2 seconds for it to return.435            } catch (InterruptedException ignored) {}
DropBoxManagerService在SystemServer的startOtherServices()中添加,資訊預設存放路徑為/data/system/dropbox。DropBoxManagerService實現了IDropBoxManagerService服務介面,Client通過DropBoxManager來訪問服務。在Watchdog中調用AMS.addErrorToDropBox()後,該函數會起背景工作執行緒(因為涉及I/O),將前面得到的stack資訊dump到Dropbox,並且擷取最近的logcat,最後通過DBMS的addText()介面進行儲存。 

接下來,如果設定了ActivityController就調用其systemNotResponding()介面(IActivityController是給測試開發時用的介面,用於監視AMS裡的行為)。然後判斷Debugger是否連著和是否允許restart。如果沒有連著debugger且允許restart,就開始大開殺戒了。
467                Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);...476                Slog.w(TAG, "*** GOODBYE!");477                Process.killProcess(Process.myPid());478                System.exit(10);
因為Watchdog和SystemServer是同一進程,這裡Watchdog kill掉了自己,也就是kill了SystemServer。因它是主要進程,殺掉後會被init重啟。

這就是Watchdog的大體流程,回過頭看下AMS中dumpStackTraces()的一些細節。參數中的pids包含了本進程,阻塞線程以及phone進程等。NATIVE_STACKS_OF_INTEREST包含了下面三個關鍵進程。

67    public static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {68        "/system/bin/mediaserver",69        "/system/bin/sdcard",70        "/system/bin/surfaceflinger"71    };
注意雖然它們不在SystemServer中,但因為SystemServer中的Service會用Binder同步調用它們的方法。如果這些進程中阻塞,也可能導致SystemServer中發生阻塞。

dumpStackTraces()實現中先從dalvik.vm.stack-trace-file這個system property中取出trace路徑,預設為/data/anr/traces.txt。接著它建立該檔案(需要的話),設定屬性,最後調用同名函數dumpStackTraces()完成真正的dump工作。dump工作首先會用FileObserver(利用inotify機制)監視trace檔案什麼時候寫完。它會建立一個獨立的線程ObserverThread並運行。然後對於前面加入到要dump線程列表中的進程,發送SIGQUIT訊號。如果是虛擬機器進程,ART中的SignalCatcher::HandleSigQuit()(在/art/runtime/signal_catcher.cc)會被調用來dump資訊(DVM也是類似的)。對於前面的核心Service,調用Debug.dumpNativeBacktraceToFile()來輸出它們的backtrace。

總結下來,dumpStackTraces()大體流程如下:

可以看到,其中主要收集三類資訊:一是關鍵進程(也就是上面收集的pid)的stacktrace;二是幾個關鍵native服務的stacktrace;三是CPU的使用率。其中一是通過往目標進程發SIGQUIT來擷取,因為Java虛擬機器的Runtime會捕獲SIGQUIT訊號列印棧資訊。二的原理是向後台debuggerd這個daemon發起申請,讓其用ptrace列印目標進程的stacktrace然後用本地socket傳回來。部分實現位於android_os_Debug.cpp和/system/core/libcutils/debugger.c。發起申請和接收資料的代碼在以下函數:

131int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {132  int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, timeout_secs);...137  /* Write the data read from the socket to the fd. */...141  while ((n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {142    if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) {143      result = -1;144      break;145    }146  }...
最後使用ProcessCpuTracker類測量CPU使用率。它主要是通過讀系統的/proc/[pid]/stat檔案。裡邊可以讀到進程所佔用的時間(user mode和kernel mode)。統計半秒後,排序後輸出最占CPU的前幾名的stacktrace以便分析誰可能是罪魁禍首。

總得來說,Watchdog是一個軟體實現的檢測SystemServer進程內死結或掛起問題,並能夠從中恢複的機制。除了Watchdog外,Android中還有一些自檢容錯及出錯資訊收集機制,前者有ANR,OOM Killer,init中的重啟機制等,後者有Dropbox,Debuggerd,Eventlog,Bugreport等。除此之外,其它的資訊查看和調試命令就不計其數了,如dumpsys, dumpstate, showslab, procrank, procmem, latencytop, librank, schedtop, svc, am ,wm, atrace, proc, pm, service, getprop/setprop, logwrapper, input, getevent/sendevent等。充分利用這些工具能夠有效提高分析問題的效率。

Android中的軟體Watchdog

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.