居然没写完.完全遗忘了
2015年2月4日星期三,今天心情不畅。或者是这里真的没有那种让人值得留恋的地方。回首这5年来,风风雨雨。恍然有种物是人非的凄凉。
还是看看android的Log日志系统吧。重新将这一切都了解起来。
怎么用这个Log,在文件头,importandroid.util.Log。几乎每天都会接触到。不停的调试。说来也是醉了。竟然重来没曾想了解过它。还是基于4.4.4来看源码。
1. java层的调用
定位下Log.java:frameworks/base/core/java/android/util/Log.java
简单的看下源码:
………..
public final class Log {
/**
* Priority constant forthe println method; use Log.v.
*/
public static final int VERBOSE = 2;
/**
* Priority constant forthe println method; use Log.d.
*/
public static final int DEBUG = 3;
/**
* Priority constant forthe println method; use Log.i.
*/
public static final int INFO = 4;
/**
* Priority constant forthe println method; use Log.w.
*/
public static final int WARN = 5;
/**
* Priority constant forthe println method; use Log.e.
*/
public static final int ERROR = 6;
/**
* Priority constant forthe println method.
*/
public static final int ASSERT = 7;
……..
public static int v(Stringtag, String msg) {
returnprintln_native(LOG_ID_MAIN, VERBOSE, tag, msg);
}
…..
public static int v(Stringtag, String msg, Throwable tr) {
returnprintln_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr));
}
…..
public static int d(Stringtag, String msg) {
returnprintln_native(LOG_ID_MAIN, DEBUG, tag, msg);
}
……..
public static int d(Stringtag, String msg, Throwable tr) {
returnprintln_native(LOG_ID_MAIN, DEBUG, tag, msg + '\n' + getStackTraceString(tr));
}
…….
public static int i(Stringtag, String msg) {
returnprintln_native(LOG_ID_MAIN, INFO, tag, msg);
}
………
public static int i(Stringtag, String msg, Throwable tr) {
return println_native(LOG_ID_MAIN,INFO, tag, msg + '\n' + getStackTraceString(tr));
}
…….
public static int w(Stringtag, String msg) {
returnprintln_native(LOG_ID_MAIN, WARN, tag, msg);
}
……
public static int w(Stringtag, String msg, Throwable tr) {
returnprintln_native(LOG_ID_MAIN, WARN, tag, msg + '\n' + getStackTraceString(tr));
}
……..
public static nativeboolean isLoggable(String tag, int level);
…..
public static int w(Stringtag, Throwable tr) {
return println_native(LOG_ID_MAIN,WARN, tag, getStackTraceString(tr));
}
…..
public static int e(Stringtag, String msg) {
returnprintln_native(LOG_ID_MAIN, ERROR, tag, msg);
}
…..
public static int e(Stringtag, String msg, Throwable tr) {
returnprintln_native(LOG_ID_MAIN, ERROR, tag, msg + '\n' + getStackTraceString(tr));
}
………
/**
* Low-level logging call.
* @param priority Thepriority/type of this log message
* @param tag Used toidentify the source of a log message. Itusually identifies
* the class or activity where the logcall occurs.
* @param msg The messageyou would like logged.
* @return The number ofbytes written.
*/
public static intprintln(int priority, String tag, String msg) {
returnprintln_native(LOG_ID_MAIN, priority, tag, msg);
}
/**@hide */ public static final int LOG_ID_MAIN = 0;
/**@hide */ public static final int LOG_ID_RADIO = 1;
/**@hide */ public static final int LOG_ID_EVENTS = 2;
/**@hide */ public static final int LOG_ID_SYSTEM = 3;
/** @hide */ public staticnative int println_native(int bufID,
int priority,String tag, String msg);
}
这里的代码,有一项bufID,其值可以是LOG_ID_MAIN,LOG_ID_RADIO, LOG_ID_EVENTS, LOG_ID_SYSTEM. 另外一项priority对应VERBOSE(2),DEBUG(3), INFO(4), WARN(5), ERROR(6), ASSERT(7).最终所有的函数调用都会调用到native层的println_native。
2. native层的实现
定位下:frameworks/base/core/jni/android_util_Log.cpp
……
namespace android {
struct levels_t {
jint verbose;
jintdebug;
jint info;
jint warn;
jint error;
jint assert;
};
static levels_t levels;
……
static jintandroid_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;
}
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 },
};
int register_android_util_Log(JNIEnv* env)
{
jclass clazz = env->FindClass("android/util/Log");
if (clazz == NULL) {
ALOGE("Can't find android/util/Log");
return -1;
}
levels.verbose = env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz, "VERBOSE", "I"));
levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz,"DEBUG", "I"));
levels.info = env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz, "INFO", "I"));
levels.warn = env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz, "WARN", "I"));
levels.error = env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz, "ERROR", "I"));
levels.assert = env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz, "ASSERT", "I"));
return AndroidRuntime::registerNativeMethods(env,"android/util/Log", gMethods, NELEM(gMethods));
}
}; // namespace android
这个文件里有结构体levels_t,保存了priovity的阀值。以及以下两个函数
1. register_android_util_Log:中奖java层的几个priority值保存到native层的levels_t结构体中,最终调用AndroidRuntime的registerNativeMethods方法。
2. android_util_Log_println_native:函数判断了bufID的合法性后,就将bufID,priority, tagObj, msgObj四个参数传入__android_log_buf_write中。
了解register_android_util_Log
1. 从何处调用。
这不属于这篇文章的篇幅,大致流程是从init.rcàZygoteàAndroidRuntime::startàAndroidRuntime::startRegà AndroidRuntime::register_jni_procs
在AndroidRuntime的静态数组gRegJNI[]的每个元素存储了各个模块的函数指针。在调用register_jni_procs方法后,就把静态数组gRegJNI[]的各个framework子模块本地方法关联信息注册到系统中。而其中就有register_android_util_Log。
2. registerNativeMethods做何用?
看下AndroidRunntime中registerNativeMethods的定义
int AndroidRuntime::registerNativeMethods(JNIEnv*env,
const char* className, const JNINativeMethod*gMethods, int numMethods)
{
return jniRegisterNativeMethods(env,className, gMethods, numMethods);
}
这里只是对jniRegisterNativeMethods进行封装。
再分析jniRegisterNativeMethods
定位:libnativehelper/JNIHelp.cpp
extern "C" int jniRegisterNativeMethods(C_JNIEnv*env, const char* className,
constJNINativeMethod* gMethods, int numMethods)
{
JNIEnv*e = reinterpret_cast<JNIEnv*>(env);
…..
if((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
char*msg;
asprintf(&msg, "RegisterNatives failed for '%s';aborting...", className);
e->FatalError(msg);
}
return0;
}
将clazz所指向的类作为参数调用了RegisterNatives方法。
这样,虚拟机就有了java跟jni层之间的对应关系。
主线的跟踪android_util_Log_println_native
函数在判断参数合法合理性后,调用了__android_log_buf_write。
定位:system/core/liblog/logd_write.c
int __android_log_buf_write(int bufID, intprio, const char *tag, const char *msg)
{
struct iovec vec[3];
char tmp_tag[32];
if (!tag)
tag = "";
/* XXX: This needs to go! */
if ((bufID != LOG_ID_RADIO) &&
(!strcmp(tag, "HTC_RIL") ||
!strncmp(tag, "RIL", 3) || /* Any log tag with "RIL"as the prefix */
!strncmp(tag, "IMS", 3) || /* Any log tag with "IMS"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;
// Inform third party apps/ril/radio.. to use Rlog or RLOG
snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
tag = tmp_tag;
}
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);
}
函数判断了传入参数的合法合理性,进行处理后,将参数打包传递给结构体iovec,同时将bugID,以及打包好的iovec作为参数传递给write_to_log。
要稍微注意下,如果tag包含HTC_RIL, RIL, IMS, AT, GSM, STK, CDMA, PHONE, SMS, (这些都是涉及到radio),就将bugID设置为LOG_ID_RADIO。将不同的类型归属到不同的缓冲区中。
write_to_log
继续查看代码,write_to_log是个函数指针。会重定向到__write_to_log_init
static int __write_to_log_init(log_id_t,struct iovec *vec, size_t nr);
static int (*write_to_log)(log_id_t, structiovec *vec, size_t nr) = __write_to_log_init;
__write_to_log_init
先看下源码
static int __write_to_log_init(log_id_tlog_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);
}
1. 在write_to_log初始定向到__write_to_log_init后,在__write_to_log_init中,分别打开了4个log文件,并保存文件描述符到log_fds数组中。
2. 初始化4个log_fds后,将write_to_log重定向到__write_to_log_kernel
3. 如果打开任意一个log文件失败,就关闭log通道。
4. 对LOG_ID_SYSTEM类属性有不同处理,如果LOG_ID_SYSTEM打不开就用LOG_ID_MAIN的代替,换言之,在一些情况下,SYSTEM跟MAIN缓冲池上共用的。
5. 在函数中有几个变量LOGGER_LOG_XXXXX,查看system/core/include/log/logger.h可以看到它们的定义。
#defineLOGGER_LOG_MAIN "log/main"
#defineLOGGER_LOG_RADIO "log/radio"
#define LOGGER_LOG_EVENTS "log/events"
#define LOGGER_LOG_SYSTEM "log/system"
6. 最终,android的log日志机制还是通过/dev/log下的四个文件进行的。
7. 在函数再次调用write_to_log,这里是重定向后的,正常情况下的话是write_to_log_kernel
write_to_log_kernel
先看源码
static int __write_to_log_kernel(log_id_tlog_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进行循环写入。
log_writev
#if FAKE_LOG_DEVICE
// This will be defined when building forthe 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) | O_CLOEXEC)
#define log_writev(filedes, vector, count)writev(filedes, vector, count)
#define log_close(filedes) close(filedes)
#endif
Log_writev是个宏定义。FAKE_LOG_DEVICE基本都是false。
writev
在linux中,writev()可以实现在文件和进程的多个缓冲区之间传送数据,免除多次系统调用或复制数据的开销。即收集内存中分散的若干缓冲区中的数据写至文件的连续区域中。
小结
1. Java调用android.util.Log.x进行log信息的记录
2. Log会通过jni调用到c层,通过写4个/dev/log/下的设备文件访问内核中的Logger驱动程序。
那么,问题来了。写了这么多始终不知道logger驱动是怎么回事。
Logger驱动
Logger驱动是个轻量级驱动,它是MISC类型的驱动。根本之前的分析,还知道,Logger驱动是通过循环读写文件来实现的。
Logger驱动目录文件:linux/kernel/drivers/staging/android/logger.c
linux/kernel/drivers/staging/android/logger.h
Logger驱动的启动
定位到文件:linux/kernel/drivers/staging/android/logger.c
在logger.c中,可以找到这么两行,
……
device_initcall(logger_init);
module_exit(logger_exit);
……
在编译器编译的时候,logger_init的起始地址值会被放在一个section中,在kernel初始化段中被取出依此完成初始化。有兴趣的朋友,可以查阅些linux内核,linux驱动方面的书籍。我只是个软件研发的门外汉。
Logger驱动初始化
Kernel初始化时,会调用logger_init,那就先看看logger_init.
/*
*Log size must must be a power of two, and greater than
*(LOGGER_ENTRY_MAX_PAYLOAD + sizeof(struct logger_entry)).
*/
static int __init create_log(char*log_name, int size)
{
intret = 0;
structlogger_log *log;
unsignedchar *buffer;
buffer= vmalloc(size);
if(buffer == NULL)
return-ENOMEM;
log= kzalloc(sizeof(struct logger_log), GFP_KERNEL);
if(log == NULL) {
ret= -ENOMEM;
gotoout_free_buffer;
}
log->buffer= buffer;
log->misc.minor= MISC_DYNAMIC_MINOR;
log->misc.name= kstrdup(log_name, GFP_KERNEL);
if(log->misc.name == NULL) {
ret= -ENOMEM;
gotoout_free_log;
}
log->misc.fops= &logger_fops;
log->misc.parent= NULL;
init_waitqueue_head(&log->wq);
INIT_LIST_HEAD(&log->readers);
INIT_LIST_HEAD(&log->plugins);
mutex_init(&log->mutex);
log->w_off= 0;
log->head= 0;
log->size= size;
INIT_LIST_HEAD(&log->logs);
list_add_tail(&log->logs,&log_list);
/*finally, initialize the misc device for this log */
ret= misc_register(&log->misc);
if(unlikely(ret)) {
pr_err("failedto register misc device for log '%s'!\n",
log->misc.name);
gotoout_free_log;
}
pr_info("created%luK log '%s'\n",
(unsignedlong) log->size >> 10, log->misc.name);
return0;
out_free_log:
kfree(log);
out_free_buffer:
vfree(buffer);
returnret;
}
static int __init logger_init(void)
{
intret;
ret= create_log(LOGGER_LOG_MAIN, 256*1024);
if(unlikely(ret))
gotoout;
ret= create_log(LOGGER_LOG_EVENTS, 256*1024);
if(unlikely(ret))
gotoout;
ret= create_log(LOGGER_LOG_RADIO, 256*1024);
if(unlikely(ret))
gotoout;
ret= create_log(LOGGER_LOG_SYSTEM, 256*1024);
if(unlikely(ret))
gotoout;
out:
returnret;
}
在logger_init中,创建了4个log分区,分别是log_radio、log_events、log_system、log_main。
#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 */
#define LOGGER_ENTRY_MAX_PAYLOAD 4076