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 不一致???