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设备中了。

原文地址:https://www.cnblogs.com/dyllove98/p/3170126.html