logcat 備忘

Google 在其Android 源碼 system/core/logcat/logcat.cpp 中實現logcat的。但哥們沒有實力解釋其實現過程,所以本文不討論這個cpp。這裏只是簡單介紹下logcat在系統中的生養問題。

logcat 是google提供給android 開發人員的一款工具。具體的介紹請參考Google Api。附上Google 給我們的解釋:


The Android logging system provides a mechanism for collecting and viewing system debug output. Logs from various applications and portions of the system are collected in a series of circular buffers, which then can be viewed and filtered by the logcat command. You can use logcat from an ADB shell to view the log messages.

This document is a reference to the available command line options. For more information on logcat , see Reading and Writing Logs . For more information on accessing logcat from DDMS, instead of the command line, see the documentation for the Dalvik Debug Monitor Server .

 

可是除了開發人員自己使用adb logcat 外,某些程序也會養貓。這個可能會造成嚴重的問題。

通過搜索源碼,哥們大概找出只有有限的幾種情況系統會自己生養一隻log貓,而這些貓均是由一些watchdog放養出來的。衆所周知,watchdog的作用是在防止系統死鎖,以及在緊急時期reboot系統。而Android 在framework層實現的軟看門狗,正是Google 爲Android守護其系統幾個重量級的service特意製造的(watchdog的實現在frameworks\base\services\java\com\android\server\WatchDog.java)。

其中ActivityManagerService,PowerManagerService,WindowManagerService,直接實現Watchdog.Monitor。也就是說當這3個服務啓動時,Watchdog會隨之啓動。
SystemServer是android啓動至java層時把Watchdog和其他重要服務一同啓動。
MailService 在判斷checkAccountId大於0時,也就是有其他賬戶輸入時,啓動Watchdog。
NFCService 在boolean _disable()方法中實現Watchdog。其中調用_disable方法的有 onTerminate 。
WifiWatchdogService 是爲WifiService 服務。當收到MESSAGE_ENABLE_WIFI消息後,實例化WifiWatchdogService。從而啓動Watchdog。

當系統出現crash, ANR (Application Not Responding) ,WTF (What a Terrible Failure)時,看門狗會調用在ActivityManagerService中定義的addErrorToDropBox方法,放出logcat。在android2.3中,共有7個service會有watchdog。分別爲:ActivityManagerService,MailService,NfcService,PowerManagerService,SystemServer,

WifiWatchdogStateMachine,WindowManagerService。在4.0中彷彿又增加了:InputManager,AttachmentDownloadService,MountService,NativeDaemonConnector,

NetworkManagementService。

但這些都有一個問題,就是放狗後,如若沒有出現crash等問題還好,如果出現crash等問題,由狗衍生的貓,在正常情況下會有一隻永生不死,除非系統重啓。否則,整個系統就是一個貓圈,到處充斥着永生不死的logcat。而android的GC無法回收之,原因是logcat一直對於系統來說是活蹦亂跳的,系統沒有權利回收。

根據源碼分析可知,google是在一個線程裏啓動了logcat這個進程。但是哥們在addError這個方法裏沒有發現殺死logcat的實現。

public void addErrorToDropBox(String eventType,
            ProcessRecord process, ActivityRecord activity, ActivityRecord parent, String subject,
            final String report, final File logFile,
            final ApplicationErrorReport.CrashInfo crashInfo) {
       ..............//哥們刪了點

Thread worker = new Thread("Error dump: " + dropboxTag) {
            @Override
            public void run() {
                if (report != null) {
                    sb.append(report);
                }
                if (logFile != null) {
                    try {
                        sb.append(FileUtils.readTextFile(logFile, 128 * 1024, "\n\n[[TRUNCATED]]"));
                    } catch (IOException e) {
                        Slog.e(TAG, "Error reading " + logFile, e);
                    }
                }
                if (crashInfo != null && crashInfo.stackTrace != null) {
                    sb.append(crashInfo.stackTrace);
                }

                String setting = Settings.Secure.ERROR_LOGCAT_PREFIX + dropboxTag;
                int lines = Settings.Secure.getInt(mContext.getContentResolver(), setting, 0);
                if (lines > 0) {
                    sb.append("\n");
                    InputStreamReader input = null;
                    try {
                        java.lang.Process logcat = new ProcessBuilder("/system/bin/logcat",
                                "-v", "time", "-b", "events", "-b", "system", "-b", "main",
                                "-t", String.valueOf(lines)).redirectErrorStream(true).start(); //放貓了

                        try { logcat.getOutputStream().close(); } catch (IOException e) {}
                        try { logcat.getErrorStream().close(); } catch (IOException e) {}
                        input = new InputStreamReader(logcat.getInputStream());

                        int num;
                        char[] buf = new char[8192];
                        while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);
                    } catch (IOException e) {
                        Slog.e(TAG, "Error running logcat", e);
                    } finally {
                        if (input != null) try { input.close(); } catch (IOException e) {}
                    }
                }

                dbox.addText(dropboxTag, sb.toString());
            }
        };

        if (process == null || process.pid == MY_PID) {
            worker.run();  // We may be about to die -- need to run this synchronously
        } else {
            worker.start();
        }
    }

所以,大家在代碼裏欲養貓一定要注意啊啊啊,小心繫統成貓圈。

有人會說那麼正常的adb logcat 爲什麼會關閉呢?
通過adb命令啓動的logcat,由代碼可知(代碼出自sdk/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java)stopLogCat方法裏會結束掉logcat也就是說正常連接的adb logcat 當釋放窗口後,logcat會自動退出。

while (!shell.isDisposed()) { // shell沒有釋放
if (!mDisplay.readAndDispatch())
mDisplay.sleep();
}
mLogPanel.stopLogCat(true); //logcat關閉

有些service需要再仔細看看,哥們因爲時間問題大概記錄下,留着以後學習。備忘一下。哇嘎嘎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章