• 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,欢迎关注!

  • 相关阅读:
    GISer 应届生找工作历程(完结)
    c#跨窗体调用操作
    c#基础学习笔记-----------委托事件
    c#基础笔记-----------集合
    ArcEngine开发鹰眼实现问题
    Null Object模式
    c#基础------------静态类与非静态类
    GIS初学者
    c#基础学习汇总----------base和this,new和virtual
    用Python编写水仙花数
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4142038.html
Copyright © 2020-2023  润新知