android系統中的log定向

來源:互聯網
上載者:User
http://blog.csdn.net/knock/article/details/5511255
為了調試,必須要將log怎麼列印的搞清楚,於是有了以下的分析。

我們通常在程式中插入LOGD(..),LOGE(..)之類的語句,但什麼情況下可以查看這些列印訊息呢?
首先,來到定義處:system/core/include/cutils/log.h,在開頭就可以看到
#ifndef LOG_TAG
#define LOG_TAG NULL
#endif
所以程式中#include "log.h"之前要定義LOG_TAG,不然就為空白.
再看LOGD的定義
#ifndef LOGD
#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif
跟進
#ifndef LOG
#define LOG(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)

__android_log_print()是位於system/core/liblog/logd_write.c內
int __android_log_print(int prio, const char *tag, const char *fmt, ...)
{
    va_list ap;
    char buf[LOG_BUF_SIZE];   

    va_start(ap, fmt);
    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
    va_end(ap);

    return __android_log_write(prio, tag, buf);
}
看__android_log_write()
int __android_log_write(int prio, const char *tag, const char *msg)
{
    ......

    return write_to_log(log_id, vec, 3);
}
write_to_log定義如下
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)
{
#ifdef HAVE_PTHREADS
    pthread_mutex_lock(&log_init_lock);
#endif

    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);

        write_to_log = __write_to_log_kernel;

        if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
                log_fds[LOG_ID_EVENTS] < 0) {
            log_close(log_fds[LOG_ID_MAIN]);
            log_close(log_fds[LOG_ID_RADIO]);
            log_close(log_fds[LOG_ID_EVENTS]);
            log_fds[LOG_ID_MAIN] = -1;
            log_fds[LOG_ID_RADIO] = -1;
            log_fds[LOG_ID_EVENTS] = -1;
            write_to_log = __write_to_log_null;
        }
    }

#ifdef HAVE_PTHREADS
    pthread_mutex_unlock(&log_init_lock);
#endif

    return write_to_log(log_id, vec, nr);
}
這段的主要意思是開啟/dev/log/main,/dev/log/radio,/dev/log/events三個裝置都成功則將

write_to_log指向__write_to_log_kernel,否則指向__write_to_log_null。
下面就分別看看這兩個
static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr)
{
    return -1;
}

static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
{
    ssize_t ret;
    int log_fd;

    if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
        log_fd = log_fds[(int)log_id];
    } else {
        return EBADF;
    }

    do {
        ret = log_writev(log_fd, vec, nr);
    } while (ret < 0 && errno == EINTR);

    return ret;
}
__write_to_log_null()什麼也不做,表示丟棄log資訊。__write_to_log_kernel會調用log_writev()

將log寫進對應的裝置(/dev/log/*).

為什麼寫進init.rc裡由init來執行的程式不能輸出log呢?下面再來探究一番。。
system/core/init/init.c中,
void service_start(struct service *svc)函數啟動服務,有這麼一句
        if (needs_console) {
            setsid();
            open_console();
        } else {
            zap_stdio();
        }
而這兩個函數為:
static void zap_stdio(void)
{
    int fd;
    fd = open("/dev/null", O_RDWR);
    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
    close(fd);
}

static void open_console()
{
    int fd;
    if ((fd = open(console_name, O_RDWR)) < 0) {
        fd = open("/dev/null", O_RDWR);
    }
    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
    close(fd);
}
zap_stdio()比較狠,直接將STDIN,STDOUT,STDERR都幹掉了,而open_console()則只是在/dev/console

不存在的情況下才幹掉STDIN,STDOUT,STDERR,如果/dev/console存在,則將所有輸入輸出重新導向到它


調用哪個取決於needs_console,
needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
而svc->flags關於SVC_CONSOLE的部分來自於system/core/init/parser.c
static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
       case K_console:
        svc->flags |= SVC_CONSOLE;
        break;

}
這也就是說如果init.rc中service部分有請求console,則可以列印到console。

但怎麼樣才能列印到系統的log中,可以使用logcat來查看呢?這就需要用到logwrapper。
system/core/logwrapper/logwrapper.c中,logwrapper先開啟/dev/ptmx,查詢到裝置名稱後
fork()一個子進程並將STDOUT,STDERR定向到查詢到的裝置。
        // redirect stdout and stderr
        close(parent_ptty);
        dup2(child_ptty, 1);
        dup2(child_ptty, 2);
        close(child_ptty);
然後開始執行要啟動並執行程式
 child(argc - 1, &argv[1]);

總結:
系統中的程式中輸出log一般是到/dev/log/下的三個裝置中,可以用logcat查看。
對於init啟動並執行程式則有兩種方法查看到log資訊:
1.添加/system/bin/logwrapper,可以用logcat查看,例如
 service /system/bin/logwrapper /system/bin/rild
2.添加console,像sh一樣直接輸出到console
 service console /system/bin/sh
      console

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.