美文网首页Android开发
Android日志打印(11)

Android日志打印(11)

作者: xcz1899 | 来源:发表于2018-09-14 20:48 被阅读0次

    内核空间日志打印

    日志保存

    在linux内核中使用printk来实现日志打印输出且保存到/proc/kmsg,通过cat /proc/kmsg 或dmesg查看。
    具体的打印级别如下:

    #linux/kernel.h
    
    #define KERN_EMERG  "<0>"    /* system is unusable          */  
    #define KERN_ALERT  "<1>"    /* action must be taken immediately */  
    #define KERN_CRIT  "<2>"    /* critical conditions          */  
    #deinfe KERN_ERR    "<3>"    /* error conditions        */  
    #deinfe KERN_WARNING    "<4>"    /* warning conditions          */  
    #deinfe KERN_NOTICE "<5>"    /* normal but significant condition */  
    #deinfe KERN_INFO  "<6>"    /* informational            */  
    #deinfe KERN_DEBUG  "<7>"    /* debug-level messages        */ 
    
    控制日志打印

    在/proc/sys/kernel/printk保存了4个数字,分别表示当前控制台日志级别、未明确指定日志级别的默认消息日志级别、最高允许设置的控制台日志级别、引导时默认的日志级别。
    当printk的日志级别高于当前控制台日志级别时,信息就会在控制台上显示。我们可以通过echo 0 > /proc/sys/kernel/printk或者类似于echo 0 1 4 7 > /proc/sys/kernel/printk来关闭日志打印

    用户空间日志打印

    在Android用户空间中,提供了一个轻量级的日志系统,这个日志系统是以驱动程序的形式实现在内核空间的,在用户空间分别提供了Java接口和C/C++接口来使用这个日志系统。


    8090047.jpg
    内核层面定义日志文件

    在kernel的Logger驱动程序模块中(android\vendor\mstar\kernel\linaro\drivers\android\logger.c),定义了log_main、log_events、log_system和log_radio日志缓冲区,分别对应设备文件/dev/log/main、/dev/log/events、/dev/log/radio、/dev/log/system。

    static int __init logger_init(void)
    {
        ...
        ret = create_log(LOGGER_LOG_MAIN, 256*1024);
        ret = create_log(LOGGER_LOG_EVENTS, 256*1024);
        ret = create_log(LOGGER_LOG_RADIO, 256*1024);
        ret = create_log(LOGGER_LOG_SYSTEM, 256*1024);
        ...
    }
    

    Logger可以读写/dev/log/main、/dev/log/events、/dev/log/radio、/dev/log/system等文件。

    C/C++层面读写日志文件

    1.在C/C++层定义了ALOG来实现日志打印,我们跟着ALOG来往下追溯源码:

    system/core/include/log/log.h
    
    #ifndef ALOGV
    #define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
    
    #ifndef ALOG
    #define ALOG(priority, tag, ...) \
        LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
    #endif
    
    #ifndef LOG_PRI
    #define LOG_PRI(priority, tag, ...) \
        android_printLog(priority, tag, __VA_ARGS__)
    #endif
    
    #define android_printLog(prio, tag, fmt...) \
        __android_log_print(prio, tag, fmt)
    

    2.__android_log_print()在logd_write.c中定义

    #system/core/liblog/logd_write.c
    
    int __android_log_print(int prio, const char *tag, const char *fmt, ...)
    {
        return __android_log_write(prio, tag, buf);
    }
    
    int __android_log_write(int prio, const char *tag, const char *msg)
    {
        return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
    }
    

    3.又调用logd_write.c中的__android_log_buf_write()函数:

    #system\core\liblog\logd_write.c
    ...
    static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
    ...
    int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
    {
        ...
        /* 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;
        }
        ...
        return write_to_log(bufID, vec, 3);
    }
    

    5.再看write_to_log函数所指向的__write_to_log_init函数:

    #\system\core\liblog\logd_write_kern.c
    
    static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
    {
        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_kernel函数
            write_to_log = __write_to_log_kernel;
            ...
        }
        return write_to_log(log_id, vec, nr);
    }
    

    6.__write_to_log_kernel函数调用log_writev

    #\system\core\liblog\logd_write_kern.c
    
    #define log_writev(filedes, vector, count) writev(filedes, vector, count)
    static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
    {
        do {
            ret = log_writev(log_fd, vec, nr);
            if (ret < 0) {
                ret = -errno;
            }
        } while (ret == -EINTR);
    }
    

    7.最后log_writev()映射到具体的驱动层的writev()函数.把日志写到内核层面定义日志文件:/dev/log/main、/dev/log/events、/dev/log/radio、/dev/log/system。

    8.关于C/C++层面如何打印日志:

    # 方法一
    1.定义TAG,引入log.h
    #define LOG_TAG "lights"
    #include <cutils/log.h>
    
    2.直接用LOGV/LOGD/LOGI/LOGW/LOGE
    
    #方法二
    1.在Android.mk文件中加入LOCAL_LDLIBS := -llog 或 LOCAL_SHARED_LIBRARIES := \ libutils \ liblog \ libcutils
    
    2.在CPP文件中加入
    #include <android/log.h>
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "ProjectName", __VA_ARGS__);
    
    3.直接用LOGD("XXXXX")
    
    Java层面

    1.Java层面通过Log类来实现日志的打印,具体的调用如下:

    #frameworks/base/core/java/android/util/Log.java
    
     public static int e(String tag, String msg) {
            return println_native(LOG_ID_MAIN, ERROR, tag, msg);
        }
        // 这里定义的LOG_ID_MAIN和kernel中定义的对应
        /** @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 static final int LOG_ID_CRASH = 4;   
    

    2.println_native函数在frameworks/base/core/jni/android_util_Log.cpp定义的

    #frameworks/base/core/jni/android_util_Log.cpp
    
    static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
            jint bufID, jint priority, jstring tagObj, jstring msgObj)
    {
        int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
        return res;
    }
    

    3.回到C/C++层面的第3步,发现Java层面和C/C++层面走到了一起

    logcat 命令行工具

    Logcat工具源代码位于system/core/logcat目录下,只有一个源代码文件logcat.cpp,编译后生成的可执行文件位于out/target/product/generic/system/bin目录下。
    Logcat是一个命令行工具,可以把上面存储到/dev/log/main、/dev/log/events、/dev/log/radio、/dev/log/system的日志读取出来。
    -c 清除(刷新)整个日志并退出。
    -d 将日志转储到屏幕并退出。
    -f <filename> 将日志消息输出写入 <filename>。默认值为 stdout。
    -g 打印指定日志缓冲区的大小并退出。
    -n <count> 将已旋转日志的最大数量设置为 <count>。默认值为 4。 需要使用 -r 选项。
    -r <kbytes> 每输出 <kbytes> 时旋转日志文件。默认值为 16。需要使用 -f 选项。
    -s 将默认过滤器规则设为静默式。
    -v <format> 设置日志消息的输出格式。默认值为 brief 格式有关支持的格式列表

    相关文章

      网友评论

        本文标题:Android日志打印(11)

        本文链接:https://www.haomeiwen.com/subject/wouzwftx.html