zoukankan      html  css  js  c++  java
  • Android应用程序框架层和系统运行库层日志系统源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6598703

    在开发Android应用程序时,少不了使用Log来监控和调试程序的执行。在上一篇文章Android日志系统驱动程序Logger源代码分析中,我们分析了驱动程序Logger的源代码,在前面的文章浅谈Android系统开发中Log的使用一文,我们也简单介绍在应用程序中使Log的方法,在这篇文章中,我们将详细介绍Android应用程序框架层和系统运行库存层日志系统的源代码,使得我们可以更好地理解Android的日志系统的实现。

            我们在Android应用程序,一般是调用应用程序框架层的Java接口(android.util.Log)来使用日志系统,这个Java接口通过JNI方法和系统运行库最终调用内核驱动程序Logger把Log写到内核空间中。按照这个调用过程,我们一步步介绍Android应用程序框架层日志系统的源代码。学习完这个过程之后,我们可以很好地理解Android系统的架构,即应用程序层(Application)的接口是如何一步一步地调用到内核空间的。

            一. 应用程序框架层日志系统Java接口的实现。

            在浅谈Android系统开发中Log的使用一文中,我们曾经介绍过Android应用程序框架层日志系统的源代码接口。这里,为了描述方便和文章的完整性,我们重新贴一下这部份的代码,在frameworks/base/core/java/android/util/Log.java文件中,实现日志系统的Java接口:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. ................................................  
    2.   
    3. public final class Log {  
    4.   
    5. ................................................  
    6.   
    7.     /** 
    8.      * Priority constant for the println method; use Log.v. 
    9.          */  
    10.     public static final int VERBOSE = 2;  
    11.   
    12.     /** 
    13.      * Priority constant for the println method; use Log.d. 
    14.          */  
    15.     public static final int DEBUG = 3;  
    16.   
    17.     /** 
    18.      * Priority constant for the println method; use Log.i. 
    19.          */  
    20.     public static final int INFO = 4;  
    21.   
    22.     /** 
    23.      * Priority constant for the println method; use Log.w. 
    24.          */  
    25.     public static final int WARN = 5;  
    26.   
    27.     /** 
    28.      * Priority constant for the println method; use Log.e. 
    29.          */  
    30.     public static final int ERROR = 6;  
    31.   
    32.     /** 
    33.      * Priority constant for the println method. 
    34.          */  
    35.     public static final int ASSERT = 7;  
    36.   
    37. .....................................................  
    38.   
    39.     public static int v(String tag, String msg) {  
    40.         return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);  
    41.     }  
    42.   
    43.     public static int v(String tag, String msg, Throwable tr) {  
    44.         return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + ' ' + getStackTraceString(tr));  
    45.     }  
    46.   
    47.     public static int d(String tag, String msg) {  
    48.         return println_native(LOG_ID_MAIN, DEBUG, tag, msg);  
    49.     }  
    50.   
    51.     public static int d(String tag, String msg, Throwable tr) {  
    52.         return println_native(LOG_ID_MAIN, DEBUG, tag, msg + ' ' + getStackTraceString(tr));  
    53.     }  
    54.   
    55.     public static int i(String tag, String msg) {  
    56.         return println_native(LOG_ID_MAIN, INFO, tag, msg);  
    57.     }  
    58.   
    59.     public static int i(String tag, String msg, Throwable tr) {  
    60.         return println_native(LOG_ID_MAIN, INFO, tag, msg + ' ' + getStackTraceString(tr));  
    61.     }  
    62.   
    63.     public static int w(String tag, String msg) {  
    64.         return println_native(LOG_ID_MAIN, WARN, tag, msg);  
    65.     }  
    66.   
    67.     public static int w(String tag, String msg, Throwable tr) {  
    68.         return println_native(LOG_ID_MAIN, WARN, tag, msg + ' ' + getStackTraceString(tr));  
    69.     }  
    70.   
    71.     public static int w(String tag, Throwable tr) {  
    72.         return println_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));  
    73.     }  
    74.       
    75.     public static int e(String tag, String msg) {  
    76.         return println_native(LOG_ID_MAIN, ERROR, tag, msg);  
    77.     }  
    78.   
    79.     public static int e(String tag, String msg, Throwable tr) {  
    80.         return println_native(LOG_ID_MAIN, ERROR, tag, msg + ' ' + getStackTraceString(tr));  
    81.     }  
    82.   
    83. ..................................................................  
    84.     /** @hide */ public static native int LOG_ID_MAIN = 0;  
    85.     /** @hide */ public static native int LOG_ID_RADIO = 1;  
    86.     /** @hide */ public static native int LOG_ID_EVENTS = 2;  
    87.     /** @hide */ public static native int LOG_ID_SYSTEM = 3;  
    88.   
    89.     /** @hide */ public static native int println_native(int bufID,  
    90.         int priority, String tag, String msg);  
    91. }  

             定义了2~7一共6个日志优先级别ID和4个日志缓冲区ID。回忆一下Android日志系统驱动程序Logger源代码分析一文,在Logger驱动程序模块中,定义了log_main、log_events和log_radio三个日志缓冲区,分别对应三个设备文件/dev/log/main、/dev/log/events和/dev/log/radio。这里的4个日志缓冲区的前面3个ID就是对应这三个设备文件的文件描述符了,在下面的章节中,我们将看到这三个文件描述符是如何创建的。在下载下来的Android内核源代码中,第4个日志缓冲区LOG_ID_SYSTEM并没有对应的设备文件,在这种情况下,它和LOG_ID_MAIN对应同一个缓冲区ID,在下面的章节中,我们同样可以看到这两个ID是如何对应到同一个设备文件的。

             在整个Log接口中,最关键的地方声明了println_native本地方法,所有的Log接口都是通过调用这个本地方法来实现Log的定入。下面我们就继续分析这个本地方法println_native。

             二. 应用程序框架层日志系统JNI方法的实现。

             在frameworks/base/core/jni/android_util_Log.cpp文件中,实现JNI方法println_native:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. /* //device/libs/android_runtime/android_util_Log.cpp 
    2. ** 
    3. ** Copyright 2006, The Android Open Source Project 
    4. ** 
    5. ** Licensed under the Apache License, Version 2.0 (the "License");  
    6. ** you may not use this file except in compliance with the License.  
    7. ** You may obtain a copy of the License at  
    8. ** 
    9. **     http://www.apache.org/licenses/LICENSE-2.0  
    10. ** 
    11. ** Unless required by applicable law or agreed to in writing, software  
    12. ** distributed under the License is distributed on an "AS IS" BASIS,  
    13. ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
    14. ** See the License for the specific language governing permissions and  
    15. ** limitations under the License. 
    16. */  
    17.   
    18. #define LOG_NAMESPACE "log.tag."  
    19. #define LOG_TAG "Log_println"  
    20.   
    21. #include <assert.h>  
    22. #include <cutils/properties.h>  
    23. #include <utils/Log.h>  
    24. #include <utils/String8.h>  
    25.   
    26. #include "jni.h"  
    27. #include "utils/misc.h"  
    28. #include "android_runtime/AndroidRuntime.h"  
    29.   
    30. #define MIN(a,b) ((a<b)?a:b)  
    31.   
    32. namespace android {  
    33.   
    34. struct levels_t {  
    35.     jint verbose;  
    36.     jint debug;  
    37.     jint info;  
    38.     jint warn;  
    39.     jint error;  
    40.     jint assert;  
    41. };  
    42. static levels_t levels;  
    43.   
    44. static int toLevel(const char* value)   
    45. {  
    46.     switch (value[0]) {  
    47.         case 'V': return levels.verbose;  
    48.         case 'D': return levels.debug;  
    49.         case 'I': return levels.info;  
    50.         case 'W': return levels.warn;  
    51.         case 'E': return levels.error;  
    52.         case 'A': return levels.assert;  
    53.         case 'S': return -1; // SUPPRESS  
    54.     }  
    55.     return levels.info;  
    56. }  
    57.   
    58. static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)  
    59. {  
    60. #ifndef HAVE_ANDROID_OS  
    61.     return false;  
    62. #else /* HAVE_ANDROID_OS */  
    63.     int len;  
    64.     char key[PROPERTY_KEY_MAX];  
    65.     char buf[PROPERTY_VALUE_MAX];  
    66.   
    67.     if (tag == NULL) {  
    68.         return false;  
    69.     }  
    70.       
    71.     jboolean result = false;  
    72.       
    73.     const char* chars = env->GetStringUTFChars(tag, NULL);  
    74.   
    75.     if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {  
    76.         jclass clazz = env->FindClass("java/lang/IllegalArgumentException");  
    77.         char buf2[200];  
    78.         snprintf(buf2, sizeof(buf2), "Log tag "%s" exceeds limit of %d characters ",  
    79.                 chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));  
    80.   
    81.         // release the chars!  
    82.         env->ReleaseStringUTFChars(tag, chars);  
    83.   
    84.         env->ThrowNew(clazz, buf2);  
    85.         return false;  
    86.     } else {  
    87.         strncpy(key, LOG_NAMESPACE, sizeof(LOG_NAMESPACE)-1);  
    88.         strcpy(key + sizeof(LOG_NAMESPACE) - 1, chars);  
    89.     }  
    90.       
    91.     env->ReleaseStringUTFChars(tag, chars);  
    92.   
    93.     len = property_get(key, buf, "");  
    94.     int logLevel = toLevel(buf);  
    95.     return (logLevel >= 0 && level >= logLevel) ? true : false;  
    96. #endif /* HAVE_ANDROID_OS */  
    97. }  
    98.   
    99. /* 
    100.  * In class android.util.Log: 
    101.  *  public static native int println_native(int buffer, int priority, String tag, String msg) 
    102.  */  
    103. static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,  
    104.         jint bufID, jint priority, jstring tagObj, jstring msgObj)  
    105. {  
    106.     const char* tag = NULL;  
    107.     const char* msg = NULL;  
    108.   
    109.     if (msgObj == NULL) {  
    110.         jclass npeClazz;  
    111.   
    112.         npeClazz = env->FindClass("java/lang/NullPointerException");  
    113.         assert(npeClazz != NULL);  
    114.   
    115.         env->ThrowNew(npeClazz, "println needs a message");  
    116.         return -1;  
    117.     }  
    118.   
    119.     if (bufID < 0 || bufID >= LOG_ID_MAX) {  
    120.         jclass npeClazz;  
    121.   
    122.         npeClazz = env->FindClass("java/lang/NullPointerException");  
    123.         assert(npeClazz != NULL);  
    124.   
    125.         env->ThrowNew(npeClazz, "bad bufID");  
    126.         return -1;  
    127.     }  
    128.   
    129.     if (tagObj != NULL)  
    130.         tag = env->GetStringUTFChars(tagObj, NULL);  
    131.     msg = env->GetStringUTFChars(msgObj, NULL);  
    132.   
    133.     int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);  
    134.   
    135.     if (tag != NULL)  
    136.         env->ReleaseStringUTFChars(tagObj, tag);  
    137.     env->ReleaseStringUTFChars(msgObj, msg);  
    138.   
    139.     return res;  
    140. }  
    141.   
    142. /* 
    143.  * JNI registration. 
    144.  */  
    145. static JNINativeMethod gMethods[] = {  
    146.     /* name, signature, funcPtr */  
    147.     { "isLoggable",      "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },  
    148.     { "println_native",  "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },  
    149. };  
    150.   
    151. int register_android_util_Log(JNIEnv* env)  
    152. {  
    153.     jclass clazz = env->FindClass("android/util/Log");  
    154.   
    155.     if (clazz == NULL) {  
    156.         LOGE("Can't find android/util/Log");  
    157.         return -1;  
    158.     }  
    159.       
    160.     levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I"));  
    161.     levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I"));  
    162.     levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));  
    163.     levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));  
    164.     levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I"));  
    165.     levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I"));  
    166.                   
    167.     return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));  
    168. }  
    169.   
    170. }; // namespace android  

            在gMethods变量中,定义了println_native本地方法对应的函数调用是android_util_Log_println_native。在android_util_Log_println_native函数中,通过了各项参数验证正确后,就调用运行时库函数__android_log_buf_write来实现Log的写入操作。__android_log_buf_write函实实现在liblog库中,它有4个参数,分别缓冲区ID、优先级别ID、Tag字符串和Msg字符串。下面运行时库liblog中的__android_log_buf_write的实现。

           三. 系统运行库层日志系统的实现。

           在系统运行库层liblog库的实现中,内容比较多,这里,我们只关注日志写入操作__android_log_buf_write的相关实现:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)  
    2. {  
    3.     struct iovec vec[3];  
    4.   
    5.     if (!tag)  
    6.         tag = "";  
    7.   
    8.     /* XXX: This needs to go! */  
    9.     if (!strcmp(tag, "HTC_RIL") ||  
    10.         !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */  
    11.         !strcmp(tag, "AT") ||  
    12.         !strcmp(tag, "GSM") ||  
    13.         !strcmp(tag, "STK") ||  
    14.         !strcmp(tag, "CDMA") ||  
    15.         !strcmp(tag, "PHONE") ||  
    16.         !strcmp(tag, "SMS"))  
    17.             bufID = LOG_ID_RADIO;  
    18.   
    19.     vec[0].iov_base   = (unsigned char *) &prio;  
    20.     vec[0].iov_len    = 1;  
    21.     vec[1].iov_base   = (void *) tag;  
    22.     vec[1].iov_len    = strlen(tag) + 1;  
    23.     vec[2].iov_base   = (void *) msg;  
    24.     vec[2].iov_len    = strlen(msg) + 1;  
    25.   
    26.     return write_to_log(bufID, vec, 3);  
    27. }  

            函数首先是检查传进来的tag参数是否是为HTC_RIL、RIL、AT、GSM、STK、CDMA、PHONE和SMS中的一个,如果是,就无条件地使用ID为LOG_ID_RADIO的日志缓冲区作为写入缓冲区,接着,把传进来的参数prio、tag和msg分别存放在一个向量数组中,调用write_to_log函数来进入下一步操作。write_to_log是一个函数指针,定义在文件开始的位置上:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);  
    2. static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;  

            并且初始化为__write_to_log_init函数:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)  
    2. {  
    3. #ifdef HAVE_PTHREADS  
    4.     pthread_mutex_lock(&log_init_lock);  
    5. #endif  
    6.   
    7.     if (write_to_log == __write_to_log_init) {  
    8.         log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);  
    9.         log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);  
    10.         log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);  
    11.         log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);  
    12.   
    13.         write_to_log = __write_to_log_kernel;  
    14.   
    15.         if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||  
    16.                 log_fds[LOG_ID_EVENTS] < 0) {  
    17.             log_close(log_fds[LOG_ID_MAIN]);  
    18.             log_close(log_fds[LOG_ID_RADIO]);  
    19.             log_close(log_fds[LOG_ID_EVENTS]);  
    20.             log_fds[LOG_ID_MAIN] = -1;  
    21.             log_fds[LOG_ID_RADIO] = -1;  
    22.             log_fds[LOG_ID_EVENTS] = -1;  
    23.             write_to_log = __write_to_log_null;  
    24.         }  
    25.   
    26.         if (log_fds[LOG_ID_SYSTEM] < 0) {  
    27.             log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];  
    28.         }  
    29.     }  
    30.   
    31. #ifdef HAVE_PTHREADS  
    32.     pthread_mutex_unlock(&log_init_lock);  
    33. #endif  
    34.   
    35.     return write_to_log(log_id, vec, nr);  
    36. }  

            这里我们可以看到,如果是第一次调write_to_log函数,write_to_log == __write_to_log_init判断语句就会true,于是执行log_open函数打开设备文件,并把文件描述符保存在log_fds数组中。如果打开/dev/LOGGER_LOG_SYSTEM文件失败,即log_fds[LOG_ID_SYSTEM] < 0,就把log_fds[LOG_ID_SYSTEM]设置为log_fds[LOG_ID_MAIN],这就是我们上面描述的如果不存在ID为LOG_ID_SYSTEM的日志缓冲区,就把LOG_ID_SYSTEM设置为和LOG_ID_MAIN对应的日志缓冲区了。LOGGER_LOG_MAIN、LOGGER_LOG_RADIO、LOGGER_LOG_EVENTS和LOGGER_LOG_SYSTEM四个宏定义在system/core/include/cutils/logger.h文件中:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #define LOGGER_LOG_MAIN     "log/main"  
    2. #define LOGGER_LOG_RADIO    "log/radio"  
    3. #define LOGGER_LOG_EVENTS   "log/events"  
    4. #define LOGGER_LOG_SYSTEM   "log/system"  

            接着,把write_to_log函数指针指向__write_to_log_kernel函数:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)  
    2. {  
    3.     ssize_t ret;  
    4.     int log_fd;  
    5.   
    6.     if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {  
    7.         log_fd = log_fds[(int)log_id];  
    8.     } else {  
    9.         return EBADF;  
    10.     }  
    11.   
    12.     do {  
    13.         ret = log_writev(log_fd, vec, nr);  
    14.     } while (ret < 0 && errno == EINTR);  
    15.   
    16.     return ret;  
    17. }  

           函数调用log_writev来实现Log的写入,注意,这里通过一个循环来写入Log,直到写入成功为止。这里log_writev是一个宏,在文件开始的地方定义为:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #if FAKE_LOG_DEVICE  
    2. // This will be defined when building for the host.  
    3. #define log_open(pathname, flags) fakeLogOpen(pathname, flags)  
    4. #define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)  
    5. #define log_close(filedes) fakeLogClose(filedes)  
    6. #else  
    7. #define log_open(pathname, flags) open(pathname, flags)  
    8. #define log_writev(filedes, vector, count) writev(filedes, vector, count)  
    9. #define log_close(filedes) close(filedes)  
    10. #endif  

           这里,我们看到,一般情况下,log_writev就是writev了,这是个常见的批量文件写入函数,就不多说了。

           至些,整个调用过程就结束了。总结一下,首先是从应用程序层调用应用程序框架层的Java接口,应用程序框架层的Java接口通过调用本层的JNI方法进入到系统运行库层的C接口,系统运行库层的C接口通过设备文件来访问内核空间层的Logger驱动程序。这是一个典型的调用过程,很好地诠释Android的系统架构,希望读者好好领会。

    老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

  • 相关阅读:
    python json 和 pickle的补充 hashlib configparser logging
    go 流程语句 if goto for swich
    go array slice map make new操作
    go 基础
    块级元素 行内元素 空元素
    咽炎就医用药(慢性肥厚性咽炎)
    春季感冒是风寒还是风热(转的文章)
    秋季感冒 咳嗽 怎么选药
    解决IE浏览器“无法显示此网页”的问题
    常用的 css 样式 记录
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4142038.html
Copyright © 2011-2022 走看看