android 6.0 logcat机制(二)logcat从logd中获取log保存到文件中

这篇博客分析的是logcat是如何获取logd中的log,然后写入文件。


一、设置保存log文件的路径

在手机刚开机的时候,会有类似如下命令执行

/system/bin/logcat -r 5120 -v threadtime -v usec -v printable -n 5 -f /data/local/log/logcat.log



/system/bin/logcat -r 5120 -v threadtime -v usec -v printable -n 5 -b radio -f /data/local/log/logcat-radio.log

/system/bin/logcat -r 5120 -v threadtime -v usec -v printable -n 5 -b events -f /data/local/log/logcat-events.log

我们先看下logcat的如何对这个命令的实现的,在其main函数中,对f命令的实现如下:

            case 'f':
                if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) {
                    tail_time = lastLogTime(optarg);
                }
                // redirect output to a file
                g_outputFileName = optarg;

把文件名保存在g_outputFileName了,然后在main函数后面会调用setupOutput函数,我们来看下这个函数:

static void setupOutput()
{

    if (g_outputFileName == NULL) {
        g_outFD = STDOUT_FILENO;

    } else {
        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
            fprintf(stderr, "failed to set background scheduling policy\n");
        }

        struct sched_param param;
        memset(¶m, 0, sizeof(param));
        if (sched_setscheduler((pid_t) 0, SCHED_BATCH, ¶m) < 0) {
            fprintf(stderr, "failed to set to batch scheduler\n");
        }

        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
            fprintf(stderr, "failed set to priority\n");
        }

        g_outFD = openLogFile (g_outputFileName);//得到了fd

        if (g_outFD < 0) {
            logcat_panic(false, "couldn't open output file");
        }

        struct stat statbuf;
        if (fstat(g_outFD, &statbuf) == -1) {
            close(g_outFD);
            logcat_panic(false, "couldn't get output file stat\n");
        }

        if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
            close(g_outFD);
            logcat_panic(false, "invalid output file stat\n");
        }

        g_outByteCount = statbuf.st_size;
    }
}

在这个函数中把文件的fd获取到了,是g_outFD。

最后我们可以在printBinary函数中往这个文件中写值。

void printBinary(struct log_msg *buf)
{
    size_t size = buf->len();

    TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
}

也可以通过processBuffer来往文件写log。我们最后应该是通过processBuffer来写log的。

也就是上面的命令最终会把log保存在/data/local/log/logcat-radio.log文件下,当然这只是radio的log。


二、logcat获取logd中的log

而我们再看logcat的main最后是一个死循环,一直调用android_logger_list_read来从logd中获取log,然后再打印。

    while (1) {
        struct log_msg log_msg;
        log_device_t* d;
        int ret = android_logger_list_read(logger_list, &log_msg);//调用android_logger_list_read获取log

        if (ret == 0) {
            logcat_panic(false, "read: unexpected EOF!\n");
        }

        if (ret < 0) {
            if (ret == -EAGAIN) {
                break;
            }

            if (ret == -EIO) {
                logcat_panic(false, "read: unexpected EOF!\n");
            }
            if (ret == -EINVAL) {
                logcat_panic(false, "read: unexpected length.\n");
            }
            logcat_panic(false, "logcat read failure");
        }

        for(d = devices; d; d = d->next) {
            if (android_name_to_log_id(d->device) == log_msg.id()) {
                break;
            }
        }
        if (!d) {
            g_devCount = 2; // set to Multiple
            d = &unexpected;
            d->binary = log_msg.id() == LOG_ID_EVENTS;
        }

        if (dev != d) {
            dev = d;
            maybePrintStart(dev, printDividers);
        }
        if (g_printBinary) {
            printBinary(&log_msg);
        } else {
            processBuffer(dev, &log_msg);
        }
    }

    android_logger_list_free(logger_list);

    return EXIT_SUCCESS;

打印的话就是通过之前传进来的文件,写log到该文件的fd。


android_logger_list_read函数就是通过socket连接logd获取log。

int android_logger_list_read(struct logger_list *logger_list,
                             struct log_msg *log_msg)
{
    int ret, e;
    struct logger *logger;
    struct sigaction ignore;
    struct sigaction old_sigaction;
    unsigned int old_alarm = 0;

    if (!logger_list) {
        return -EINVAL;
    }

    if (logger_list->mode & ANDROID_LOG_PSTORE) {
        return android_logger_list_read_pstore(logger_list, log_msg);
    }

    if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
        memset(&ignore, 0, sizeof(ignore));
        ignore.sa_handler = caught_signal;
        sigemptyset(&ignore.sa_mask);
    }

    if (logger_list->sock < 0) {
        char buffer[256], *cp, c;

        int sock = socket_local_client("logdr",
                                       ANDROID_SOCKET_NAMESPACE_RESERVED,
                                       SOCK_SEQPACKET);
        if (sock < 0) {
            if ((sock == -1) && errno) {
                return -errno;
            }
            return sock;
        }
上面logdr就是logcat到logd的socket。


三、总结

3.1 开3个进程保存不同log

我们手机上会开3个logcat进程来保存log,这3个进程会一直开着就是上面的死循环来不断保存log。

/system/bin/logcat -r 5120 -v threadtime -v usec -v printable -n 5 -f /data/local/log/logcat.log



/system/bin/logcat -r 5120 -v threadtime -v usec -v printable -n 5 -b radio -f /data/local/log/logcat-radio.log

/system/bin/logcat -r 5120 -v threadtime -v usec -v printable -n 5 -b events -f /data/local/log/logcat-events.log


3.2 kernel相关log

另外kernel的log是通过log_read_kern.c中的函数来实现的,而写的话通过logd_write_kern.c来实现的。

是通过节点来实现,而不是通过socket到logd实现的

节点:

dev/log/main

dev/log/radio

dev/log/system

dev/log/events


下篇博客我们主要说下logd是如何处理logcat的请求读log的。

你可能感兴趣的:(android,log机制)