在程序开发过程中,LOG是广泛使用的用来记录程序执行过程的机制,它既可以用于程序调试,也可以用于产品运营中的事件记录。在Android系统中,提供了简单、便利的LOG机制,开发人员可以方便地使用。在这一篇文章中,我们简单介绍在Android内核空间和用户空间中LOG的使用和查看方法


  一. 内核开发时LOG的使用。Android内核是基于Linux Kerne 2.36的,因此,Linux Kernel的LOG机制同样适合于Android内核,它就是有名的printk,与C语言的printf齐名。与printf类似,printk提供格式化输入功能,同时,它也具有所有LOG机制的特点--提供日志级别过虑功能。printk提供了8种日志级别(<linux/kernel.h>):




1. #define KERN_EMERG  "<0>"     /* system is unusable           */
2. #define KERN_ALERT  "<1>"     /* action must be taken immediately */
3. #define KERN_CRIT   "<2>"     /* critical conditions          */
4. #deinfe KERN_ERR    "<3>"     /* error conditions         */
5. #deinfe KERN_WARNING    "<4>"     /* warning conditions           */
6. #deinfe KERN_NOTICE "<5>"     /* normal but significant condition */
7. #deinfe KERN_INFO   "<6>"     /* informational            */
8. #deinfe KERN_DEBUG  "<7>"     /* debug-level messages         */

在Android系统中,printk输出的日志信息保存在/proc/kmsg中,要查看/proc/kmsg的内容


在 Android 源码中  /frameworks/base/core/java/android/util/Log.java 定义了log 接口


public static int i(String tag, String msg) {
         return println_native(LOG_ID_MAIN, INFO, tag, msg);
     }

     /**
      * Send a {@link #INFO} log message and log the exception.
      * @param tag Used to identify the source of a log message.  It usually identifies
      *        the class or activity where the log call occurs.
      * @param msg The message you would like logged.
      * @param tr An exception to log
      */
     public static int i(String tag, String msg, Throwable tr) {
         return println_native(LOG_ID_MAIN, INFO, tag, msg + '\n' + getStackTraceString(tr));
     }

     /**
      * Send a {@link #WARN} log message.
      * @param tag Used to identify the source of a log message.  It usually identifies
      *        the class or activity where the log call occurs.
      * @param msg The message you would like logged.
      */
     public static int w(String tag, String msg) {
         return println_native(LOG_ID_MAIN, WARN, tag, msg);
     }

     /**
      * Send a {@link #WARN} log message and log the exception.
      * @param tag Used to identify the source of a log message.  It usually identifies
      *        the class or activity where the log call occurs.
      * @param msg The message you would like logged.
      * @param tr An exception to log
      */
     public static int w(String tag, String msg, Throwable tr) {
         return println_native(LOG_ID_MAIN, WARN, tag, msg + '\n' + getStackTraceString(tr));
     }

     /**
      * Checks to see whether or not a log for the specified tag is loggable at the specified level.
      *
      *  The default level of any tag is set to INFO. This means that any level above and including
      *  INFO will be logged. Before you make any calls to a logging method you should check to see
      *  if your tag should be logged. You can change the default level by setting a system property:
      *      'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>'
      *  Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will
      *  turn off all logging for your tag. You can also create a local.prop file that with the
      *  following in it:
      *      'log.tag.<YOUR_LOG_TAG>=<LEVEL>'
      *  and place that in /data/local.prop.
      *
      * @param tag The tag to check.
      * @param level The level to check.
      * @return Whether or not that this is allowed to be logged.
      * @throws IllegalArgumentException is thrown if the tag.length() > 23.
      */
     public static native boolean isLoggable(String tag, int level);

     /*
      * Send a {@link #WARN} log message and log the exception.
      * @param tag Used to identify the source of a log message.  It usually identifies
      *        the class or activity where the log call occurs.
      * @param tr An exception to log
      */
     public static int w(String tag, Throwable tr) {
         return println_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));
     }

     /**
      * Send an {@link #ERROR} log message.
      * @param tag Used to identify the source of a log message.  It usually identifies
      *        the class or activity where the log call occurs.
      * @param msg The message you would like logged.
      */
     public static int e(String tag, String msg) {
         return println_native(LOG_ID_MAIN, ERROR, tag, msg);
     }

     /**
      * Send a {@link #ERROR} log message and log the exception.
      * @param tag Used to identify the source of a log message.  It usually identifies
      *        the class or activity where the log call occurs.
      * @param msg The message you would like logged.
      * @param tr An exception to log
      */
     public static int e(String tag, String msg, Throwable tr) {
         return println_native(LOG_ID_MAIN, ERROR, tag, msg + '\n' + getStackTraceString(tr));
     }


一、内核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); 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'!/n", log->misc.name); 
 579        return ret; 
 580    } 
 581 
 582    printk(KERN_INFO"logger: created %luK log '%s'/n", 
 583           (unsigned long)log->size >> 10, log->misc.name); 
 584 
 585    return 0; 
 586} 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'!/n", log->misc.name);
 579        return ret;
 580    }
 581
 582    printk(KERN_INFO"logger: created %luK log '%s'/n",
 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};  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) 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 */ 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}; 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 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} 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};  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} 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} 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}; 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信息读到用户空间,定义如下:

08/*
 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} 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))  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} 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}  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}; 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} 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} 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函数定义如下:

14/*
 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} 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} 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} 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 + '/n' + 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 + '/n' + 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 + '/n' + 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 + '/n' + 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 + '/n' + 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}
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 + '/n' + 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 + '/n' + 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 + '/n' + 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 + '/n' + 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 + '/n' + 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}; 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} 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} 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; 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}  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}  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)  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} 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设备中了。