zoukankan      html  css  js  c++  java
  • Android6的Logger日志系统

    版权声明:本文为博主原创文章,未经博主允许不得转载。

    Android2.x中Logger日志系统采用了字符设备驱动来支持,到了Android6.0已经找不到kernel/goldfish/drivers/staging/android/logger.c这一层的驱动程序了。不过上层接口还是没变的,所以顺着最顶层接口,可以一路找到6.0下Logger的实现机制。

    从最上层frameworks/base/core/java/android/util/Log.java找到:

    public final class Log {
    ……
        public static int d(String tag, String msg) {
            return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
        }
    ……
    }

    再从frameworks/base/core/jni/android_utilLog.cpp中找到:

    static jint android_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;
    }

    接下来就要去Runtime层找__android_log_buf_write了:system/core/liblog/logd_write.c:

    int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
    {
        struct iovec vec[3];
    
       ……   // 将日志优先级、tag、内容赋给vec的三段
    
        return write_to_log(bufID, vec, 3);
    }

    在文件system/core/liblog/logd_write.c的头部可以看到write_to_log是个函数指针,初始值指向__write_to_log_init:

    static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
    ……
    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) {
            int ret;
    
            ret = __write_to_log_initialize();
        ……
            write_to_log = __write_to_log_daemon;
        }
        ……
        return write_to_log(log_id, vec, nr);
    }

    屏蔽掉不关键的信息,__write_to_log_init首次被执行时调用__write_to_log_initialize,之后把write_to_log指针指向了__write_to_log_daemon,之后再执行write_to_log。以后__android_log_buf_write也将通过write_to_log调用__write_to_log_daemon。

    再来看__write_to_log_initialize:

    static int __write_to_log_initialize()
    {
        int i, ret = 0;
    
    #if FAKE_LOG_DEVICE
        for (i = 0; i < LOG_ID_MAX; i++) {
            char buf[sizeof("/dev/log_system")];
            snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
            log_fds[i] = fakeLogOpen(buf, O_WRONLY);
        }
    #else
        if (pstore_fd < 0) {
            pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
        }
    
        if (logd_fd < 0) {
            i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
            if (i < 0) {
                ret = -errno;
            } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) {
                ret = -errno;
                close(i);
            } else {
                struct sockaddr_un un;
                memset(&un, 0, sizeof(struct sockaddr_un));
                un.sun_family = AF_UNIX;
                strcpy(un.sun_path, "/dev/socket/logdw");
    
                if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un,
                                               sizeof(struct sockaddr_un))) < 0) {
                    ret = -errno;
                    close(i);
                } else {
                    logd_fd = i;
                }
            }
        }
    #endif
    
        return ret;
    }

    用gdb把断点设在函数首行发现直接进入了#else的部分,说明FAKE_LOG_DEVICE或者没定义,或者定义为0,以后都可以忽略。接下来创建scoket并连接/dev/socket/logdw,并将套接字赋给logd_fd。以后的日志都写到了logd_fd中。

    所以研究Android6.0的日志系统在内核层应该去找/dev/socket/logdw的驱动。


    写了个简单的程序从Runtime层由上往下跟进:

    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <android/log.h>
    
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "keymatch", __VA_ARGS___)
    
    int main(int argc, const char** argv)
    {
        __android_log_write(ANDROID_LOG_INFO, "LOG", "ABCDEF");
        printf("123456");
        return 0;
    }

    Android.mk文件需要注意,加上链接libs:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE_TAGS := optional
    LOCAL_LDLIBS := -llog
    LOCAL_MODULE := testlog 
    LOCAL_SRC_FILES := $(call all-subdir-c-files)
    include $(BUILD_EXECUTABLE)

    __android_log_write这个函数定义在logd_write.c中:

    int __android_log_write(int prio, const char *tag, const char *msg)
    {
        return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
    }

    当我用gdb step in 到__android_log_buf_write时,却怎么也进不去:

    (gdb) b main
    Breakpoint 1 at 0x614: file external/testlog/testlog.c, line 10.
    (gdb) c
    The program is not being run.
    (gdb) target remote :1234
    Remote debugging using :1234
    warning: Unable to find dynamic linker breakpoint function.
    GDB will be unable to debug shared library initializers
    and track explicitly loaded dynamic code.
    Cannot access memory at address 0x0
    0xb6ef6658 in ?? ()
    (gdb) c
    Continuing.
    warning: Could not load shared library symbols for 6 libraries, e.g. /system/bin/linker.
    Use the "info sharedlibrary" command to see the complete listing.
    Do you need "set solib-search-path" or "set sysroot"?
    
    Breakpoint 1, main (argc=1, argv=0xbee3dba4) at external/testlog/testlog.c:10
    10        __android_log_write(ANDROID_LOG_INFO, "LOG", "ABCDEF");
    (gdb) set solib-absolute-prefix out/target/product/generic/symb
    warning: Unable to find dynamic linker breakpoint function.
    GDB will be unable to debug shared library initializers
    and track explicitly loaded dynamic code.
    (gdb) set solib-absolute-prefix out/target/product/generic/symbols/
    Reading symbols from out/target/product/generic/symbols/system/bin/linker...done.
    Loaded symbols for out/target/product/generic/symbols/system/bin/linker
    Reading symbols from out/target/product/generic/symbols/system/lib/libc++.so...done.
    Loaded symbols for out/target/product/generic/symbols/system/lib/libc++.so
    Reading symbols from out/target/product/generic/symbols/system/lib/libc.so...done.
    Loaded symbols for out/target/product/generic/symbols/system/lib/libc.so
    Reading symbols from out/target/product/generic/symbols/system/lib/libm.so...done.
    Loaded symbols for out/target/product/generic/symbols/system/lib/libm.so
    Reading symbols from out/target/product/generic/symbols/system/lib/liblog.so...done.
    Loaded symbols for out/target/product/generic/symbols/system/lib/liblog.so
    Reading symbols from out/target/product/generic/symbols/system/lib/libnetd_client.so...done.
    Loaded symbols for out/target/product/generic/symbols/system/lib/libnetd_client.so
    (gdb) n
    9    {
    (gdb) n
    10        __android_log_write(ANDROID_LOG_INFO, "LOG", "ABCDEF");
    (gdb) s
    __android_log_write (prio=4, tag=0xb6f18070 "LOG", msg=0xb6f18074 "ABCDEF") at system/core/liblog/logd_write.c:359
    359    {
    (gdb) s
    360        return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
    (gdb) si
    0xb6d8bcd2    360        return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
    (gdb)
    0xb6d8bcd4    360        return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
    (gdb)
    361    }
    (gdb)

    无论使用s或者si都不行,我判断应该是因为__android_log_buf_write被调用的次数不多,因此被编译器内联了。不过没关系,这只是个过路函数,它内部调用了write_to_log,这是个函数指针,凡是被函数指针指过的函数,应该都不会被内联。因为被内联的函数相当于代码展开,函数不存在了,也就无法被函数指针指向。因此,可以把断点端到write_to_log所指向的__write_to_log_initialize和__write_to_log_daemon。

  • 相关阅读:
    14、Cahin of Responsibility 责任链 COR设计模式
    13、Visitor 访问者模式 访问数据结构并处理数据 行为型设计模式
    HTML inline 与block元素
    javascript 继承实现
    深入理解linux的权限设置和SUID,SGID以及粘滞位
    设置express ejs模板的后缀名html
    Centos 安装编译codeblocks&&codelite
    JavaScript中两种类型的全局对象/函数【转】
    Canvas vs. SVG[转]
    centos 学习总结
  • 原文地址:https://www.cnblogs.com/palance/p/5247093.html
Copyright © 2011-2022 走看看