android 6.0 logcat機制(二)logcat從logd中獲取log儲存到檔案中

kc專欄發表於2016-04-06

這篇部落格分析的是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的。

相關文章