日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

解读Android LOG机制的实现

發(fā)布時間:2024/8/22 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 解读Android LOG机制的实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載自http://www.cnblogs.com/hoys/archive/2011/09/30/2196199.html

?

Android提供了用戶級輕量的LOG機制,它的實現(xiàn)貫穿了Java,JNI,本地c/c++實現(xiàn)以及LINUX內(nèi)核驅(qū)動等Android的各個層次,而且足夠簡單清晰,是一個相當不錯的解讀案例。本系列文章針對LOG機制的內(nèi)部實現(xiàn)機理進行解讀,本文是系列的第一篇,解讀LOG機制的實現(xiàn)架構(gòu)。

?

(1)LOG的實現(xiàn)架構(gòu)

?

LOG的運行環(huán)境

?

??? 下圖是Android官方網(wǎng)站上給出的Android的Debug環(huán)境。

?

?

??? Android的LOG機制當然也在這個環(huán)境中運行。我們重點關(guān)注Emulator和Device上運行的部分,App VMs產(chǎn)生LOG信息,并與ADB Device Daemon交互輸出這些信息,而ADB Device Daemon又通過相應的協(xié)議通過USB(Device)或本地連接(Emulator),與PC上運行的ADB Host Daemon交互,通過PC上的調(diào)試工具呈現(xiàn)給用戶。JDWP Debugger、DDMS、ADB Host Daemon以及ADB Device Daemon之間的交互與其使用的協(xié)議,不在本文討論范圍之內(nèi)。本文討論的內(nèi)容運行在Emulator/Device上,產(chǎn)生LOG信息,并通過程序LogCat輸出。

?

?

?

LOG的實現(xiàn)架構(gòu)

?

??? Android中LOG的實現(xiàn)架構(gòu)如下圖所示,這基本上也是Android的某個模塊實現(xiàn)各個層次的經(jīng)典架構(gòu)。

?

?

??? Android應用程序通過Framework提供的機制操作;Java領(lǐng)域需要本地c/c++提供服務的地方,通過JNI實現(xiàn);JNI調(diào)用底層庫;庫函數(shù)通過操作映射的設備文件操作設備,LINUX kernel中的Driver完成相應的操作。另外,拋開Java和JNI,LINUX上用戶域的c/c++程序,也可以通過操作設備文件來完成。?

?

??? Android的LOG也是這樣實現(xiàn)的,并將在本系列文章中分別講述。應用程序通過android.util.Log里的各種靜態(tài)方法,輸出LOG信息[系列之二中具體講述];Log通過JNI接口調(diào)用c/c++的實現(xiàn),而本地實現(xiàn)的寫LOG,也基本就是寫信息到設備文件[系列之三中具體講述];設備文件是Android為了LOG機制而寫的LINUX的一個輕量級的驅(qū)動logger[系列之四中具體講述];LOG信息的顯示可以是Emulator/Device上運行的LogCat程序[系列之五中具體講述];另外,Android的本地實現(xiàn)庫也可利用現(xiàn)有機制,在c/c++的空間 直接輸出LOG[系列之六中具體講述]。



(2)JAVA域輸出LOG


LOG輸出幫助類

?

Android的Java程序通過android.util.Log類來輸出Log,下圖列出了我們常用的Log的靜態(tài)方法。

?

?

一般,要輸出Log信息,可直接調(diào)用Log.v()/Log.d()/Log.i()/Log.w()/Log.e()等類方法。這里之所以有這么多有區(qū)分的方法,這也是Log的分類。Log的分類就如同Log的靜態(tài)常量成員定義的那樣,而Log的優(yōu)先級按照數(shù)字大小排列,數(shù)字大的優(yōu)先級高。而Log.wtf()記錄的則是非常致命的FAULT信息(What? a Terrible Failure),報這個錯誤,不光是在Log里記錄,還要在界面上有提示,并可能殺死當前的進程。

?

有了這些分類,如果要輸出的LOG優(yōu)先級低于當前設置的優(yōu)先級,則該Log信息不會顯示。一般的,在Java程序中用Log的方法打印Log之前,應先用isLoggable()判斷一下,該級別是否能被記錄。

?

另外,用Log.println()能達到與Log.v()/Log.d()/…等方法同樣的輸出效果,只是在用它時,要指定對應的優(yōu)先級。

?

?

?

類Log的實現(xiàn)

?

類android.util.Log的實現(xiàn)是比較簡單的。

?

?

類android.util.Log的構(gòu)造函數(shù)是私有的,并不會被實例化,只是提供了靜態(tài)的屬性和方法。

?

而android.util.Log的各種Log記錄方法的實現(xiàn)都依賴于native的實現(xiàn)println_native(),Log.v()/Log.d()/Log.i()/Log.w()/Log.e()最終都是調(diào)用了println_native()。如Log.d()的實現(xiàn):

?

??? public static int d(String tag, String msg) {

??????? return println_native(LOG_ID_MAIN, DEBUG, tag, msg);

??? }

?

Native方法println_native()是通過JNI在c/c++中實現(xiàn)的,詳情參閱本系列之三:JNI及c/c++ 域?qū)懺O備文件。

(3)JNI及c/c++域?qū)懺O備文件

?

類Log的JNI實現(xiàn)

?

由前文知道,類android.util.Log有兩個Native方法,需要通過JNI在c/c++中實現(xiàn)。

?

<pre class="java" name="code">public static native boolean isLoggable(String tag, int level);

?

public static native int println_native(int bufID,

??????????? int priority, String tag, String msg);

?

?

?

這兩個方法是在frameworks/base/core/jni/android_util_log.cpp中實現(xiàn)的。如何實現(xiàn)JNI的,在這里不做表述。不過最終這兩個方法分別轉(zhuǎn)入了下列兩個c/c++函數(shù)的調(diào)用。

?

static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)

?

static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,

??????? jint bufID, jint priority, jstring tagObj, jstring msgObj)

?

?

isLoggable()的實現(xiàn)

?

isLoggable的實現(xiàn)是比較<level>(來自參數(shù))與當前property里設定的“l(fā)og.tag.<tag>”(<tag>來自參數(shù))的值,大于或等于都是可記錄的。程序?qū)崿F(xiàn)片斷如下:

?

??? // LOG_NAMESPACE : “l(fā)og.tag.”

??? // chars: convert from param<tag>

??? strncpy(key, LOG_NAMESPACE, sizeof(LOG_NAMESPACE)-1);

??? strcpy(key + sizeof(LOG_NAMESPACE) - 1, chars);

?

??? len = property_get(key, buf, "");

??? int logLevel = toLevel(buf);

?

??? return (logLevel >= 0 && level >= logLevel) ? true : false;

?

?

println_native()的實現(xiàn)

?

函數(shù)android_util_Log_println_native() [文件android_util.Log.cpp中]調(diào)用了__android_log_buf_write()[文件system/core/liblog/logd_write.c中]。__android_log_buf_write()組織了參數(shù),又調(diào)用了write_to_log這個函數(shù)指針。

?

write_to_log這個函數(shù)指針是實現(xiàn)的關(guān)鍵。

?

看write_to_log的定義:

?

static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);

static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;

?

write_to_log初始是指向__write_to_log_init()這個函數(shù)的。所以第一次執(zhí)行write_to_log的時候是執(zhí)行了__write_to_log_init()。而如果write_to_log不是第一次被執(zhí)行,它已經(jīng)在__write_to_log_init()里被修改指向了__write_to_log_kernel()。

?

先看__write_to_log_init()的實現(xiàn):

?

static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)

{

#ifdef HAVE_PTHREADS

??? pthread_mutex_lock(&log_init_lock);

#endif

?

??? if (write_to_log == __write_to_log_init) {

??????? log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);

??????? log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);

??????? log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);

?? ?????log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);

?

??????? write_to_log = __write_to_log_kernel;

?

??????? if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||

??????????????? log_fds[LOG_ID_EVENTS] < 0) {

??????????? log_close(log_fds[LOG_ID_MAIN]);

??????????? log_close(log_fds[LOG_ID_RADIO]);

??????????? log_close(log_fds[LOG_ID_EVENTS]);

??????????? log_fds[LOG_ID_MAIN] = -1;

??????????? log_fds[LOG_ID_RADIO] = -1;

??????????? log_fds[LOG_ID_EVENTS] = -1;

??????????? write_to_log = __write_to_log_null;

??????? }

?

??????? if (log_fds[LOG_ID_SYSTEM] < 0) {

??????????? log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];

??????? }

??? }

?

#ifdef HAVE_PTHREADS

??? pthread_mutex_unlock(&log_init_lock);

#endif

?

??? return write_to_log(log_id, vec, nr);

}

?

基本上就是做互斥訪問的保護,然后如果是第一次調(diào)用(write_to_log還指向__write_to_log_init()),就打開相應的設備文件,獲取描述符,并把write_to_log指向__write_to_log_kernel()。再在__write_to_log_kernel()中具體執(zhí)行寫入文件操作。

?

看__write_to_kernel()的實現(xiàn),基本就是寫操作:

?

static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)

{

??? ssize_t ret;

??? int log_fd;

?

??? if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {

??????? log_fd = log_fds[(int)log_id];

??? } else {

??????? return EBADF;

??? }

?

??? do {

??????? ret = log_writev(log_fd, vec, nr);

??? } while (ret < 0 && errno == EINTR);

?

??? return ret;

}

?

?

總結(jié)一下,println_native()的操作,就是打開設備文件(如果還沒打開),然后寫入數(shù)據(jù)。而具體怎么寫入的,要看Log的設備驅(qū)動Logger的實現(xiàn)。

(4)LOG設備驅(qū)動Logger

Log的驅(qū)動是在kernel/drivers/staging/android/Logger.c中實現(xiàn)的。

?

一、初始化

?

看一個LINUX驅(qū)動,先看它如何初始化的。

?

static int __init init_log(struct logger_log *log)

{

??????? int ret;

?

??????? ret = misc_register(&log->misc);

??????? if (unlikely(ret)) {

??????????????? printk(KERN_ERR "logger: failed to register misc "

??????????????????????????????? "device for log '%s'!\n", log->misc.name);

??????????????? return ret;

??????? }

?

??????? printk(KERN_INFO "logger: created %luK log '%s'\n",

????????????? ?????????(unsigned long) log->size >> 10, log->misc.name);

?

??????? return 0;

}

?

static int __init logger_init(void)

{

??????? int ret;

?

??????? ret = init_log(&log_main);

??????? if (unlikely(ret))

??????????????? goto out;

?

??????? ret = init_log(&log_events);

??????? if (unlikely(ret))

??????????????? goto out;

?

??????? ret = init_log(&log_radio);

??????? if (unlikely(ret))

??????????????? goto out;

?

??????? ret = init_log(&log_system);

??????? if (unlikely(ret))

???????? ???????goto out;

?

out:

??????? return ret;

}

?

device_initcall(logger_init);

?

?

整個Logger驅(qū)動的入口點就是Logger_init(),它用init_log(struct logger_log *log)初始化了log_main, log_events, log_radio和log_system四個logger_log類型的結(jié)構(gòu),而這四個結(jié)構(gòu)變量分別記錄著log的四個存儲體。Logger從這四個變量實現(xiàn)了同種設備的四個驅(qū)動,而log的驅(qū)動是MISC類型的驅(qū)動,通過misc_register()向系統(tǒng)注冊。四次注冊之后,它們對應的MINOR ID將是不同的,Looger也是通過minor來區(qū)分是哪一個驅(qū)動的。

?

static struct logger_log *get_log_from_minor(int minor)

{

??????? if (log_main.misc.minor == minor)

??????????????? return &log_main;

??????? if (log_events.misc.minor == minor)

??????????????? return &log_events;

??????? if (log_radio.misc.minor == minor)

??????????????? return &log_radio;

??????? if (log_system.misc.minor == minor)

??????????????? return &log_system;

??????? return NULL;

}

?

?

本文將以log_main來講解Logger驅(qū)動的實現(xiàn)。

?

?

?

二、關(guān)鍵數(shù)據(jù)結(jié)構(gòu)

?

上節(jié)中,提到了log_main這個結(jié)構(gòu)體變量,現(xiàn)在來看它的定義。

?

?

Log_main里保存了Logger操作必須的變量。buffer指向的真是一個靜態(tài)數(shù)組,用來存放用來讀寫的數(shù)據(jù),Logger用它組成了一個邏輯上的循環(huán)隊列,寫者可以往w_off指向的地方寫東西,而一旦有內(nèi)容,會通知等待隊列wq里的讀者們來讀取內(nèi)容。因為buffer實現(xiàn)的是循環(huán)隊列,所以buffer的大小size經(jīng)常用來做除高位的運算,一定要是一個2次冪的數(shù)字。mutex用來保護log_main這個關(guān)鍵資源的。Logger是MISC類型的驅(qū)動,它保留著一個miscdevice類型的變量misc。misc里面也有最為關(guān)鍵的file_operations結(jié)構(gòu),這正是應用程序通過文件操作,與驅(qū)動打交道的入口。

?

?

?

三、Logger實現(xiàn)的功能

?

從上面log_main的類型定義就能看出,Logger實現(xiàn)了什么。一句話概括Logger就是實現(xiàn)了讀寫者,并實現(xiàn)同步操作。不過,Logger的讀寫者有些特殊,寫者寫操作不會被阻塞,也不會寫滿溢出,也就是寫時只要有內(nèi)容可以不停的寫,超出Buffer就覆蓋舊的[與應用程序具體的寫操作結(jié)合來看];讀者因為要讀的內(nèi)容為空就會被阻塞掛起,而一旦有內(nèi)容,所有被掛起的讀者都會被喚醒[與應用程序具體的讀操作結(jié)合來看]。

?

?

?

下面看具體實現(xiàn)的時候,就分別從讀者和寫者的角度去看。

?

?

?

?

3.1. 寫者的實現(xiàn)

?

看二小節(jié)圖中的關(guān)鍵結(jié)構(gòu)logger_fops: file_operations,寫者的關(guān)鍵實現(xiàn)就看open、release和write這幾個函數(shù)的實現(xiàn)了,它們被分別賦值給了logger_open() / logger_release() / logger_aio_write()。

?

?

logger_open()為寫者做的工作就是,通過minor id獲得logger_log的實例,然后賦值給函數(shù)參數(shù)中傳遞進來的file的private_data中。

?

logger_release()不需要為寫者做的什么工作。

?

?

logger_poll()因為寫不需要被阻塞。所以這里檢測到是因為非因為讀而打開的文件(!(file->f_mode &FMODE_READ))時,就直接返回POLLOUT | POLLWRNORM。無論怎樣都可寫。

?

logger_aio_write()是寫數(shù)據(jù)(也就是log信息)的關(guān)鍵。這里是通過異步IO的方法,應用程序通過write()/writev()和aio_write()時都能調(diào)用到這個方法。

?

記錄log信息時,寫log用的接口是writev(),寫的是vec形式的數(shù)據(jù),這邊寫的過程中來的當然也是vec數(shù)據(jù)了,另外,寫具體之間,還寫入了類型為logger_entry的數(shù)據(jù),來記錄時間等信息。寫數(shù)據(jù)到具體buffer時因為存儲的位置可能不是連續(xù)的,而寫在buffer的結(jié)尾和開頭位置,所以要做判斷,并可能要有兩次寫的buffer的動作。參數(shù)里的數(shù)據(jù)來自用戶空間,不能在內(nèi)核空間直接使用,要用copy_from_user()。寫完之后,用wake_up_interruptible(&log->wq)喚醒所有在掛起等待的讀者。

?

?

?

3.2. 讀者的實現(xiàn)

?

看二小節(jié)圖中的關(guān)鍵結(jié)構(gòu)logger_fops: file_operations,寫者的關(guān)鍵實現(xiàn)就看open、release和read這幾個函數(shù)的實現(xiàn)了,它們被分別賦值給了logger_open() / logger_release() / logger_read()。

?

?

logger_open() 為讀者做的工作就是,通過minor id獲得logger_log的實例,然后動態(tài)申請一個logger_reader類型的讀者,并把它加入到logger_log的讀者列表readers的結(jié)尾,再賦值給函數(shù)參數(shù)中傳遞進來的file的private_data中。

?

logger_release() 與logger_open()對應,將這個讀者從讀者列表logger_log.readers中移除,并釋放掉這個動態(tài)申請的實例。

?

logger_poll()因為應用讀之前會調(diào)用poll()/select()查看是否可以寫。所以這里會用poll_wait()把參數(shù)中的poll_table加入到logger_log.wq中,并且如果有內(nèi)容可讀,才設置可讀標志|= POLLIN |POLLRDNORM。

?

?

logger_read() 是讀數(shù)據(jù)(也就是log信息)的關(guān)鍵。

?

讀數(shù)據(jù)之前,要先保證有數(shù)據(jù),否則該讀者就要被掛起在logger_log的等待隊列wq上。從具體buffer讀數(shù)據(jù)到時因為存儲的位置可能不是連續(xù)的,存儲在buffer的結(jié)尾和開頭位置,所以要做判斷,并可能要有兩次讀去buffer的動作。數(shù)據(jù)來自內(nèi)核空間,要通過用戶空間的參數(shù)里傳遞出去,需要copy_to_user()。

?

?

?

3.3 循環(huán)隊列的實現(xiàn)

?

這個是數(shù)據(jù)結(jié)構(gòu)里最經(jīng)典的案例了,這里不再具體解釋如何實現(xiàn),只是列出重要結(jié)構(gòu),只是希望讀者還記得數(shù)據(jù)結(jié)構(gòu)里邏輯結(jié)構(gòu)和物理結(jié)構(gòu)的說法。

?

隊列大小:log_main.size

寫頭:log_main.w_off

讀頭:logger_reader.r_off

隊列為空判斷:log_main.w_off == logger_reader.r_off

隊列為滿判斷:不需要

?

?

?

3.4 ioctl的實現(xiàn)

?

Logger提供給應用程序通過ioctl()來獲取信息或控制LOGbuffer的功能。Logger是把logger_ioctl通過file_operations注冊到文件系統(tǒng)中來實現(xiàn)這一功能的。Logger_ioctl()提供了下列ioctl控制命令:LOGGER_GET_LOG_BUF_SIZE / LOGGER_GET_LOG_LEN/ LOGGER_GET_NEXT_ENTRY_LEN / LOGGER_FLUSH_LOG。實現(xiàn)很簡單:

?

LOGGER_GET_LOG_BUF_SIZE獲取Buffer的大小,直接返回logger_log.size即可;

?

LOGGER_GET_LOG_LEN只對讀有效,獲取當前LOG的大小,存儲連續(xù)的話就是log->w_off -reader->r_off,否則就是(log->size -reader->r_off) + log->w_off;

?

LOGGER_GET_NEXT_ENTRY_LEN獲取Entry的長度,只對讀有效。

?

LOGGER_FLUSH_LOG只對寫打開有效。所謂FLUSH LOG,直接重置每個reader的r_off,并設置新reader要訪問用的head即可。

從前文知道,LOG被寫入到了驅(qū)動的節(jié)點,那如何獲取這些LOG信息并呈現(xiàn)出來的呢?ANDROID里是有個叫LogCat的應用程序被用來獲取LOG信息。LogCat不僅從設備節(jié)點處獲取LOG,并且還提供了很多選項供用戶來過濾、控制輸出格式等。本文只講解如何獲取LOG部分,相關(guān)的LogCat的使用方式,可參考Android的Logcat命令詳解。

?

LogCat是在文件system/core/logcat/logcat.cpp中實現(xiàn)的。

?

(5)獲取LOG的應用程序LogCat

從Logger設備驅(qū)動的實現(xiàn)知道,Log的讀取是阻塞的操作,亦即,有數(shù)據(jù)可用,讀出數(shù)據(jù);否則,讀操作會被BLOCK,相應的讀進程也會被掛起等待。下面看應用程序LogCat中如何實現(xiàn)讀的,這可能需要不斷回頭與寫操作和驅(qū)動實現(xiàn)結(jié)合來看。

?

看具體實現(xiàn)之前,先看一個logcat中定義的重要的結(jié)構(gòu)體log_device_t。其中的重要的成員在后面用到的時候再具體解釋。

?

?

?

?

一、打開設備節(jié)點

?

Android的Logcat命令詳解的命令參數(shù)-b <buffer>知道,logcat是可以通過參數(shù)來指定對哪個buffer(main/radio/event)進行操作的。Logcat的b參數(shù)解析的地方,是通過傳遞進來的參數(shù)(main/radio/event)來創(chuàng)建了一個上面的結(jié)構(gòu)變量,而這些結(jié)構(gòu)通過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]);

??????????????? }

?

?

而創(chuàng)建實例的時候的參數(shù)被保留了下來,用于后續(xù)操作。

?

<buf>是由LOG_FILE_DIR和optarg(-b參數(shù))組合在一起的(為:“/dev/log/main”,“/dev/log/event”或“/dev/log/radio”),保留在device: char*;

?

<binary>保留在binary: bool;

?

<optarg[0]>是-b參數(shù)的第一個字符,保存在label: char中。

?

好了,下面就有了打開設備節(jié)點時的參數(shù):

?

dev->fd = open(dev->device, mode);

?

dev->device根據(jù)-b的參數(shù)可能為“/dev/log/main”,“/dev/log/event”或“/dev/log/radio”;

?

mode缺省時為O_RDONLY,讀取。只要在運行l(wèi)ogcat時,用了-c參數(shù)清除log時才以O_WRONLY打開。

?

而打開文件的文件操作符保存在log_device_t的fd域中,用于后續(xù)的操作。

?

?

?

獲取Log的操作都是在readLogLines(log_device_t* devices)中實現(xiàn)的。

?

?

?

因為logcat可能會同時操作多個Buffer,而read()會阻塞讀取進程,對其他Buffer的讀取就不能進行,所以這里用select()來判斷可讀取的Buffer。

?

二、select選取可讀取的Buffer

?

Logcat把log_device_t中的所有的buffer的文件操作符dev->fd,都放在readset中[line#7],做為select()的里的<readfds: fd_set*>讀參數(shù),來獲取可讀取的Buffer。這樣當任何一個Buffer上有LOG數(shù)據(jù)時,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()返回之后,通過循環(huán)判定dev->fd是否在readset里被設置(FD_ISSET)[line#3],知道哪個log buffer里已經(jīng)有數(shù)據(jù)了。

?

??????? 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]已經(jīng)有數(shù)據(jù)的LOG Buffer的文件操作符dev->fd就可得到新到來的log了。

?

?

?

應用程序logcat中已經(jīng)獲取了LOG信息,接下來對數(shù)據(jù)的處理就都可以在這里進行了,可以過濾,寫文件,格式化輸入等操作。詳細的logcat的命令參數(shù)可參見Android的Logcat命令詳解.


(6)c/c++域使用LOG

c/c++本地庫中實現(xiàn)LOG輸出

?

通過前面的文章知道Android的Java中通過android.util.Log輸出Log信息,那Android的本地c/c++程序能不能也通過這樣的機制來記錄Log呢?再回頭看Log現(xiàn)有的c/c++的本地實現(xiàn),答案當然是肯定的,而且是相當簡單。Android直接在頭文件(system/core/include/cutils/log.h)里定義了一些宏就可以很好的實現(xiàn)了。

?

因為,LOG分了VERBOSE/DEBUG/INFO/WARN/ERROR/ASSERT等類別,簡單起見,以DEBUG為例的實現(xiàn)來說明。

?

#ifndef LOGD

#define LOGD(...) LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)

#endif

?

#ifndef LOGD_IF

#define LOGD_IF(cond, ...) \

??? ( (CONDITION(cond)) \

??? ? LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) \

??? : (void)0 )

#endif

?

#ifndef LOG

#define LOG(priority, tag, ...) \

??? LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)

#endif

?

#ifndef LOG_PRI

#define LOG_PRI(priority, tag, ...)???????????????????????????????????? \

??? ({????????????????????????????????????????????????????????????????? \

?????? if (((priority == ANDROID_LOG_VERBOSE) && (LOG_NDEBUG == 0)) ||? \

?????????? ((priority == ANDROID_LOG_DEBUG) && (LOG_NDDEBUG == 0))? ||? \

?????????? ((priority == ANDROID_LOG_INFO) && (LOG_NIDEBUG == 0))?? ||? \

??????????? (priority == ANDROID_LOG_WARN)????????????????????????? ||? \

??????????? (priority == ANDROID_LOG_ERROR)?????????????????? ??????||? \

??????????? (priority == ANDROID_LOG_FATAL))??????????????????????????? \

??????????????? (void)android_printLog(priority, tag, __VA_ARGS__);???? \

??? })

#endif

?

#define android_printLog(prio, tag, fmt...) \

__android_log_print(prio, tag, fmt)

?

?

而這一系列宏,最后還是用到了函數(shù)__android_log_print()

?

int __android_log_print(int prio, const char *tag, const char *fmt, ...)

{

??? va_list ap;

??? char buf[LOG_BUF_SIZE];

?

??? va_start(ap, fmt);

??? vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);

??? va_end(ap);

?

??? return __android_log_write(prio, tag, buf);

}

?

這里還是調(diào)到了函數(shù)__android_log_write()。這個函數(shù)應該很熟悉吧,正是前文敘及的c/c++本地函數(shù)實現(xiàn)寫設備文件的地方。

?

?

?

c/c++程序中記錄Log的做法

?

要在c/c++中記錄Log通常的做法是:

?

定義自己的TAG_LOG宏;包含頭文件log.h;然后在需要記錄Log的地方直接用LOGV/LOGD/LOGI/LOGW/LOGE即可。

?

比如,文件lights.c中就在開頭這樣寫,

?

#define LOG_TAG "lights"

#include <cutils/log.h>

?

?

然后在該文件的后續(xù)部分,大量的用了LOGV/LOGE, etc來記錄LOG。

轉(zhuǎn)載于:https://www.cnblogs.com/fwycmengsoft/archive/2011/11/21/2257183.html

總結(jié)

以上是生活随笔為你收集整理的解读Android LOG机制的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。