一、logd介紹
logd 是Android L版本提出來的概念,其作用是保存Android運行期間的log(日志)。在Android L之前,log由kernel的ring buffer 保存,在Android L之后,log保存在用戶空間。
1) logd進程啟動
系統啟動到init函數時會解析init.rc文件,啟動logd進程和logd-reinit(重新初始化logd) 進程,init.rc文件中的相關內容如下:
onload_persist_props_action
load_persist_props
start logd
start logd-reinit
在system/core/logd中的logd.rc文件中,啟動logdservice和logd-reinit service,具體內容如下:
service logd /system/bin/logd
socket logd stream 0666logd logd
socket logdr seqpacket0666 logd logd
socket logdw dgram 0222logd logd
group root system readproc
writepid/dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
oneshot
disabled
writepid/dev/cpuset/system-background/tasks
注:logd-reinit只會執行一次,logd和logd-reinit都會調用system/core/logd/main.cpp中的main函數,但logd-reinit通過netlink給logd進程發送reinit命令后就會退出。
2) logd 實現log管理
主要原理是:上層應用(Android層)通過調用Log/Slog/Rlog中的v/d方法打印log,最終log會通過netlink傳遞給logd,logd會將log保存在內存中(由C++中的list容器管理,后續會介紹),logcat等log獲取工具同樣通過netlink方式從logd獲取log。
二、logd實現流程
本文主要介紹load側的實現,對於Androidlog如何傳遞給logd,只做簡要介紹。
1. Android層往logd寫log
Android層調用Log/Slog/Rlog中的v/d方法打印log,最終會調用到
system/core/liblog/logger_write.c 中的__android_log_buf_write函數,調用流程如下:
__android_log_buf_write
->write_to_log
->__write_to_log_init
->__write_to_log_initialize
->logdOpen
->__write_to_log_daemon
->logdWrite
文件最終寫到 “/dev/socket/logdw”中,此時logd中的LogListener會監測到有log需寫入,待log保存后,會通知LogReader將新保存的log傳遞給logcat等。
2. Logd log保存機制
在system/core/lodgd/main.cpp文件的main函數中,創建了LogBuffer,LogReader,LogListener和CommandListener四個對象,
LogBuffer用於管理log;
LogReader用於將log傳遞給logcat;
LogListener用於監聽是否有log寫入logd;
CommandListener用於監聽是否有命令發送給logd。
接下來介紹log的保存流程:
1) 創建LogBuffer對象,在classLogBuffer類中,定義了一個list容器,保存了指向LogBufferElement對象的指針,創建LogBuffer對象時在其構造函數中會調用LogBuffer::init()函數初始化各log域(如main/system/kernel/crash等)的大小。
2) 創建LogListener對象並開始監聽
LogListener *swl = newLogListener(logBuf, reader);
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
if(swl->startListener(600)) {
exit(1);
}
3) 在startListener函數中創建線程,線程注冊函數為SocketListener::threadStart;
4) 執行runListener函數,如果socket監聽到數據,則執行onDataAvailable函數進行處理;
5) 調用logbuf->log(LogBuffer::log),這個函數很重要,新建一個LogBufferElement對象(用於保存log),調用mLogElements.insert將LogBufferElement加入list容器,實現log的保存。
3.
三、Logd-reinit 進程
Logd-reinit 進程在啟動時,會給logd進程發送reinit命令,logd在收到命令后,會重新初始化LogBuffer。
在system/core/lodgd/main.cpp文件的main函數中會創建一個線程用於監測是否有reinit請求
if (pthread_create(&thread, &attr,reinit_thread_start, NULL))
在reinit_thread_start函數中,會重新初始化各個log區的大小,以及其他參數的初始化,但不會重新生成LogBuffer對象。相關代碼如下:
//Anything that reads persist.<property>
if(logBuf) {
logBuf->init();
logBuf->initPrune(NULL);
}
四、Logd log 獲取
可通過logcat工具獲取logd log,logcat 相關代碼所在路徑:system/core/logcat
執行adb logcat 即可查看log
注:通過logcat獲取的log,並不完全是按照log分類來打印的,如在KERNEL log中可能存在MAIN log。
logcat實現的大部分函數都在logcat/logcat.cpp文件中,其中__logcat函數是最重要的函數,其負責logcat 輸入參數的解析以及log的處理。
logcat 最終讀取log通過liblog/logd_reader.c 中的logdRead函數實現。此函數負責打開/dev/logdr,並通過socket獲取log。
最近在使用logcat 獲取log時,存在log丟失的情況,不知大家是否有遇到過。
調查發現,當同時啟動6個線程分別獲取main/kernel/system/crash/events等log時,由於啟動的線程太多(即多個socket 與server 連接),導致有log丟失。
原文鏈接:https://blog.csdn.net/yinjian1013/article/details/78261527
