作者:刘昊昱

博客: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;591592    ret =init_log(&log_main);593    if (unlikely(ret))594        goto out;595596    ret =init_log(&log_events);597    if (unlikely(ret))598        goto out;599600    ret =init_log(&log_radio);601    if (unlikely(ret))602        goto out;603604out: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;574575    ret =misc_register(&log->misc);576    if (unlikely(ret)) {577        printk(KERN_ERR"logger: failed to register misc "578               "device forlog '%s'!\n", log->misc.name);579        return ret;580    }581582    printk(KERN_INFO"logger: created %luK log '%s'\n",583           (unsigned long)log->size >> 10, log->misc.name);584585    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, which535 * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, andless than536 * 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};555556DEFINE_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 operation386 *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;393394    ret =nonseekable_open(inode, file);395    if (ret)396        return ret;397398    log =get_log_from_minor(MINOR(inode->i_rdev));399    if (!log)400        return -ENODEV;401402    if (file->f_mode &FMODE_READ) {403        struct logger_reader*reader;404405        reader =kmalloc(sizeof(struct logger_reader), GFP_KERNEL);406        if (!reader)407            return -ENOMEM;408409        reader->log = log;410       INIT_LIST_HEAD(&reader->list);411412       mutex_lock(&log->mutex);413        reader->r_off =log->head;414       list_add_tail(&reader->list, &log->readers);415       mutex_unlock(&log->mutex);416417        file->private_data =reader;418    } else419        file->private_data =log;420421    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() method145 *146 * Behavior:147 *148 *  - O_NONBLOCK works149 *  - If there are no logentries to read, blocks until log is written to150 *  - Atomically reads exactlyone log entry151 *152 * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno toEINVAL if read153 * 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);162163start:164    while (1) {165       prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);166167        mutex_lock(&log->mutex);168        ret = (log->w_off ==reader->r_off);169       mutex_unlock(&log->mutex);170        if (!ret)171            break;172173        if (file->f_flags& O_NONBLOCK) {174            ret = -EAGAIN;175            break;176        }177178        if(signal_pending(current)) {179            ret = -EINTR;180            break;181        }182183        schedule();184    }185186   finish_wait(&log->wq, &wait);187    if (ret)188        return ret;189190   mutex_lock(&log->mutex);191192    /* 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    }197198    /* 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    }204205    /* get exactly one entryfrom the log */206    ret =do_read_log_to_user(log, reader, buf, ret);207208out:209   mutex_unlock(&log->mutex);210211    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    }104105    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' intothe110 * 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;120121    /*122     * We read from the log intwo disjoint operations. First, we read from123     * the current read headoffset up to 'count' bytes or to the end of124     * 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;129130    /*131     * Second, we read anyremaining bytes, starting back at the head of132     * the log.133     */134    if (count != len)135        if (copy_to_user(buf +len, log->buffer, count - len))136            return -EFAULT;137138    reader->r_off =logger_offset(reader->r_off + count);139140    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 tooptimize320 * 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;330331    now =current_kernel_time();332333    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);338339    /* null writes succeed,return zero */340    if (unlikely(!header.len))341        return 0;342343   mutex_lock(&log->mutex);344345    /*346     * Fix up any readers,pulling them forward to the first readable347     * entry after (what willbe) the new write offset. We do this now348     * because if we partiallyfail, we can end up with clobbered log349     * entries that encroach onreadable buffer.350     */351    fix_up_readers(log,sizeof(struct logger_entry) + header.len);352353    do_write_log(log,&header, sizeof(struct logger_entry));354355    while (nr_segs-- > 0) {356        size_t len;357        ssize_t nr;358359        /* figure out how muchof this vector we can keep */360        len = min_t(size_t,iov->iov_len, header.len - ret);361362        /* 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        }369370        iov++;371        ret += nr;372    }373374   mutex_unlock(&log->mutex);375376    /* wake up any blockedreaders */377   wake_up_interruptible(&log->wq);378379    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 were252 * 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 first254 * 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;263264    if (clock_interval(old,new, log->head))265        log->head =get_next_entry(log, log->head, len);266267    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 line235 * 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    }246247    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;223224    do {225        size_t nr =get_entry_len(log, off);  // 取得一下条记录的长度226        off = logger_offset(off+ nr);     // off指向一条记录227        count += nr;228    } while (count < len);229230    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;280281    len = min(count,log->size - log->w_off); // 处理后半部分282    memcpy(log->buffer +log->w_off, buf, len);283284    if (count != len) // 如果有需要,处理前半部分285        memcpy(log->buffer,buf + len, count - len);286287    log->w_off =logger_offset(log->w_off + count);288289}


回到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' to293 * 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;303304    len = min(count,log->size - log->w_off);305    if (len &©_from_user(log->buffer + log->w_off, buf, len))306        return -EFAULT;307308    if (count != len)309        if(copy_from_user(log->buffer, buf + len, count - len))310            return -EFAULT;311312    log->w_off =logger_offset(log->w_off + count);313314    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    } 99100    private staticTerribleFailureHandler sWtfHandler = new TerribleFailureHandler() {101            public voidonTerribleFailure(String tag, TerribleFailure what) {102               RuntimeInit.wtf(tag, what);103            }104        };105106    private Log() {107    }108109    /**110     * Send a {@link #VERBOSE}log message.111     * @param tag Used toidentify the source of a log message.  Itusually identifies112     *        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    }118119    /**120     * Send a {@link #VERBOSE}log message and log the exception.121     * @param tag Used toidentify the source of a log message.  Itusually identifies122     *        the class or activity where the logcall occurs.123     * @param msg The messageyou would like logged.124     * @param tr An exceptionto log125     */126    public static int v(Stringtag, String msg, Throwable tr) {127        returnprintln_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr));128    }129130    /**131     * Send a {@link #DEBUG}log message.132     * @param tag Used toidentify the source of a log message.  Itusually identifies133     *        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    }139140    /**141     * Send a {@link #DEBUG}log message and log the exception.142     * @param tag Used toidentify the source of a log message.  Itusually identifies143     *        the class or activity where the logcall occurs.144     * @param msg The messageyou would like logged.145     * @param tr An exceptionto log146     */147    public static int d(Stringtag, String msg, Throwable tr) {148        returnprintln_native(LOG_ID_MAIN, DEBUG, tag, msg + '\n' + getStackTraceString(tr));149    }150151    /**152     * Send an {@link #INFO}log message.153     * @param tag Used toidentify the source of a log message.  Itusually identifies154     *        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    }160161    /**162     * Send a {@link #INFO} logmessage and log the exception.163     * @param tag Used toidentify the source of a log message.  Itusually identifies164     *        the class or activity where the logcall occurs.165     * @param msg The messageyou would like logged.166     * @param tr An exceptionto log167     */168    public static int i(Stringtag, String msg, Throwable tr) {169        returnprintln_native(LOG_ID_MAIN, INFO, tag, msg + '\n' + getStackTraceString(tr));170    }171172    /**173     * Send a {@link #WARN} logmessage.174     * @param tag Used toidentify the source of a log message.  Itusually identifies175     *        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    }181182    /**183     * Send a {@link #WARN} logmessage and log the exception.184     * @param tag Used toidentify the source of a log message.  Itusually identifies185     *        the class or activity where the log calloccurs.186     * @param msg The messageyou would like logged.187     * @param tr An exceptionto log188     */189    public static int w(Stringtag, String msg, Throwable tr) {190        return println_native(LOG_ID_MAIN,WARN, tag, msg + '\n' + getStackTraceString(tr));191    }192193    /**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 including197     *  INFO will be logged. Before you make anycalls to a logging method you should check to see198     *  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 will201     *  turn off all logging for your tag. You canalso create a local.prop file that with the202     *  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);212213    /*214     * Send a {@link #WARN} logmessage and log the exception.215     * @param tag Used toidentify the source of a log message.  Itusually identifies216     *        the class or activity where the logcall occurs.217     * @param tr An exceptionto log218     */219    public static int w(Stringtag, Throwable tr) {220        returnprintln_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));221    }222223    /**224     * Send an {@link #ERROR}log message.225     * @param tag Used toidentify the source of a log message.  Itusually identifies226     *        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    }232233    /**234     * Send a {@link #ERROR}log message and log the exception.235     * @param tag Used toidentify the source of a log message.  Itusually identifies236     *        the class or activity where the logcall occurs.237     * @param msg The messageyou would like logged.238     * @param tr An exceptionto log239     */240    public static int e(Stringtag, String msg, Throwable tr) {241        return println_native(LOG_ID_MAIN, ERROR,tag, msg + '\n' + getStackTraceString(tr));242    }243244    /**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 the248     * {@linkandroid.os.DropBoxManager} and/or the process may be terminated249     * 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    }256257    /**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    }266267    /**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    }280281    /**282     * Sets the terriblefailure handler, for testing.283     *284     * @return the old handler285     *286     * @hide287     */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    }296297    /**298     * Handy function to get aloggable stack trace from a Throwable299     * @param tr An exceptionto log300     */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    }310311    /**312     * Low-level logging call.313     * @param priority Thepriority/type of this log message314     * @param tag Used toidentify the source of a log message.  Itusually identifies315     *        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    }322323    /** @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;327328    /** @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;108109    if (msgObj == NULL) {110        jclass npeClazz;111112        npeClazz =env->FindClass("java/lang/NullPointerException");113        assert(npeClazz !=NULL);114115       env->ThrowNew(npeClazz, "println needs a message");116        return -1;117    }118119    if (bufID < 0 || bufID>= LOG_ID_MAX) {120        jclass npeClazz;121122        npeClazz =env->FindClass("java/lang/NullPointerException");123        assert(npeClazz !=NULL);124125       env->ThrowNew(npeClazz, "bad bufID");126        return -1;127    }128129    if (tagObj != NULL)130        tag =env->GetStringUTFChars(tagObj, NULL);131    msg =env->GetStringUTFChars(msgObj, NULL);132133    int res =__android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);134135    if (tag != NULL)136       env->ReleaseStringUTFChars(tagObj, tag);137   env->ReleaseStringUTFChars(msgObj, msg);138139    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];165166    if (!tag)167        tag = "";168169    /* 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;179180    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;186187    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#endif101102    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);107108        write_to_log =__write_to_log_kernel;109110        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        }120121        if(log_fds[LOG_ID_SYSTEM] < 0) {122           log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];123        }124    }125126#ifdef HAVE_PTHREADS127   pthread_mutex_unlock(&log_init_lock);128#endif129130    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;5253    for ( ; count > 0;count--, vecs++ ) {54        const char*  buf = (const char*)vecs->iov_base;55        int          len = (int)vecs->iov_len;5657        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;6667            total += ret;68            buf   += ret;69            len   -= ret;70        }71    }72Exit:73    return total;74}


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

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

更多相关文章

  1. C语言函数以及函数的使用
  2. Android 在 xml中定义图片
  3. Android 如何在XML文件中定义动画
  4. Android去除标题栏及自定义title栏
  5. Android 自定义View(手写签名)
  6. Android Camera源码函数结构
  7. android UDID获取:android 设备SN的获取 续 android 设备唯一码的
  8. android中自定义播放器的实现

随机推荐

  1. 移动端布局学习小结与实践
  2. 移动端布局学习小结与实践
  3. 移动端布局学习小结与实践
  4. 移动布局原理、实战手机页面的基本整体架
  5. 仿PHP中文网首页
  6. Android(安卓)Studio decide the exact v
  7. 调用android自带的截图工具进行截图
  8. Android的TCP Socket连接组件
  9. android 中如何解析Rss订阅的xml文件
  10. android新手学习帮助网址