<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                Android系統在運行時庫層提供了一個用來和Logger日志驅動程序進行交互的日志庫liblog。通過日志庫liblog提供的接口,應用程序就可以方便地往Logger日志驅動程序中寫入日志記錄。位于運行時庫層的C/C++日志寫入接口和位于應用程序框架層的Java日志寫入接口都是通過liblog庫提供的日志寫入接口來往Logger日志驅動程序中寫入日志記錄的,因此,在分析這些C/C++或者Java日志寫入接口之前,我們首先介紹liblog庫的日志記錄寫入接口。 日志庫liblog提供的日志記錄寫入接口實現在logd_write.c文件中,它的位置如下: ~~~ ~/Android/system/core ----liblog ----logd_write.c ~~~ 它里面實現了一系列的日志記錄寫入函數,如圖4-7所示。 ![liblog日志庫的函數調用關系](https://box.kancloud.cn/1a90a02056d4bf73315921341dac963a_679x346.jpg =679x346) 根據寫入的日志記錄的類型不同,這些函數可以劃分為三個類別。其中,函數__android_log_assert、__android_log_vprint和__android_log_print用來寫入類型為main的日志記錄;函數__android_log_btwrite和__android_log_bwrite用來寫入類型為events的日志記錄;函數__android_log_buf_print可以寫入任意一種類型的日志記錄。特別地,在函數__android_log_write和__android_log_buf_write中,如果要寫入的日志記錄的標簽以“RIL”開頭或者等于“HTC_RIL”、“AT”、“GSM”、“STK”、“CDMA”、“PHONE”或“SMS”,那么它們就會被認為是radio類型的日志記錄。 無論寫入的是什么類型的日志記錄,它們最終都是通過調用函數write_to_log寫入到Logger日志驅動程序中的。write_to_log是一個函數指針,它開始時指向函數__write_to_log_init。因此,當函數write_to_log第一次被調用時,實際上執行的是函數__write_to_log_init。函數__write_to_log_init執行的是一些日志庫的初始化操作,接著將函數指針write_to_log重定向到函數__write_to_log_kernel或者__write_to_log_null中,這取決于是否成功地將日志設備文件打開。 在本節接下來的內容中,我們就分別描述日志庫liblog提供的日志記錄寫入函數的實現。 **write_to_log** **system/core/liblog/logd_write.c** ~~~ 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。當它第一次被調用時,便會執行函數__write_to_log_init來初始化日志庫liblog,如下所示。 **system/core/liblog/logd_write.c** ~~~ static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) { ...... 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]; } } ...... return write_to_log(log_id, vec, nr); } ~~~ 在函數__write_to_log_init中,第7行如果發現函數指針write_to_log指向的是自己,那么就會調用函數open打開系統中的日志設備文件,并且把得到的文件描述符保存在全局數組log_fds中。 LOG_ID_MAIN、LOG_ID_RADIO、LOG_ID_EVENTS、LOG_ID_SYSTEM和LOG_ID_MAX是五個枚舉值,它們的定義如下所示。 **system/core/include/cutils/log.h** ~~~ typedef enum { LOG_ID_MAIN = 0, LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, LOG_ID_SYSTEM = 3, LOG_ID_MAX } log_id_t; ~~~ LOGGER_LOG_MAIN、LOGGER_LOG_RADIO、LOGGER_LOG_EVENTS和LOGGER_LOG_SYSTEM是四個宏,它們的定義如下所示。 **system/core/include/cutils/logger.h** ~~~ #define LOGGER_LOG_MAIN "log/main" #define LOGGER_LOG_RADIO "log/radio" #define LOGGER_LOG_EVENTS "log/events" #define LOGGER_LOG_SYSTEM "log/system" ~~~ 因此,函數__write_to_log_init的第8行到第11行實際上是調用宏log_open來打開/dev/log/main、/dev/log/radio、/dev/log/events和/dev/log/system四個日志設備文件。宏log_open的定義如下所示。 **system/core/liblog/logd_write.c** ~~~ #if FAKE_LOG_DEVICE // This will be defined when building for the host. #define log_open(pathname, flags) fakeLogOpen(pathname, flags) #define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) #define log_close(filedes) fakeLogClose(filedes) #else #define log_open(pathname, flags) open(pathname, flags) #define log_writev(filedes, vector, count) writev(filedes, vector, count) #define log_close(filedes) close(filedes) #endif ~~~ 在正式環境中編譯日志庫liblog時,宏FAKE_LOG_DEVICE的值定義為0,因此,宏log_open實際上指向的是打開文件操作函數open。從這里同時也可以看到,在正式環境中,宏log_writev和log_close分別指向寫文件操作函數writev和關閉文件操作函數close。 回到函數__write_to_log_init中,第15行的if語句判斷/dev/log/main、/dev/log/radio和/dev/log/events三個日志設備文件是否都打開成功。如果是,就將函數指針write_to_log指向函數__write_to_log_kernel;否則,將函數指針write_to_log指向函數__write_to_log_null。第26行的if語句判斷日志設備文件/dev/log/system是否打開成功。如果不成功,就將log_fds[LOG_ID_SYSTEM]的值設置為log_fds[LOG_ID_MAIN],即將類型為system和main的日志記錄都寫入到日志設備文件/dev/log/main中。 **__write_to_log_kernel** **system/core/liblog/logd_write.c** ~~~ 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; } ~~~ 函數__write_to_log_kernel根據參數log_id在全局數組log_fds中找到對應的日志設備文件描述符,然后調用宏log_writev,即函數writev,把日志記錄寫入到Logger日志驅動程序中。 如果調用宏log_writev寫入日志記錄時,Logger日志驅動程序的返回值小于0,并且錯誤碼等于EINTR,那么就需要重新執行寫入日志記錄的操作。這種情況一般出現在當前進程等待寫入日志記錄的過程中,剛好碰到有新的信號需要處理,這時候內核就會返回一個EINTR錯誤碼給調用者,表示需要調用者再次執行相同的操作。 **__write_to_log_null** **system/core/liblog/logd_write.c** ~~~ static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr) { return -1; } ~~~ 函數__write_to_log_null是一個空實現,什么也不做。在日志設備文件打開失敗的情況下,函數指針write_to_log才會指向該函數。 **__android_log_write** **system/core/liblog/logd_write.c** ~~~ int __android_log_write(int prio, const char *tag, const char *msg) { struct iovec vec[3]; log_id_t log_id = LOG_ID_MAIN; if (!tag) tag = ""; /* XXX: This needs to go! */ if (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || !strcmp(tag, "SMS")) log_id = LOG_ID_RADIO; vec[0].iov_base = (unsigned char *) &prio; vec[0].iov_len = 1; vec[1].iov_base = (void *) tag; vec[1].iov_len = strlen(tag) + 1; vec[2].iov_base = (void *) msg; vec[2].iov_len = strlen(msg) + 1; return write_to_log(log_id, vec, 3); } ~~~ 在默認情況下,函數__android_log_write寫入的日志記錄的類型為main。然而,如果傳進來的日志記錄的標簽以“RIL”開頭或者等于“HTC_RIL”、“AT”、“GSM”、“STK”、“CDMA”、“PHONE”或“SMS”,那么它就會被認為是類型為radio的日志記錄。 第20行到第25行首先將日志記錄的優先級、標簽和內容保存在數組元素vec[0]、vec[1]和vec[2]中,然后再將它們寫入到Logger日志驅動程序中。日志記錄的標簽和內容的類型均為字符串,它們后面緊跟著的字符串結束字符‘\0’也被寫入到Logger日志驅動程序中。這樣做的好處是,可以通過字符串結束字符‘\0’來解析日志記錄的標簽字段和內容字段。 **__android_log_buf_write** **system/core/liblog/logd_write.c** ~~~ int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg) { struct iovec vec[3]; if (!tag) tag = ""; /* XXX: This needs to go! */ if (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || !strcmp(tag, "SMS")) bufID = LOG_ID_RADIO; vec[0].iov_base = (unsigned char *) &prio; vec[0].iov_len = 1; vec[1].iov_base = (void *) tag; vec[1].iov_len = strlen(tag) + 1; vec[2].iov_base = (void *) msg; vec[2].iov_len = strlen(msg) + 1; return write_to_log(bufID, vec, 3); } ~~~ 函數__android_log_buf_write的實現與函數__android_log_write的實現類似,不過它可以指定寫入的日志記錄的類型。特別地,如果要寫入的日志記錄的標簽以“RIL”開頭或者等于“HTC_RIL”、“AT”、“GSM”、“STK”、“CDMA”、“PHONE”或“SMS”,那么它們就會被認為是類型為radio的日志記錄。 函數__android_log_buf_write與前面分析的函數__android_log_write一樣,把緊跟在日志記錄標簽和內容后面的字符串結束符號‘\0’也寫入到Logger日志驅動程序中,目的也是為了以后從Logger日志驅動程序讀取日志時,可以方便地將日志記錄的標簽字段和內容字段解析出來。 **__android_log_vprint、__android_log_print、__android_log_assert** **system/core/liblog/logd_write.c** ~~~ int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap) { char buf[LOG_BUF_SIZE]; vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); return __android_log_write(prio, tag, buf); } 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); } void __android_log_assert(const char *cond, const char *tag, const char *fmt, ...) { char buf[LOG_BUF_SIZE]; if (fmt) { va_list ap; va_start(ap, fmt); vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); va_end(ap); } else { /* Msg not provided, log condition. N.B. Do not use cond directly as * format string as it could contain spurious '%' syntax (e.g. * "%d" in "blocks%devs == 0"). */ if (cond) snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond); else strcpy(buf, "Unspecified assertion failed"); } __android_log_write(ANDROID_LOG_FATAL, tag, buf); __builtin_trap(); /* trap so we have a chance to debug the situation */ } ~~~ 函數__android_log_vprint、__android_log_print和__android_log_assert都是調用函數__android_log_write向Logger日志驅動程序中寫入日志記錄的,它們都可以使用格式化字符串來描述要寫入的日志記錄內容。 **__android_log_buf_print** **system/core/liblog/logd_write.c** ~~~ int __android_log_buf_print(int bufID, 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_buf_write(bufID, prio, tag, buf); } ~~~ 函數__android_log_buf_print是調用函數__android_log_buf_write向Logger日志驅動程序中寫入日志記錄的,它可以指定要寫入的日志記錄的類型,以及使用格式化字符串來描述要寫入的日志記錄內容。 **__android_log_bwrite、__android_log_btwrite** **system/core/liblog/logd_write.c** ~~~ int __android_log_bwrite(int32_t tag, const void *payload, size_t len) { struct iovec vec[2]; vec[0].iov_base = &tag; vec[0].iov_len = sizeof(tag); vec[1].iov_base = (void*)payload; vec[1].iov_len = len; return write_to_log(LOG_ID_EVENTS, vec, 2); } /* * Like __android_log_bwrite, but takes the type as well. Doesn't work * for the general case where we're generating lists of stuff, but very * handy if we just want to dump an integer into the log. */ int __android_log_btwrite(int32_t tag, char type, const void *payload, size_t len) { struct iovec vec[3]; vec[0].iov_base = &tag; vec[0].iov_len = sizeof(tag); vec[1].iov_base = &type; vec[1].iov_len = sizeof(type); vec[2].iov_base = (void*)payload; vec[2].iov_len = len; return write_to_log(LOG_ID_EVENTS, vec, 3); } ~~~ 函數__android_log_bwrite和__android_log_btwrite寫入的日志記錄的類型為events。其中,函數__android_log_bwrite寫入的日志記錄的內容可以由多個值組成,而函數__android_log_btwrite寫入的日志記錄的內容只有一個值。在前面的4.1小節中提到,類型為events的日志記錄的內容一般是由一系列值組成的,每一個值都有自己的名稱、類型和單位。函數__android_log_btwrite就是通過第二個參數type來指定要寫入的日志記錄內容的值類型的,由于它寫入的日志記錄的內容只有一個值,因此,為了方便讀取,就把這個值的類型抽取出來,作為一個獨立的字段寫入到Logger日志驅動程序中。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看