• Android架构分析之LOG模块


    作者:刘昊昱 

    博客:http://blog.csdn.net/liuhaoyutz

    Android版本:2.3.7_r1

    Linux内核版本:android-goldfish-2.6.29

    Android的LOG模块分为内核驱动部分和用户空间接口部分。

    一、内核LOG模块分析

    我们先来看内核驱动部分,其代码位于drivers/staging/android/logger.c文件中。按照分析Linux内核驱动程序的惯例,我们从模块初始化函数开始分析:

    588static int __init logger_init(void)
    589{                                                             
    590    int ret;
    591
    592    ret =init_log(&log_main);
    593    if (unlikely(ret))
    594        goto out;
    595
    596    ret =init_log(&log_events);
    597    if (unlikely(ret))
    598        goto out;
    599
    600    ret =init_log(&log_radio);
    601    if (unlikely(ret))
    602        goto out;
    603
    604out:
    605    return ret;
    606}
    607device_initcall(logger_init);


    logger_init函数即是LOG模块初始化函数,其调用了3次init_log函数,注册了log_main,log_events,log_radio三个LOG设备,init_log函数定义如下:

    571static int __init init_log(struct logger_log *log)
    572{
    573    int ret;
    574
    575    ret =misc_register(&log->misc);
    576    if (unlikely(ret)) {
    577        printk(KERN_ERR"logger: failed to register misc "
    578               "device forlog '%s'!
    ", log->misc.name);
    579        return ret;
    580    }
    581
    582    printk(KERN_INFO"logger: created %luK log '%s'
    ",
    583           (unsigned long)log->size >> 10, log->misc.name);
    584
    585    return 0;
    586}


    575行,调用misc_register函数,注册misc设备。

    init_log函数的参数是logger_log结构体类型,该类型代表一个LOG设备,其定义如下:

     30/*
     31 * struct logger_log -represents a specific log, such as 'main' or 'radio'
     32 *
     33 * This structure lives frommodule insertion until module removal, so it does
     34 * not need additionalreference counting. The structure is protected by the
     35 * mutex 'mutex'.
     36 */
     37struct logger_log {
     38    unsigned char *     buffer; /* the ring buffer itself */
     39    struct miscdevice   misc;  /* misc device representing the log */
     40    wait_queue_head_t   wq; /* wait queue for readers */
     41    struct list_head    readers; /* this log's readers */
     42    struct mutex        mutex; /* mutex protecting buffer */
     43    size_t          w_off;  /* current write head offset */
     44    size_t          head;   /* new readers start here */
     45    size_t          size;   /* size of the log */
     46};


    log_main,log_events,log_radio三个LOG设备都是logger_log结构体类型的变量,其定义如下:

    533/*
    534 * Defines a log structure with name 'NAME' and a size of 'SIZE'bytes, which
    535 * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, andless than
    536 * LONG_MAX minus LOGGER_ENTRY_MAX_LEN.
    537 */
    538#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) 
    539static unsigned char _buf_ ## VAR[SIZE]; 
    540static struct logger_log VAR = { 
    541    .buffer = _buf_ ## VAR, 
    542    .misc = { 
    543        .minor =MISC_DYNAMIC_MINOR, 
    544        .name = NAME, 
    545        .fops =&logger_fops, 
    546        .parent = NULL, 
    547    }, 
    548    .wq =__WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), 
    549    .readers = LIST_HEAD_INIT(VAR.readers), 
    550    .mutex =__MUTEX_INITIALIZER(VAR .mutex), 
    551    .w_off = 0, 
    552    .head = 0, 
    553    .size = SIZE, 
    554};
    555
    556DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)
    557DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024)
    558DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024)


    在drivers/staging/android/logger.h文件中,有如下定义:

    33#define LOGGER_LOG_RADIO   "log_radio" /* radio-related messages */
    34#define LOGGER_LOG_EVENTS  "log_events"    /*system/hardware events */
    35#define LOGGER_LOG_MAIN    "log_main"  /*everything else */


    由上面代码的注释,可以理解log_main,log_events,log_radio三种LOG设备的作用。

    综合以上分析,可知在LOG模块初始化函数logger_init中,以misc设备类型注册了3个LOG设备log_main,log_events和log_radio,分别对应/dev/log/main,/dev/log/events,/dev/log/radio,应用空间程序就可以通过对这三个设备进行读写操作与LOG内核驱动模块交互。

    由DEFINE_LOGGER_DEVICE 宏定义可知,LOG设备的操作函数集被设置为logger_fops,其定义如下:

    522static struct file_operations logger_fops = {
    523    .owner = THIS_MODULE,
    524    .read = logger_read,
    525    .aio_write =logger_aio_write,
    526    .poll = logger_poll,
    527    .unlocked_ioctl =logger_ioctl,
    528    .compat_ioctl =logger_ioctl,
    529    .open = logger_open,
    530    .release = logger_release,
    531};


    我们先来看open函数:

    384/*
    385 * logger_open - the log's open() file operation
    386 *
    387 * Note how near a no-op this is in the write-only case. Keep it thatway!
    388 */
    389static int logger_open(struct inode *inode, struct file *file)
    390{
    391    struct logger_log *log;
    392    int ret;
    393
    394    ret =nonseekable_open(inode, file);
    395    if (ret)
    396        return ret;
    397
    398    log =get_log_from_minor(MINOR(inode->i_rdev));
    399    if (!log)
    400        return -ENODEV;
    401
    402    if (file->f_mode &FMODE_READ) {
    403        struct logger_reader*reader;
    404
    405        reader =kmalloc(sizeof(struct logger_reader), GFP_KERNEL);
    406        if (!reader)
    407            return -ENOMEM;
    408
    409        reader->log = log;
    410       INIT_LIST_HEAD(&reader->list);
    411
    412       mutex_lock(&log->mutex);
    413        reader->r_off =log->head;
    414       list_add_tail(&reader->list, &log->readers);
    415       mutex_unlock(&log->mutex);
    416
    417        file->private_data =reader;
    418    } else
    419        file->private_data =log;
    420
    421    return 0;
    422}
    423


    398行,调用get_log_from_minor函数,通过次设备号来取得对应的logger_log结构体变量。该函数定义如下:

    560static struct logger_log * get_log_from_minor(int minor)
    561{
    562    if (log_main.misc.minor ==minor)
    563        return &log_main;
    564    if (log_events.misc.minor== minor)
    565        return &log_events;
    566    if (log_radio.misc.minor ==minor)
    567        return &log_radio;
    568    return NULL;
    569}


    回到logger_open函数,402-418行,如果打开的LOG设备是可读的,创建一个logger_reader结构体变量,并初始化。logger_reader结构体代表被打开进行读操作的LOG设备,其定义如下:

     48/*
     49 * struct logger_reader - alogging device open for reading
     50 *
     51 * This object lives from opento release, so we don't need additional
     52 * reference counting. Thestructure is protected by log->mutex.
     53 */
     54struct logger_reader {
     55    struct logger_log * log;    /* associated log */
     56    struct list_head    list;  /* entry in logger_log's list */
     57    size_t          r_off;  /* current read head offset */
     58};


    下面我们来看read函数:

    143/*
    144 * logger_read - our log's read() method
    145 *
    146 * Behavior:
    147 *
    148 *  - O_NONBLOCK works
    149 *  - If there are no logentries to read, blocks until log is written to
    150 *  - Atomically reads exactlyone log entry
    151 *
    152 * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno toEINVAL if read
    153 * buffer is insufficient to hold next entry.
    154 */
    155static ssize_t logger_read(struct file *file, char __user *buf,
    156               size_t count,loff_t *pos)
    157{
    158    struct logger_reader*reader = file->private_data;
    159    struct logger_log *log =reader->log;
    160    ssize_t ret;
    161    DEFINE_WAIT(wait);
    162
    163start:
    164    while (1) {
    165       prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);
    166
    167        mutex_lock(&log->mutex);
    168        ret = (log->w_off ==reader->r_off);
    169       mutex_unlock(&log->mutex);
    170        if (!ret)
    171            break;
    172
    173        if (file->f_flags& O_NONBLOCK) {
    174            ret = -EAGAIN;
    175            break;
    176        }
    177
    178        if(signal_pending(current)) {
    179            ret = -EINTR;
    180            break;
    181        }
    182
    183        schedule();
    184    }
    185
    186   finish_wait(&log->wq, &wait);
    187    if (ret)
    188        return ret;
    189
    190   mutex_lock(&log->mutex);
    191
    192    /* is there still somethingto read or did we race? */
    193    if (unlikely(log->w_off== reader->r_off)) {
    194       mutex_unlock(&log->mutex);
    195        goto start;
    196    }
    197
    198    /* get the size of the nextentry */
    199    ret = get_entry_len(log,reader->r_off);
    200    if (count < ret) {
    201        ret = -EINVAL;
    202        goto out;
    203    }
    204
    205    /* get exactly one entryfrom the log */
    206    ret =do_read_log_to_user(log, reader, buf, ret);
    207
    208out:
    209   mutex_unlock(&log->mutex);
    210
    211    return ret;
    212}


    164-184行,这个while循环是用来处理如果没有LOG可读,则进入休眠等待。但是如果文件打开时被设置为非阻塞模式O_NONBLOCK或者有信号需要处理signal_pending(current),则不休眠等待,直接返回。

    LOG内容保存在一个循环缓冲区中,代码中通过log->w_off == reader->r_off判断是否有LOG可读。

    198-203行,如果有LOG可读,则调用get_entry_len函数取得下一条LOG的长度(LOG的长度包括loger_entry结构体的大小和有效负载payload的长度),该函数定义如下:

    86/*
     87 * get_entry_len - Grabs thelength of the payload of the next entry starting
     88 * from 'off'.
     89 *
     90 * Caller needs to holdlog->mutex.
     91 */
     92static __u32get_entry_len(struct logger_log *log, size_t off)
     93{
     94    __u16 val;
     95
     96    switch (log->size - off) {
     97    case 1:
     98        memcpy(&val, log->buffer + off,1);
     99        memcpy(((char *) &val) + 1,log->buffer, 1);
    100        break;
    101    default:
    102        memcpy(&val,log->buffer + off, 2);
    103    }
    104
    105    return sizeof(structlogger_entry) + val;
    106}


    LOG缓冲区中的每一条LOG由两部分组成,一是用于描述LOG信息的logger_entry结构体,二是LOG本身,又称为payload。在drivers/staging/android/logger.h文件中,logger_entry结构体定义如下:

    23struct logger_entry {
    24    __u16       len;   /* length of the payload */
    25    __u16       __pad; /* no matter what, we get 2 bytes of padding */
    26    __s32       pid;   /* generating process's pid */
    27    __s32       tid;   /* generating process's tid */
    28    __s32       sec;   /* seconds since Epoch */
    29    __s32       nsec;  /* nanoseconds */
    30    char        msg[0]; /* the entry's payload */
    31};


    get_entry_len函数用于取得整个LOG的长度,包括logger_entry结构体大小和payload的长度,logger_entry的大小是固定的,关键是怎样取得payload的长度。

    payload的长度记录在logger_entry第一个成员len中,16位,占2个字节。因为LOG缓冲区是一个循环缓冲区,所以这2个字节存放的位置有一种特殊情况是,第一个字节在最后一个位置,第二个字节在第一个位置。所以在get_entry_len函数的实现中,分两种情况处理,case 1就是分别拷贝第一个字节和第二个字节到val变量中,其它的情况都是直接将2个节的内容拷贝到val变量中。

    最后,注意get_entry_len函数的105行,返回值是sizeof(struct logger_entry) + val,即我们前面所说的,返回logger_entry结构体的大小加上payload的长度。

    回到logger_read函数,206行,调用do_read_log_to_user函数,该函数真正将LOG信息读到用户空间,定义如下:

    108/*
    109 * do_read_log_to_user - reads exactly 'count' bytes from 'log' intothe
    110 * user-space buffer 'buf'. Returns 'count' on success.
    111 *
    112 * Caller must hold log->mutex.
    113 */
    114static ssize_t do_read_log_to_user(struct logger_log *log,
    115                   structlogger_reader *reader,
    116                   char __user*buf,
    117                   size_tcount)
    118{
    119    size_t len;
    120
    121    /*
    122     * We read from the log intwo disjoint operations. First, we read from
    123     * the current read headoffset up to 'count' bytes or to the end of
    124     * the log, whichever comesfirst.
    125     */
    126    len = min(count, log->size- reader->r_off);
    127    if (copy_to_user(buf,log->buffer + reader->r_off, len))
    128        return -EFAULT;
    129
    130    /*
    131     * Second, we read anyremaining bytes, starting back at the head of
    132     * the log.
    133     */
    134    if (count != len)
    135        if (copy_to_user(buf +len, log->buffer, count - len))
    136            return -EFAULT;
    137
    138    reader->r_off =logger_offset(reader->r_off + count);
    139
    140    return count;
    141}


    因为LOG保存在循环缓冲区中,所以do_read_log_to_user函数考虑到这种情况,分两步通过copy_to_user函数拷贝LOG到用户空间。

    最后注意138行,通过logger_offset宏设置下一次的读取位置。该宏定义如下:

     60/* logger_offset- returns index 'n' into the log via (optimized) modulus */
     61#define logger_offset(n)    ((n) & (log->size - 1))


    下面我们来看LOG模块的write函数:

    317/*
    318 * logger_aio_write - our write method, implementing support forwrite(),
    319 * writev(), and aio_write(). Writes are our fast path, and we try tooptimize
    320 * them above all else.
    321 */
    322ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,
    323             unsigned longnr_segs, loff_t ppos)
    324{
    325    struct logger_log *log =file_get_log(iocb->ki_filp);
    326    size_t orig =log->w_off;
    327    struct logger_entry header;
    328    struct timespec now;
    329    ssize_t ret = 0;
    330
    331    now =current_kernel_time();
    332
    333    header.pid =current->tgid;
    334    header.tid =current->pid;
    335    header.sec = now.tv_sec;
    336    header.nsec = now.tv_nsec;
    337    header.len = min_t(size_t,iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);
    338
    339    /* null writes succeed,return zero */
    340    if (unlikely(!header.len))
    341        return 0;
    342
    343   mutex_lock(&log->mutex);
    344
    345    /*
    346     * Fix up any readers,pulling them forward to the first readable
    347     * entry after (what willbe) the new write offset. We do this now
    348     * because if we partiallyfail, we can end up with clobbered log
    349     * entries that encroach onreadable buffer.
    350     */
    351    fix_up_readers(log,sizeof(struct logger_entry) + header.len);
    352
    353    do_write_log(log,&header, sizeof(struct logger_entry));
    354
    355    while (nr_segs-- > 0) {
    356        size_t len;
    357        ssize_t nr;
    358
    359        /* figure out how muchof this vector we can keep */
    360        len = min_t(size_t,iov->iov_len, header.len - ret);
    361
    362        /* write out thissegment's payload */
    363        nr =do_write_log_from_user(log, iov->iov_base, len);
    364        if (unlikely(nr <0)) {
    365            log->w_off = orig;
    366           mutex_unlock(&log->mutex);
    367            return nr;
    368        }
    369
    370        iov++;
    371        ret += nr;
    372    }
    373
    374   mutex_unlock(&log->mutex);
    375
    376    /* wake up any blockedreaders */
    377   wake_up_interruptible(&log->wq);
    378
    379    return ret;
    380}


    325行,调用file_get_log函数取得要读取的LOG设备:

     77static inlinestruct logger_log * file_get_log(struct file *file)
     78{
     79    if (file->f_mode & FMODE_READ) {
     80        struct logger_reader *reader =file->private_data;
     81        return reader->log;
     82    } else
     83        return file->private_data;
     84}


    327行,定义了一个logger_entry结构体变量header,logger_entry结构体用于描述一个LOG的信息,定义在drivers/staging/android/logger.h文件中:

    23struct logger_entry {
    24    __u16       len;   /* length of the payload */
    25    __u16       __pad; /* no matter what, we get 2 bytes of padding */
    26    __s32       pid;   /* generating process's pid */
    27    __s32       tid;   /* generating process's tid */
    28    __s32       sec;   /* seconds since Epoch */
    29    __s32       nsec;  /* nanoseconds */
    30    char        msg[0]; /* the entry's payload */
    31};


    333-337行,对logger_entry结构体变量header进行初始化。

    351行,调用fix_up_readers函数,修正某些logger_read的读取位置指针。因为LOG缓冲区是循环使用的,当进行写操作后,可能会覆盖一些末读取的内容,因此,需要修正某些logger_read的读取位置指针。

    250/*
    251 * fix_up_readers - walk the list of all readers and "fixup" any who were
    252 * lapped by the writer; also do the same for the default "starthead".
    253 * We do this by "pulling forward" the readers and starthead to the first
    254 * entry after the new write head.
    255 *
    256 * The caller needs to hold log->mutex.
    257 */
    258static void fix_up_readers(struct logger_log *log, size_t len)
    259{
    260    size_t old = log->w_off;
    261    size_t new =logger_offset(old + len);
    262    struct logger_reader*reader;
    263
    264    if (clock_interval(old,new, log->head))
    265        log->head =get_next_entry(log, log->head, len);
    266
    267    list_for_each_entry(reader,&log->readers, list)
    268        if (clock_interval(old,new, reader->r_off))
    269            reader->r_off =get_next_entry(log, reader->r_off, len);
    270}


    264行,调用clock_interval(old, new, log->head)函数,判断第三个参数log->head是否在第一个和第二个参数范围之内,即判断第三个参数log->head指定的位置是否会被本次write操作覆盖。clock_interval函数定义如下:

    233/*
    234 * clock_interval - is a < c < b in mod-space? Put another way,does the line
    235 * from a to b cross c?
    236 */
    237static inline int clock_interval(size_t a, size_t b, size_t c)
    238{
    239    if (b < a) {      // 转到循环缓冲区前面
    240        if (a < c || b >=c)
    241            return 1;
    242    } else {
    243        if (a < c &&b >= c)
    244            return 1;
    245    }
    246
    247    return 0;
    248}


    回到fix_up_readers 函数,265行,如果log->head指定的位置会被本次write操作覆盖,则调用get_next_entry获得下一条LOG记录的起始位置,并赋值给log->head。get_next_entry函数定义如下:

    214/*
    215 * get_next_entry - return the offset of the first valid entry atleast 'len'
    216 * bytes after 'off'.
    217 *
    218 * Caller must hold log->mutex.
    219 */
    220static size_t get_next_entry(struct logger_log *log, size_t off,size_t len)
    221{
    222    size_t count = 0;
    223
    224    do {
    225        size_t nr =get_entry_len(log, off);  // 取得一下条记录的长度
    226        off = logger_offset(off+ nr);     // off指向一条记录
    227        count += nr;
    228    } while (count < len);
    229
    230    return off;
    231}


    回到fix_up_readers 函数,267-269行,遍历log->readers列表。对于每个logger_reader,如果logger_reader.r_off被覆盖,则向后做偏移。

    回到logger_aio_write函数,353行,调用do_write_log函数,将logger_entry写入LOG缓冲区。do_write_log函数定义如下:

    272/*
    273 * do_write_log - writes 'len' bytes from 'buf' to 'log'
    274 *
    275 * The caller needs to hold log->mutex.
    276 */
    277static void do_write_log(struct logger_log *log, const void *buf,size_t count)
    278{
    279    size_t len;
    280
    281    len = min(count,log->size - log->w_off); // 处理后半部分
    282    memcpy(log->buffer +log->w_off, buf, len);
    283
    284    if (count != len) // 如果有需要,处理前半部分
    285        memcpy(log->buffer,buf + len, count - len);
    286
    287    log->w_off =logger_offset(log->w_off + count);
    288
    289}


    回到logger_aio_write函数,355-372行,通过这个while循环将用户空间提供的LOG内容写入LOG设备中。真正的写操作是通过do_write_log_from_user函数完成的,该函数定义如下:

    291/*
    292 * do_write_log_user - writes 'len' bytes from the user-space buffer'buf' to
    293 * the log 'log'
    294 *
    295 * The caller needs to hold log->mutex.
    296 *
    297 * Returns 'count' on success, negative error code on failure.
    298 */
    299static ssize_t do_write_log_from_user(struct logger_log *log,
    300                      constvoid __user *buf, size_t count)
    301{
    302    size_t len;
    303
    304    len = min(count,log->size - log->w_off);
    305    if (len &©_from_user(log->buffer + log->w_off, buf, len))
    306        return -EFAULT;
    307
    308    if (count != len)
    309        if(copy_from_user(log->buffer, buf + len, count - len))
    310            return -EFAULT;
    311
    312    log->w_off =logger_offset(log->w_off + count);
    313
    314    return count;
    315}


    回到logger_aio_write函数,377行,调用wake_up_interruptible函数唤醒在log->wq上等待的logger_reader。

    至此,内核空间的LOG模块我们就分析完了。

    二、用户空间LOG模块分析

    Android应用程序是通过应用程序框架层的JAVA接口android.util.Log来使用LOG系统的,该接口定义在frameworks/base/core/java/android/util/Log.java文件中:

    52public finalclass Log {
     53
     54    /**
     55     * Priority constant for the printlnmethod; use Log.v.
     56     */
     57    public static final int VERBOSE = 2;
     58
     59    /**
     60     * Priority constant for the printlnmethod; use Log.d.
     61     */
     62    public static final int DEBUG = 3;
     63
     64    /**
     65     * Priority constant for the printlnmethod; use Log.i.
     66     */
     67    public static final int INFO = 4;
     68
     69    /**
     70     * Priority constant for the printlnmethod; use Log.w.
     71     */
     72    public static final int WARN = 5;
     73
     74    /**
     75     * Priority constant for the printlnmethod; use Log.e.
     76     */
     77    public static final int ERROR = 6;
     78
     79    /**
     80     * Priority constant for the printlnmethod.
     81     */
     82    public static final int ASSERT = 7;
     83
     84    /**
     85     * Exception class used to capture a stacktrace in {@link #wtf()}.
     86     */
     87    private static class TerribleFailureextends Exception {
     88        TerribleFailure(String msg, Throwablecause) { super(msg, cause); }
     89    }
     90
     91    /**
     92     * Interface to handle terrible failuresfrom {@link #wtf()}.
     93     *
     94     * @hide
     95     */
     96    public interface TerribleFailureHandler {
    97        void onTerribleFailure(String tag, TerribleFailurewhat);
     98    }
     99
    100    private staticTerribleFailureHandler sWtfHandler = new TerribleFailureHandler() {
    101            public voidonTerribleFailure(String tag, TerribleFailure what) {
    102               RuntimeInit.wtf(tag, what);
    103            }
    104        };
    105
    106    private Log() {
    107    }
    108
    109    /**
    110     * Send a {@link #VERBOSE}log message.
    111     * @param tag Used toidentify the source of a log message.  Itusually identifies
    112     *        the class or activity where the logcall occurs.
    113     * @param msg The messageyou would like logged.
    114     */
    115    public static int v(Stringtag, String msg) {
    116        returnprintln_native(LOG_ID_MAIN, VERBOSE, tag, msg);
    117    }
    118
    119    /**
    120     * Send a {@link #VERBOSE}log message and log the exception.
    121     * @param tag Used toidentify the source of a log message.  Itusually identifies
    122     *        the class or activity where the logcall occurs.
    123     * @param msg The messageyou would like logged.
    124     * @param tr An exceptionto log
    125     */
    126    public static int v(Stringtag, String msg, Throwable tr) {
    127        returnprintln_native(LOG_ID_MAIN, VERBOSE, tag, msg + '
    ' + getStackTraceString(tr));
    128    }
    129
    130    /**
    131     * Send a {@link #DEBUG}log message.
    132     * @param tag Used toidentify the source of a log message.  Itusually identifies
    133     *        the class or activity where the logcall occurs.
    134     * @param msg The messageyou would like logged.
    135     */
    136    public static int d(Stringtag, String msg) {
    137        returnprintln_native(LOG_ID_MAIN, DEBUG, tag, msg);
    138    }
    139
    140    /**
    141     * Send a {@link #DEBUG}log message and log the exception.
    142     * @param tag Used toidentify the source of a log message.  Itusually identifies
    143     *        the class or activity where the logcall occurs.
    144     * @param msg The messageyou would like logged.
    145     * @param tr An exceptionto log
    146     */
    147    public static int d(Stringtag, String msg, Throwable tr) {
    148        returnprintln_native(LOG_ID_MAIN, DEBUG, tag, msg + '
    ' + getStackTraceString(tr));
    149    }
    150
    151    /**
    152     * Send an {@link #INFO}log message.
    153     * @param tag Used toidentify the source of a log message.  Itusually identifies
    154     *        the class or activity where the logcall occurs.
    155     * @param msg The messageyou would like logged.
    156     */
    157    public static int i(Stringtag, String msg) {
    158        returnprintln_native(LOG_ID_MAIN, INFO, tag, msg);
    159    }
    160
    161    /**
    162     * Send a {@link #INFO} logmessage and log the exception.
    163     * @param tag Used toidentify the source of a log message.  Itusually identifies
    164     *        the class or activity where the logcall occurs.
    165     * @param msg The messageyou would like logged.
    166     * @param tr An exceptionto log
    167     */
    168    public static int i(Stringtag, String msg, Throwable tr) {
    169        returnprintln_native(LOG_ID_MAIN, INFO, tag, msg + '
    ' + getStackTraceString(tr));
    170    }
    171
    172    /**
    173     * Send a {@link #WARN} logmessage.
    174     * @param tag Used toidentify the source of a log message.  Itusually identifies
    175     *        the class or activity where the logcall occurs.
    176     * @param msg The messageyou would like logged.
    177     */
    178    public static int w(Stringtag, String msg) {
    179        returnprintln_native(LOG_ID_MAIN, WARN, tag, msg);
    180    }
    181
    182    /**
    183     * Send a {@link #WARN} logmessage and log the exception.
    184     * @param tag Used toidentify the source of a log message.  Itusually identifies
    185     *        the class or activity where the log calloccurs.
    186     * @param msg The messageyou would like logged.
    187     * @param tr An exceptionto log
    188     */
    189    public static int w(Stringtag, String msg, Throwable tr) {
    190        return println_native(LOG_ID_MAIN,WARN, tag, msg + '
    ' + getStackTraceString(tr));
    191    }
    192
    193    /**
    194     * Checks to see whether ornot a log for the specified tag is loggable at the specified level.
    195     *
    196     *  The default level of any tag is set to INFO.This means that any level above and including
    197     *  INFO will be logged. Before you make anycalls to a logging method you should check to see
    198     *  if your tag should be logged. You can changethe default level by setting a system property:
    199     *      'setprop log.tag.<YOUR_LOG_TAG><LEVEL>'
    200     *  Where level is either VERBOSE, DEBUG, INFO,WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will
    201     *  turn off all logging for your tag. You canalso create a local.prop file that with the
    202     *  following in it:
    203     *     'log.tag.<YOUR_LOG_TAG>=<LEVEL>'
    204     *  and place that in /data/local.prop.
    205     *
    206     * @param tag The tag tocheck.
    207     * @param level The levelto check.
    208     * @return Whether or notthat this is allowed to be logged.
    209     * @throwsIllegalArgumentException is thrown if the tag.length() > 23.
    210     */
    211    public static nativeboolean isLoggable(String tag, int level);
    212
    213    /*
    214     * Send a {@link #WARN} logmessage and log the exception.
    215     * @param tag Used toidentify the source of a log message.  Itusually identifies
    216     *        the class or activity where the logcall occurs.
    217     * @param tr An exceptionto log
    218     */
    219    public static int w(Stringtag, Throwable tr) {
    220        returnprintln_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));
    221    }
    222
    223    /**
    224     * Send an {@link #ERROR}log message.
    225     * @param tag Used toidentify the source of a log message.  Itusually identifies
    226     *        the class or activity where the logcall occurs.
    227     * @param msg The messageyou would like logged.
    228     */
    229    public static int e(Stringtag, String msg) {
    230        returnprintln_native(LOG_ID_MAIN, ERROR, tag, msg);
    231    }
    232
    233    /**
    234     * Send a {@link #ERROR}log message and log the exception.
    235     * @param tag Used toidentify the source of a log message.  Itusually identifies
    236     *        the class or activity where the logcall occurs.
    237     * @param msg The messageyou would like logged.
    238     * @param tr An exceptionto log
    239     */
    240    public static int e(Stringtag, String msg, Throwable tr) {
    241        return println_native(LOG_ID_MAIN, ERROR,tag, msg + '
    ' + getStackTraceString(tr));
    242    }
    243
    244    /**
    245     * What a Terrible Failure:Report a condition that should never happen.
    246     * The error will always belogged at level ASSERT with the call stack.
    247     * Depending on systemconfiguration, a report may be added to the
    248     * {@linkandroid.os.DropBoxManager} and/or the process may be terminated
    249     * immediately with anerror dialog.
    250     * @param tag Used toidentify the source of a log message.
    251     * @param msg The messageyou would like logged.
    252     */
    253    public static intwtf(String tag, String msg) {
    254        return wtf(tag, msg,null);
    255    }
    256
    257    /**
    258     * What a Terrible Failure:Report an exception that should never happen.
    259     * Similar to {@link#wtf(String, String)}, with an exception to log.
    260     * @param tag Used toidentify the source of a log message.
    261     * @param tr An exceptionto log.
    262     */
    263    public static intwtf(String tag, Throwable tr) {
    264        return wtf(tag,tr.getMessage(), tr);
    265    }
    266
    267    /**
    268     * What a Terrible Failure:Report an exception that should never happen.
    269     * Similar to {@link #wtf(String,Throwable)}, with a message as well.
    270     * @param tag Used toidentify the source of a log message.
    271     * @param msg The messageyou would like logged.
    272     * @param tr An exceptionto log.  May be null.
    273     */
    274    public static intwtf(String tag, String msg, Throwable tr) {
    275        TerribleFailure what =new TerribleFailure(msg, tr);
    276        int bytes =println_native(LOG_ID_MAIN, ASSERT, tag, getStackTraceString(tr));
    277       sWtfHandler.onTerribleFailure(tag, what);
    278        return bytes;
    279    }
    280
    281    /**
    282     * Sets the terriblefailure handler, for testing.
    283     *
    284     * @return the old handler
    285     *
    286     * @hide
    287     */
    288    public staticTerribleFailureHandler setWtfHandler(TerribleFailureHandler handler) {
    289        if (handler == null) {
    290            throw newNullPointerException("handler == null");
    291        }
    292        TerribleFailureHandleroldHandler = sWtfHandler;
    293        sWtfHandler = handler;
    294        return oldHandler;
    295    }
    296
    297    /**
    298     * Handy function to get aloggable stack trace from a Throwable
    299     * @param tr An exceptionto log
    300     */
    301    public static StringgetStackTraceString(Throwable tr) {
    302        if (tr == null) {
    303            return"";
    304        }
    305        StringWriter sw = newStringWriter();
    306        PrintWriter pw = newPrintWriter(sw);
    307        tr.printStackTrace(pw);
    308        return sw.toString();
    309    }
    310
    311    /**
    312     * Low-level logging call.
    313     * @param priority Thepriority/type of this log message
    314     * @param tag Used toidentify the source of a log message.  Itusually identifies
    315     *        the class or activity where the log calloccurs.
    316     * @param msg The messageyou would like logged.
    317     * @return The number ofbytes written.
    318     */
    319    public static intprintln(int priority, String tag, String msg) {
    320        returnprintln_native(LOG_ID_MAIN, priority, tag, msg);
    321    }
    322
    323    /** @hide */ public staticfinal int LOG_ID_MAIN = 0;
    324    /** @hide */ public staticfinal int LOG_ID_RADIO = 1;
    325    /** @hide */ public staticfinal int LOG_ID_EVENTS = 2;
    326    /** @hide */ public staticfinal int LOG_ID_SYSTEM = 3;
    327
    328    /** @hide */ public staticnative int println_native(int bufID,
    329            int priority,String tag, String msg);
    330}


    57-82行,定义了2-7共6个LOG优先级。

    115-117行,定义了Log.v函数,可以看到,该函数是通过调用本地函数println_native来实现的。

    LOG类的其它函数很多都是类似的实现,我们不再详细分析,下面我们来看println_native函数是怎么实现的。该函数定义在frameworks/base/core/jni/android_util_Log.cpp文件中。

    142/*
    143 * JNI registration.
    144 */
    145static JNINativeMethod gMethods[] = {
    146    /* name, signature, funcPtr*/
    147    {"isLoggable",     "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable},
    148    {"println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*)android_util_Log_println_native },
    149};


    由这段代码可以看出,JAVA层调用的本地函数println_native,在这里是指向android_util_Log_println_native函数,该函数定义如下:

    99/*
    100 * In class android.util.Log:
    101 *  public static native intprintln_native(int buffer, int priority, String tag, String msg)
    102 */
    103static jint android_util_Log_println_native(JNIEnv* env, jobjectclazz,
    104        jint bufID, jintpriority, jstring tagObj, jstring msgObj)
    105{
    106    const char* tag = NULL;
    107    const char* msg = NULL;
    108
    109    if (msgObj == NULL) {
    110        jclass npeClazz;
    111
    112        npeClazz =env->FindClass("java/lang/NullPointerException");
    113        assert(npeClazz !=NULL);
    114
    115       env->ThrowNew(npeClazz, "println needs a message");
    116        return -1;
    117    }
    118
    119    if (bufID < 0 || bufID>= LOG_ID_MAX) {
    120        jclass npeClazz;
    121
    122        npeClazz =env->FindClass("java/lang/NullPointerException");
    123        assert(npeClazz !=NULL);
    124
    125       env->ThrowNew(npeClazz, "bad bufID");
    126        return -1;
    127    }
    128
    129    if (tagObj != NULL)
    130        tag =env->GetStringUTFChars(tagObj, NULL);
    131    msg =env->GetStringUTFChars(msgObj, NULL);
    132
    133    int res =__android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
    134
    135    if (tag != NULL)
    136       env->ReleaseStringUTFChars(tagObj, tag);
    137   env->ReleaseStringUTFChars(msgObj, msg);
    138
    139    return res;
    140}


    开始是进行一些参数的检查,133行,调用运行时库函数__android_log_buf_write执行写操作,该函数定义在system/core/liblog/logd_write.c文件中:

    162int __android_log_buf_write(int bufID, int prio, const char *tag,const char *msg)
    163{
    164    struct iovec vec[3];
    165
    166    if (!tag)
    167        tag = "";
    168
    169    /* XXX: This needs to go!*/
    170    if (!strcmp(tag,"HTC_RIL") ||
    171        !strncmp(tag,"RIL", 3) || /* Any log tag with "RIL" as the prefix */
    172        !strcmp(tag,"AT") ||
    173        !strcmp(tag,"GSM") ||
    174        !strcmp(tag,"STK") ||
    175        !strcmp(tag,"CDMA") ||
    176        !strcmp(tag,"PHONE") ||
    177        !strcmp(tag,"SMS"))
    178            bufID =LOG_ID_RADIO;
    179
    180    vec[0].iov_base   = (unsigned char *) &prio;
    181    vec[0].iov_len    = 1;
    182    vec[1].iov_base   = (void *) tag;
    183    vec[1].iov_len    = strlen(tag) + 1;
    184    vec[2].iov_base   = (void *) msg;
    185    vec[2].iov_len    = strlen(msg) + 1;
    186
    187    return write_to_log(bufID,vec, 3);
    188}


    170-178行,如果出现“HTC_RIL”等字符,将bufID设置为LOG_ID_RADIO。

    180-185行,将prio,tag,msg保存在数组vec中。

    187行,调用write_to_log函数,该函数定义如下:

    45static int __write_to_log_init(log_id_t, struct iovec *vec, size_tnr);
    46static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) =__write_to_log_init;


    __write_to_log_init函数定义如下:

     96static int__write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
     97{
     98#ifdef HAVE_PTHREADS
     99    pthread_mutex_lock(&log_init_lock);
    100#endif
    101
    102    if (write_to_log ==__write_to_log_init) {
    103        log_fds[LOG_ID_MAIN] =log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
    104        log_fds[LOG_ID_RADIO] =log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
    105        log_fds[LOG_ID_EVENTS]= log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
    106        log_fds[LOG_ID_SYSTEM]= log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);
    107
    108        write_to_log =__write_to_log_kernel;
    109
    110        if(log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
    111               log_fds[LOG_ID_EVENTS] < 0) {
    112           log_close(log_fds[LOG_ID_MAIN]);
    113           log_close(log_fds[LOG_ID_RADIO]);
    114           log_close(log_fds[LOG_ID_EVENTS]);
    115           log_fds[LOG_ID_MAIN] = -1;
    116           log_fds[LOG_ID_RADIO] = -1;
    117           log_fds[LOG_ID_EVENTS] = -1;
    118            write_to_log =__write_to_log_null;
    119        }
    120
    121        if(log_fds[LOG_ID_SYSTEM] < 0) {
    122           log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
    123        }
    124    }
    125
    126#ifdef HAVE_PTHREADS
    127   pthread_mutex_unlock(&log_init_lock);
    128#endif
    129
    130    return write_to_log(log_id,vec, nr);
    131}


    如果write_to_log等于__write_to_log_init,即第一次调用write_to_log,则调用log_open打开4个LOG设备,并将write_to_log设置为__write_to_log_kernel,所以130行再调用write_to_log,即是调用__write_to_log_kernel函数。

     78static int__write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
     79{
     80    ssize_t ret;
     81    int log_fd;
     82
     83    if (/*(int)log_id >= 0 &&*/(int)log_id < (int)LOG_ID_MAX) {
     84        log_fd = log_fds[(int)log_id];
     85    } else {
     86        return EBADF;
     87    }
     88
     89    do {
     90        ret = log_writev(log_fd, vec, nr);
     91    } while (ret < 0 && errno ==EINTR);
     92
     93    return ret;
     94}


    核心函数是第90行调用的log_writev,该函数实现了写入操作。

     40#definelog_open(pathname, flags) open(pathname, flags)
     41#define log_writev(filedes,vector, count) writev(filedes, vector, count)
     42#define log_close(filedes)close(filedes)

    log_writev是一个宏,对应writev函数,定义在system/core/libcutils/uio.c文件中:

    49int  writev( int  fd, const struct iovec*  vecs, int count )
    50{
    51    int   total = 0;
    52
    53    for ( ; count > 0;count--, vecs++ ) {
    54        const char*  buf = (const char*)vecs->iov_base;
    55        int          len = (int)vecs->iov_len;
    56
    57        while (len > 0) {
    58            int  ret = write( fd, buf, len );
    59            if (ret < 0) {
    60                if (total == 0)
    61                    total = -1;
    62                goto Exit;
    63            }
    64            if (ret == 0)
    65                goto Exit;
    66
    67            total += ret;
    68            buf   += ret;
    69            len   -= ret;
    70        }
    71    }
    72Exit:
    73    return total;
    74}


    该函数完成将字符串数组成员依次写到指定的设备中。

    分析到这里,我们就清楚了应用程序怎样把LOG信息写到LOG设备中了。

  • 相关阅读:
    vsftpd被动模式配置
    CentOS 6.4 服务器版安装教程(超级详细图解)
    CentOS 5.4 安装和卸载桌面
    CentOS 删除桌面环境
    centos x64 vsftpd 530登陆错误问题
    [CentOS] CentOS for vsftpd with MySQL Virtual user
    vsftp 无法启动,500 OOPS: bad bool value in config file for: anonymous_enable
    第二章 创建对话框
    error: no matching function for call to 'Ui::GoToCellDialog::setupUi(QDialog*&)' ui.setupUi(dialog); ^
    第一章 Qt入门
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3170126.html
Copyright © 2020-2023  润新知