居然没写完.完全遗忘了

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(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

更多相关文章

  1. 一款常用的 Squid 日志分析工具
  2. GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
  3. RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
  4. Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
  5. android的wake_lock介绍
  6. android 编译系统阅读与分析(1)-envsetup.sh (editing....)
  7. android中include文件的使用
  8. Android(安卓)app集成友盟微社区详细步骤
  9. Android(安卓)Resource介绍和使用(一)

随机推荐

  1. Android中的SurfaceFlinger和Choreograph
  2. Android自定义action与permission!!!
  3. Android日志分类及查看过滤
  4. RoboGuice入门
  5. Android(安卓)触摸屏驱动代码分析(ADC 类
  6. Android(安卓)过滤器机制应用演示
  7. Android短信数据库字段描述
  8. Android实现随机圆形云标签效果
  9. 在studio中使用libsdl2.so,编译SDL Demo
  10. Bitmap和libyuv在JNI中的字节序