版權聲明:本文為博主原創文章,未經博主允許不得轉載。
Android2.x中Logger日志系統采用了字符設備驅動來支持,到了Android6.0已經找不到kernel/goldfish/drivers/staging/android/logger.c這一層的驅動程序了。不過上層接口還是沒變的,所以順着最頂層接口,可以一路找到6.0下Logger的實現機制。
從最上層frameworks/base/core/java/android/util/Log.java找到:
public final class Log { …… public static int d(String tag, String msg) { return println_native(LOG_ID_MAIN, DEBUG, tag, msg); } …… }
再從frameworks/base/core/jni/android_utilLog.cpp中找到:
static jint android_util_Log_println_native(JNIEnv* env, jobject clazz, jint bufID, jint priority, jstring tagObj, jstring msgObj) { const char* tag = NULL; const char* msg = NULL; if (msgObj == NULL) { jniThrowNullPointerException(env, "println needs a message"); return -1; } if (bufID < 0 || bufID >= LOG_ID_MAX) { jniThrowNullPointerException(env, "bad bufID"); return -1; } if (tagObj != NULL) tag = env->GetStringUTFChars(tagObj, NULL); msg = env->GetStringUTFChars(msgObj, NULL); int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg); if (tag != NULL) env->ReleaseStringUTFChars(tagObj, tag); env->ReleaseStringUTFChars(msgObj, msg); return res; }
接下來就要去Runtime層找__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]; …… // 將日志優先級、tag、內容賦給vec的三段 return write_to_log(bufID, vec, 3); }
在文件system/core/liblog/logd_write.c的頭部可以看到write_to_log是個函數指針,初始值指向__write_to_log_init:
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init; …… 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) { int ret; ret = __write_to_log_initialize(); …… write_to_log = __write_to_log_daemon; } …… return write_to_log(log_id, vec, nr); }
屏蔽掉不關鍵的信息,__write_to_log_init首次被執行時調用__write_to_log_initialize,之后把write_to_log指針指向了__write_to_log_daemon,之后再執行write_to_log。以后__android_log_buf_write也將通過write_to_log調用__write_to_log_daemon。
再來看__write_to_log_initialize:
static int __write_to_log_initialize() { int i, ret = 0; #if FAKE_LOG_DEVICE for (i = 0; i < LOG_ID_MAX; i++) { char buf[sizeof("/dev/log_system")]; snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i)); log_fds[i] = fakeLogOpen(buf, O_WRONLY); } #else if (pstore_fd < 0) { pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY)); } if (logd_fd < 0) { i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)); if (i < 0) { ret = -errno; } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) { ret = -errno; close(i); } else { struct sockaddr_un un; memset(&un, 0, sizeof(struct sockaddr_un)); un.sun_family = AF_UNIX; strcpy(un.sun_path, "/dev/socket/logdw"); if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un, sizeof(struct sockaddr_un))) < 0) { ret = -errno; close(i); } else { logd_fd = i; } } } #endif return ret; }
用gdb把斷點設在函數首行發現直接進入了#else的部分,說明FAKE_LOG_DEVICE或者沒定義,或者定義為0,以后都可以忽略。接下來創建scoket並連接/dev/socket/logdw,並將套接字賦給logd_fd。以后的日志都寫到了logd_fd中。
所以研究Android6.0的日志系統在內核層應該去找/dev/socket/logdw的驅動。
寫了個簡單的程序從Runtime層由上往下跟進:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <android/log.h> #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "keymatch", __VA_ARGS___) int main(int argc, const char** argv) { __android_log_write(ANDROID_LOG_INFO, "LOG", "ABCDEF"); printf("123456"); return 0; }
Android.mk文件需要注意,加上鏈接libs:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_LDLIBS := -llog LOCAL_MODULE := testlog LOCAL_SRC_FILES := $(call all-subdir-c-files) include $(BUILD_EXECUTABLE)
__android_log_write這個函數定義在logd_write.c中:
int __android_log_write(int prio, const char *tag, const char *msg) { return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg); }
當我用gdb step in 到__android_log_buf_write時,卻怎么也進不去:
(gdb) b main Breakpoint 1 at 0x614: file external/testlog/testlog.c, line 10. (gdb) c The program is not being run. (gdb) target remote :1234 Remote debugging using :1234 warning: Unable to find dynamic linker breakpoint function. GDB will be unable to debug shared library initializers and track explicitly loaded dynamic code. Cannot access memory at address 0x0 0xb6ef6658 in ?? () (gdb) c Continuing. warning: Could not load shared library symbols for 6 libraries, e.g. /system/bin/linker. Use the "info sharedlibrary" command to see the complete listing. Do you need "set solib-search-path" or "set sysroot"? Breakpoint 1, main (argc=1, argv=0xbee3dba4) at external/testlog/testlog.c:10 10 __android_log_write(ANDROID_LOG_INFO, "LOG", "ABCDEF"); (gdb) set solib-absolute-prefix out/target/product/generic/symb warning: Unable to find dynamic linker breakpoint function. GDB will be unable to debug shared library initializers and track explicitly loaded dynamic code. (gdb) set solib-absolute-prefix out/target/product/generic/symbols/ Reading symbols from out/target/product/generic/symbols/system/bin/linker...done. Loaded symbols for out/target/product/generic/symbols/system/bin/linker Reading symbols from out/target/product/generic/symbols/system/lib/libc++.so...done. Loaded symbols for out/target/product/generic/symbols/system/lib/libc++.so Reading symbols from out/target/product/generic/symbols/system/lib/libc.so...done. Loaded symbols for out/target/product/generic/symbols/system/lib/libc.so Reading symbols from out/target/product/generic/symbols/system/lib/libm.so...done. Loaded symbols for out/target/product/generic/symbols/system/lib/libm.so Reading symbols from out/target/product/generic/symbols/system/lib/liblog.so...done. Loaded symbols for out/target/product/generic/symbols/system/lib/liblog.so Reading symbols from out/target/product/generic/symbols/system/lib/libnetd_client.so...done. Loaded symbols for out/target/product/generic/symbols/system/lib/libnetd_client.so (gdb) n 9 { (gdb) n 10 __android_log_write(ANDROID_LOG_INFO, "LOG", "ABCDEF"); (gdb) s __android_log_write (prio=4, tag=0xb6f18070 "LOG", msg=0xb6f18074 "ABCDEF") at system/core/liblog/logd_write.c:359 359 { (gdb) s 360 return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg); (gdb) si 0xb6d8bcd2 360 return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg); (gdb) 0xb6d8bcd4 360 return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg); (gdb) 361 } (gdb)
無論使用s或者si都不行,我判斷應該是因為__android_log_buf_write被調用的次數不多,因此被編譯器內聯了。不過沒關系,這只是個過路函數,它內部調用了write_to_log,這是個函數指針,凡是被函數指針指過的函數,應該都不會被內聯。因為被內聯的函數相當於代碼展開,函數不存在了,也就無法被函數指針指向。因此,可以把斷點端到write_to_log所指向的__write_to_log_initialize和__write_to_log_daemon。
