解讀Android LOG機制的實現:(5)擷取LOG的應用程式LogCat
田海立@CSDN
2011/07/27
Android提供的LOG機制的實現貫穿了Java,JNI,本地c/c++實現以及LINUX核心驅動等Android的各個層次,並且簡單明晰,是一個相當不錯的解讀案例。本系列文章針對LOG機制的內部實現機理進行解讀,本文是本系列的第五篇,解讀應用程式LogCat如何通過對裝置檔案的open()/select()/read()來擷取LOG資訊。
從前文知道,LOG被寫入到了驅動的節點,那如何擷取這些LOG資訊並呈現出來的呢?ANDROID裡是有個叫LogCat的應用程式被用來擷取LOG資訊。LogCat不僅從裝置節點處擷取LOG,並且還提供了很多選項供使用者來過濾、控制輸出格式等。本文只講解如何擷取LOG部分,相關的LogCat的使用方式,可參考Android的Logcat命令詳解。
LogCat是在檔案system/core/logcat/logcat.cpp中實現的。
從Logger裝置驅動的實現知道,Log的讀取是阻塞的操作,亦即,有資料可用,讀出資料;否則,讀操作會被BLOCK,相應的讀進程也會被掛起等待。下面看應用程式LogCat中如何?讀的,這可能需要不斷回頭與寫操作和驅動實現結合來看。
看具體實現之前,先看一個logcat中定義的重要的結構體log_device_t。其中的重要的成員在後面用到的時候再具體解釋。
一、開啟裝置節點
Android的Logcat命令詳解的命令參數-b <buffer>知道,logcat是可以通過參數來指定對哪個buffer(main/radio/event)進行操作的。Logcat的b參數解析的地方,是通過傳遞進來的參數(main/radio/event)來建立了一個上面的結構變數,而這些結構通過log_device_t.next連結起來。
if (devices) { dev = devices; while (dev->next) { dev = dev->next; } dev->next = new log_device_t(buf, binary, optarg[0]); } else { devices = new log_device_t(buf, binary, optarg[0]); }
而建立執行個體的時候的參數被保留了下來,用於後續操作。
<buf>是由LOG_FILE_DIR和optarg(-b參數)組合在一起的(為:“/dev/log/main”,“/dev/log/event”或“/dev/log/radio”),保留在device: char*;
<binary>保留在binary: bool;
<optarg[0]>是-b參數的第一個字元,儲存在label: char中。
好了,下面就有了開啟裝置節點時的參數:
dev->fd = open(dev->device, mode);
dev->device根據-b的參數可能為“/dev/log/main”,“/dev/log/event”或“/dev/log/radio”;
mode預設時為O_RDONLY,讀取。只要在運行logcat時,用了-c參數清除log時才以O_WRONLY開啟。
而開啟檔案的檔案操作符儲存在log_device_t的fd域中,用於後續的操作。
擷取Log的操作都是在readLogLines(log_device_t* devices)中實現的。
因為logcat可能會同時操作多個Buffer,而read()會阻塞讀取進程,對其他Buffer的讀取就不能進行,所以這裡用select()來判斷可讀取的Buffer。
二、select選取可讀取的Buffer
Logcat把log_device_t中的所有的buffer的檔案操作符dev->fd,都放在readset中[line#7],做為select()的裡的<readfds: fd_set*>讀參數,來擷取可讀取的Buffer。這樣當任何一個Buffer上有LOG資料時,select()都會返回。當然等待過程中也忽略掉其他signal的影響。相應的代碼如下:
fd_set readset; do { timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR. FD_ZERO(&readset); for (dev=devices; dev; dev = dev->next) { FD_SET(dev->fd, &readset); } result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout); } while (result == -1 && errno == EINTR);
三、讀LOG操作
select()返回之後,通過迴圈判定dev->fd是否在readset裡被設定(FD_ISSET)[line#3],知道哪個log buffer裡已經有資料了。
if (result >= 0) { for (dev=devices; dev; dev = dev->next) { if (FD_ISSET(dev->fd, &readset)) { queued_entry_t* entry = new queued_entry_t(); /* NOTE: driver guarantees we read exactly one full entry */ ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN); //…
通過read()讀取[line#6]已經有資料的LOG Buffer的檔案操作符dev->fd就可得到新到來的log了。
應用程式logcat中已經擷取了LOG資訊,接下來對資料的處理就都可以在這裡進行了,可以過濾,寫檔案,格式化輸入等操作。詳細的logcat的命令參數可參見Android的Logcat命令詳解。
【本系列文章】
解讀Android LOG機制的實現:(1)LOG的實現架構
解讀LOG機制的實現架構。
解讀Android LOG機制的實現:(2)Java域輸出LOG
解讀Android的Java程式中如何輸出LOG資訊到LOG的體系中。
解讀Android LOG機制的實現:(3)JNI及NATIVE域寫裝置檔案
解讀android.util.Log的JNI實現,以及在c/c++的本地實現中如何操作裝置檔案寫Log資訊。
解讀Android LOG機制的實現:(4)LOG裝置驅動logger
解讀LINUX核心中的裝置驅動Logger中實現。Logger是Android為Linux寫的一個MISC類型驅動,用迴圈隊列實現了讀者/寫者。Logger是整個LOG機制實現的核心。
解讀Android LOG機制的實現:(5)擷取LOG程式LogCat
解讀應用程式LogCat如何通過對裝置檔案的open()/select()/read()來擷取LOG資訊。
解讀Android LOG機制的實現:(6)c/c++域使用LOG
解讀Android的c/c++程式中如何使用LOG機制記錄LOG資訊。