android log系统
目录 |
使用选项简介
Controlling Log Output Format Log messages contain a number of metadata fields, in addition to the tag and priority. You can modify the output format for messages so that they display a specific metadata field. To do so, you use the -v option and specify one of the supported output formats listed below. brief — Display priority/tag and PID of the process issuing the message (the default format). process — Display PID only. tag — Display the priority/tag only. raw — Display the raw log message, with no other metadata fields. time — Display the date, invocation time, priority/tag, and PID of the process issuing the message. threadtime — Display the date, invocation time, priority, tag, and the PID and TID of the thread issuing the message. long — Display all metadata fields and separate messages with blank lines.
如:adb logcat -v threadtime,可同时打印时间、PID和TID,详见 http://developer.android.com/tools/debugging/debugging-log.html
实现方式剖析
一、java接口
android.util.Log
public final class Log { ... public static final int VERBOSE = 2; ... public static int v(String tag, String msg) { return println_native(LOG_ID_MAIN, VERBOSE, tag, msg); } ... public static int v(String tag, String msg, Throwable tr) { return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr)); } ... }
android.util.Slog
public final class Slog { ... public static int v(String tag, String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg); } public static int v(String tag, String msg, Throwable tr) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg + '\n' + Log.getStackTraceString(tr)); } ... public static int println(int priority, String tag, String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, priority, tag, msg); } }
com.mediatek.xlog.Xlog
public final class Xlog { public static int v(String tag, String msg) { return Log.println_native(Log.LOG_ID_MAIN, Log.VERBOSE, tag, msg); } public static int v(String tag, String msg, Throwable tr) { return Log.println_native(Log.LOG_ID_MAIN, Log.VERBOSE, tag, msg + '\n' + Log.getStackTraceString(tr)); } ... }
com.mediatek.xlog.SXlog
public final class SXlog { public static int v(String tag, String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg); } public static int v(String tag, String msg, Throwable tr) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg + '\n' + Log.getStackTraceString(tr)); } ... }
二、jni接口
/frameworks/base/core/jni/android_util_Log.cpp
namespace android { ... 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; } /* * JNI registration. */ static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable }, { "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native }, }; ... }; // namespace android
println_native 等同于 android_util_Log_println_native.
三、hal
/system/core/liblog/logd_write.c
首先来看 jni中 android_util_Log_println_native所调用的函数 __android_log_buf_write:
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); }
此函数只是进行了传入参数的检验,并将 msg和 tag相关信息转存到了 vec[3]这个数组中,接着调用了函数 write_to_log,很容易发现 write_to_log其实是一个函数指针:
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) { #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); }
即除了第一次调用Log系列函数时会通过 __write_to_log_init进行初始化外,后续的调用都会指向到 __write_to_log_kernel当中:
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; }
log_writev则是一个宏:
#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
在android中指向 writev(filedes, vector, count):
bionic/libc/arch-sh/syscalls/writev.S
/* autogenerated by gensyscalls.py */ #include <sys/linux-syscalls.h> .text .type writev, @function .globl writev .align 4 writev: /* invoke trap */ mov.l 0f, r3 /* trap num */ trapa #(3 + 0x10) /* check return value */ cmp/pz r0 bt __NR_writev_end /* keep error number */ sts.l pr, @-r15 mov.l 1f, r1 jsr @r1 mov r0, r4 lds.l @r15+, pr __NR_writev_end: rts nop .align 2 0: .long __NR_writev 1: .long __set_syscall_errno
也就是说,log最终是写入到以下设备文件中的:
/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"
四、Log驱动
kernel/drivers/staging/android/logger.h
... #define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */ #define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */ #define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */ #define LOGGER_LOG_MAIN "log_main" /* everything else */ ...
kernel/drivers/staging/android/logger.c
... 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; init_log_proc(); out: return ret; } ...
五、疑问
文件 kernel/drivers/staging/android/logger.h 中
... #define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */ ...
名字为何与实际 dev/ 文件夹下的 log/radio 不一致???