Android架构分析之LOG模块[转]

Android架构分析之LOG模块

标签: ANDROID架构
2424人阅读 评论(1) 收藏 举报
分类:

作者:刘昊昱

博客: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内核驱动程序的惯例,我们从模块初始化函数开始分析:

  1. 588static int __init logger_init(void)
  2. 589{
  3. 590    int ret;
  4. 591
  5. 592    ret =init_log(&log_main);
  6. 593    if (unlikely(ret))
  7. 594        goto out;
  8. 595
  9. 596    ret =init_log(&log_events);
  10. 597    if (unlikely(ret))
  11. 598        goto out;
  12. 599
  13. 600    ret =init_log(&log_radio);
  14. 601    if (unlikely(ret))
  15. 602        goto out;
  16. 603
  17. 604out:
  18. 605    return ret;
  19. 606}
  20. 607device_initcall(logger_init);

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

  1. 571static int __init init_log(struct logger_log *log)
  2. 572{
  3. 573    int ret;
  4. 574
  5. 575    ret =misc_register(&log->misc);
  6. 576    if (unlikely(ret)) {
  7. 577        printk(KERN_ERR“logger: failed to register misc “
  8. 578               “device forlog ‘%s’!\n”, log->misc.name);
  9. 579        return ret;
  10. 580    }
  11. 581
  12. 582    printk(KERN_INFO“logger: created %luK log ‘%s’\n”,
  13. 583           (unsigned long)log->size >> 10, log->misc.name);
  14. 584
  15. 585    return 0;
  16. 586}

575行,调用misc_register函数,注册misc设备。init_log函数的参数是logger_log结构体类型,该类型代表一个LOG设备,其定义如下:

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

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

  1. 533/*
  2. 534 * Defines a log structure with name ‘NAME’ and a size of ‘SIZE’bytes, which
  3. 535 * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, andless than
  4. 536 * LONG_MAX minus LOGGER_ENTRY_MAX_LEN.
  5. 537 */
  6. 538#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \
  7. 539static unsigned char _buf_ ## VAR[SIZE]; \
  8. 540static struct logger_log VAR = { \
  9. 541    .buffer = _buf_ ## VAR, \
  10. 542    .misc = { \
  11. 543        .minor =MISC_DYNAMIC_MINOR, \
  12. 544        .name = NAME, \
  13. 545        .fops =&logger_fops, \
  14. 546        .parent = NULL, \
  15. 547    }, \
  16. 548    .wq =__WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \
  17. 549    .readers = LIST_HEAD_INIT(VAR.readers), \
  18. 550    .mutex =__MUTEX_INITIALIZER(VAR .mutex), \
  19. 551    .w_off = 0, \
  20. 552    .head = 0, \
  21. 553    .size = SIZE, \
  22. 554};
  23. 555
  24. 556DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)
  25. 557DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024)
  26. 558DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024)

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

  1. 33#define LOGGER_LOG_RADIO   “log_radio” /* radio-related messages */
  2. 34#define LOGGER_LOG_EVENTS  “log_events”    /*system/hardware events */
  3. 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,其定义如下:

  1. 522static struct file_operations logger_fops = {
  2. 523    .owner = THIS_MODULE,
  3. 524    .read = logger_read,
  4. 525    .aio_write =logger_aio_write,
  5. 526    .poll = logger_poll,
  6. 527    .unlocked_ioctl =logger_ioctl,
  7. 528    .compat_ioctl =logger_ioctl,
  8. 529    .open = logger_open,
  9. 530    .release = logger_release,
  10. 531};

我们先来看open函数:

  1. 384/*
  2. 385 * logger_open – the log’s open() file operation
  3. 386 *
  4. 387 * Note how near a no-op this is in the write-only case. Keep it thatway!
  5. 388 */
  6. 389static int logger_open(struct inode *inode, struct file *file)
  7. 390{
  8. 391    struct logger_log *log;
  9. 392    int ret;
  10. 393
  11. 394    ret =nonseekable_open(inode, file);
  12. 395    if (ret)
  13. 396        return ret;
  14. 397
  15. 398    log =get_log_from_minor(MINOR(inode->i_rdev));
  16. 399    if (!log)
  17. 400        return -ENODEV;
  18. 401
  19. 402    if (file->f_mode &FMODE_READ) {
  20. 403        struct logger_reader*reader;
  21. 404
  22. 405        reader =kmalloc(sizeof(struct logger_reader), GFP_KERNEL);
  23. 406        if (!reader)
  24. 407            return -ENOMEM;
  25. 408
  26. 409        reader->log = log;
  27. 410       INIT_LIST_HEAD(&reader->list);
  28. 411
  29. 412       mutex_lock(&log->mutex);
  30. 413        reader->r_off =log->head;
  31. 414       list_add_tail(&reader->list, &log->readers);
  32. 415       mutex_unlock(&log->mutex);
  33. 416
  34. 417        file->private_data =reader;
  35. 418    } else
  36. 419        file->private_data =log;
  37. 420
  38. 421    return 0;
  39. 422}
  40. 423

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

  1. 560static struct logger_log * get_log_from_minor(int minor)
  2. 561{
  3. 562    if (log_main.misc.minor ==minor)
  4. 563        return &log_main;
  5. 564    if (log_events.misc.minor== minor)
  6. 565        return &log_events;
  7. 566    if (log_radio.misc.minor ==minor)
  8. 567        return &log_radio;
  9. 568    return NULL;
  10. 569}

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

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

下面我们来看read函数:

  1. 143/*
  2. 144 * logger_read – our log’s read() method
  3. 145 *
  4. 146 * Behavior:
  5. 147 *
  6. 148 *  – O_NONBLOCK works
  7. 149 *  – If there are no logentries to read, blocks until log is written to
  8. 150 *  – Atomically reads exactlyone log entry
  9. 151 *
  10. 152 * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno toEINVAL if read
  11. 153 * buffer is insufficient to hold next entry.
  12. 154 */
  13. 155static ssize_t logger_read(struct file *file, char __user *buf,
  14. 156               size_t count,loff_t *pos)
  15. 157{
  16. 158    struct logger_reader*reader = file->private_data;
  17. 159    struct logger_log *log =reader->log;
  18. 160    ssize_t ret;
  19. 161    DEFINE_WAIT(wait);
  20. 162
  21. 163start:
  22. 164    while (1) {
  23. 165       prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);
  24. 166
  25. 167        mutex_lock(&log->mutex);
  26. 168        ret = (log->w_off ==reader->r_off);
  27. 169       mutex_unlock(&log->mutex);
  28. 170        if (!ret)
  29. 171            break;
  30. 172
  31. 173        if (file->f_flags& O_NONBLOCK) {
  32. 174            ret = -EAGAIN;
  33. 175            break;
  34. 176        }
  35. 177
  36. 178        if(signal_pending(current)) {
  37. 179            ret = -EINTR;
  38. 180            break;
  39. 181        }
  40. 182
  41. 183        schedule();
  42. 184    }
  43. 185
  44. 186   finish_wait(&log->wq, &wait);
  45. 187    if (ret)
  46. 188        return ret;
  47. 189
  48. 190   mutex_lock(&log->mutex);
  49. 191
  50. 192    /* is there still somethingto read or did we race? */
  51. 193    if (unlikely(log->w_off== reader->r_off)) {
  52. 194       mutex_unlock(&log->mutex);
  53. 195        goto start;
  54. 196    }
  55. 197
  56. 198    /* get the size of the nextentry */
  57. 199    ret = get_entry_len(log,reader->r_off);
  58. 200    if (count < ret) {
  59. 201        ret = -EINVAL;
  60. 202        goto out;
  61. 203    }
  62. 204
  63. 205    /* get exactly one entryfrom the log */
  64. 206    ret =do_read_log_to_user(log, reader, buf, ret);
  65. 207
  66. 208out:
  67. 209   mutex_unlock(&log->mutex);
  68. 210
  69. 211    return ret;
  70. 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的长度),该函数定义如下:

  1. 86/*
  2.  87 * get_entry_len – Grabs thelength of the payload of the next entry starting
  3.  88 * from ‘off’.
  4.  89 *
  5.  90 * Caller needs to holdlog->mutex.
  6.  91 */
  7.  92static __u32get_entry_len(struct logger_log *log, size_t off)
  8.  93{
  9.  94    __u16 val;
  10.  95
  11.  96    switch (log->size – off) {
  12.  97    case 1:
  13.  98        memcpy(&val, log->buffer + off,1);
  14.  99        memcpy(((char *) &val) + 1,log->buffer, 1);
  15. 100        break;
  16. 101    default:
  17. 102        memcpy(&val,log->buffer + off, 2);
  18. 103    }
  19. 104
  20. 105    return sizeof(structlogger_entry) + val;
  21. 106}

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

  1. 23struct logger_entry {
  2. 24    __u16       len;   /* length of the payload */
  3. 25    __u16       __pad; /* no matter what, we get 2 bytes of padding */
  4. 26    __s32       pid;   /* generating process’s pid */
  5. 27    __s32       tid;   /* generating process’s tid */
  6. 28    __s32       sec;   /* seconds since Epoch */
  7. 29    __s32       nsec;  /* nanoseconds */
  8. 30    char        msg[0]; /* the entry’s payload */
  9. 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信息读到用户空间,定义如下:

  1. 108/*
  2. 109 * do_read_log_to_user – reads exactly ‘count’ bytes from ‘log’ intothe
  3. 110 * user-space buffer ‘buf’. Returns ‘count’ on success.
  4. 111 *
  5. 112 * Caller must hold log->mutex.
  6. 113 */
  7. 114static ssize_t do_read_log_to_user(struct logger_log *log,
  8. 115                   structlogger_reader *reader,
  9. 116                   char __user*buf,
  10. 117                   size_tcount)
  11. 118{
  12. 119    size_t len;
  13. 120
  14. 121    /*
  15. 122     * We read from the log intwo disjoint operations. First, we read from
  16. 123     * the current read headoffset up to ‘count’ bytes or to the end of
  17. 124     * the log, whichever comesfirst.
  18. 125     */
  19. 126    len = min(count, log->size- reader->r_off);
  20. 127    if (copy_to_user(buf,log->buffer + reader->r_off, len))
  21. 128        return -EFAULT;
  22. 129
  23. 130    /*
  24. 131     * Second, we read anyremaining bytes, starting back at the head of
  25. 132     * the log.
  26. 133     */
  27. 134    if (count != len)
  28. 135        if (copy_to_user(buf +len, log->buffer, count – len))
  29. 136            return -EFAULT;
  30. 137
  31. 138    reader->r_off =logger_offset(reader->r_off + count);
  32. 139
  33. 140    return count;
  34. 141}

因为LOG保存在循环缓冲区中,所以do_read_log_to_user函数考虑到这种情况,分两步通过copy_to_user函数拷贝LOG到用户空间。最后注意138行,通过logger_offset宏设置下一次的读取位置。该宏定义如下:

  1. 60/* logger_offset- returns index ‘n’ into the log via (optimized) modulus */
  2. 61#define logger_offset(n)    ((n) & (log->size – 1))

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

  1. 317/*
  2. 318 * logger_aio_write – our write method, implementing support forwrite(),
  3. 319 * writev(), and aio_write(). Writes are our fast path, and we try tooptimize
  4. 320 * them above all else.
  5. 321 */
  6. 322ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,
  7. 323             unsigned longnr_segs, loff_t ppos)
  8. 324{
  9. 325    struct logger_log *log =file_get_log(iocb->ki_filp);
  10. 326    size_t orig =log->w_off;
  11. 327    struct logger_entry header;
  12. 328    struct timespec now;
  13. 329    ssize_t ret = 0;
  14. 330
  15. 331    now =current_kernel_time();
  16. 332
  17. 333    header.pid =current->tgid;
  18. 334    header.tid =current->pid;
  19. 335    header.sec = now.tv_sec;
  20. 336    header.nsec = now.tv_nsec;
  21. 337    header.len = min_t(size_t,iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);
  22. 338
  23. 339    /* null writes succeed,return zero */
  24. 340    if (unlikely(!header.len))
  25. 341        return 0;
  26. 342
  27. 343   mutex_lock(&log->mutex);
  28. 344
  29. 345    /*
  30. 346     * Fix up any readers,pulling them forward to the first readable
  31. 347     * entry after (what willbe) the new write offset. We do this now
  32. 348     * because if we partiallyfail, we can end up with clobbered log
  33. 349     * entries that encroach onreadable buffer.
  34. 350     */
  35. 351    fix_up_readers(log,sizeof(struct logger_entry) + header.len);
  36. 352
  37. 353    do_write_log(log,&header, sizeof(struct logger_entry));
  38. 354
  39. 355    while (nr_segs– > 0) {
  40. 356        size_t len;
  41. 357        ssize_t nr;
  42. 358
  43. 359        /* figure out how muchof this vector we can keep */
  44. 360        len = min_t(size_t,iov->iov_len, header.len – ret);
  45. 361
  46. 362        /* write out thissegment’s payload */
  47. 363        nr =do_write_log_from_user(log, iov->iov_base, len);
  48. 364        if (unlikely(nr <0)) {
  49. 365            log->w_off = orig;
  50. 366           mutex_unlock(&log->mutex);
  51. 367            return nr;
  52. 368        }
  53. 369
  54. 370        iov++;
  55. 371        ret += nr;
  56. 372    }
  57. 373
  58. 374   mutex_unlock(&log->mutex);
  59. 375
  60. 376    /* wake up any blockedreaders */
  61. 377   wake_up_interruptible(&log->wq);
  62. 378
  63. 379    return ret;
  64. 380}

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

  1. 77static inlinestruct logger_log * file_get_log(struct file *file)
  2. 78{
  3. 79    if (file->f_mode & FMODE_READ) {
  4. 80        struct logger_reader *reader =file->private_data;
  5. 81        return reader->log;
  6. 82    } else
  7. 83        return file->private_data;
  8. 84}

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

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

333-337行,对logger_entry结构体变量header进行初始化。351行,调用fix_up_readers函数,修正某些logger_read的读取位置指针。因为LOG缓冲区是循环使用的,当进行写操作后,可能会覆盖一些末读取的内容,因此,需要修正某些logger_read的读取位置指针。

  1. 250/*
  2. 251 * fix_up_readers – walk the list of all readers and “fixup” any who were
  3. 252 * lapped by the writer; also do the same for the default “starthead”.
  4. 253 * We do this by “pulling forward” the readers and starthead to the first
  5. 254 * entry after the new write head.
  6. 255 *
  7. 256 * The caller needs to hold log->mutex.
  8. 257 */
  9. 258static void fix_up_readers(struct logger_log *log, size_t len)
  10. 259{
  11. 260    size_t old = log->w_off;
  12. 261    size_t new =logger_offset(old + len);
  13. 262    struct logger_reader*reader;
  14. 263
  15. 264    if (clock_interval(old,new, log->head))
  16. 265        log->head =get_next_entry(log, log->head, len);
  17. 266
  18. 267    list_for_each_entry(reader,&log->readers, list)
  19. 268        if (clock_interval(old,new, reader->r_off))
  20. 269            reader->r_off =get_next_entry(log, reader->r_off, len);
  21. 270}

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

  1. 233/*
  2. 234 * clock_interval – is a < c < b in mod-space? Put another way,does the line
  3. 235 * from a to b cross c?
  4. 236 */
  5. 237static inline int clock_interval(size_t a, size_t b, size_t c)
  6. 238{
  7. 239    if (b < a) {      // 转到循环缓冲区前面
  8. 240        if (a < c || b >=c)
  9. 241            return 1;
  10. 242    } else {
  11. 243        if (a < c &&b >= c)
  12. 244            return 1;
  13. 245    }
  14. 246
  15. 247    return 0;
  16. 248}

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

  1. 214/*
  2. 215 * get_next_entry – return the offset of the first valid entry atleast ‘len’
  3. 216 * bytes after ‘off’.
  4. 217 *
  5. 218 * Caller must hold log->mutex.
  6. 219 */
  7. 220static size_t get_next_entry(struct logger_log *log, size_t off,size_t len)
  8. 221{
  9. 222    size_t count = 0;
  10. 223
  11. 224    do {
  12. 225        size_t nr =get_entry_len(log, off);  // 取得一下条记录的长度
  13. 226        off = logger_offset(off+ nr);     // off指向一条记录
  14. 227        count += nr;
  15. 228    } while (count < len);
  16. 229
  17. 230    return off;
  18. 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函数定义如下:

  1. 272/*
  2. 273 * do_write_log – writes ‘len’ bytes from ‘buf’ to ‘log’
  3. 274 *
  4. 275 * The caller needs to hold log->mutex.
  5. 276 */
  6. 277static void do_write_log(struct logger_log *log, const void *buf,size_t count)
  7. 278{
  8. 279    size_t len;
  9. 280
  10. 281    len = min(count,log->size – log->w_off); // 处理后半部分
  11. 282    memcpy(log->buffer +log->w_off, buf, len);
  12. 283
  13. 284    if (count != len) // 如果有需要,处理前半部分
  14. 285        memcpy(log->buffer,buf + len, count – len);
  15. 286
  16. 287    log->w_off =logger_offset(log->w_off + count);
  17. 288
  18. 289}

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

  1. 291/*
  2. 292 * do_write_log_user – writes ‘len’ bytes from the user-space buffer’buf’ to
  3. 293 * the log ‘log’
  4. 294 *
  5. 295 * The caller needs to hold log->mutex.
  6. 296 *
  7. 297 * Returns ‘count’ on success, negative error code on failure.
  8. 298 */
  9. 299static ssize_t do_write_log_from_user(struct logger_log *log,
  10. 300                      constvoid __user *buf, size_t count)
  11. 301{
  12. 302    size_t len;
  13. 303
  14. 304    len = min(count,log->size – log->w_off);
  15. 305    if (len &©_from_user(log->buffer + log->w_off, buf, len))
  16. 306        return -EFAULT;
  17. 307
  18. 308    if (count != len)
  19. 309        if(copy_from_user(log->buffer, buf + len, count – len))
  20. 310            return -EFAULT;
  21. 311
  22. 312    log->w_off =logger_offset(log->w_off + count);
  23. 313
  24. 314    return count;
  25. 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文件中:

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

57-82行,定义了2-7共6个LOG优先级。115-117行,定义了Log.v函数,可以看到,该函数是通过调用本地函数println_native来实现的。

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

  1. 142/*
  2. 143 * JNI registration.
  3. 144 */
  4. 145static JNINativeMethod gMethods[] = {
  5. 146    /* name, signature, funcPtr*/
  6. 147    {“isLoggable”,     “(Ljava/lang/String;I)Z”, (void*) android_util_Log_isLoggable},
  7. 148    {“println_native”“(IILjava/lang/String;Ljava/lang/String;)I”, (void*)android_util_Log_println_native },
  8. 149};

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

  1. 99/*
  2. 100 * In class android.util.Log:
  3. 101 *  public static native intprintln_native(int buffer, int priority, String tag, String msg)
  4. 102 */
  5. 103static jint android_util_Log_println_native(JNIEnv* env, jobjectclazz,
  6. 104        jint bufID, jintpriority, jstring tagObj, jstring msgObj)
  7. 105{
  8. 106    const char* tag = NULL;
  9. 107    const char* msg = NULL;
  10. 108
  11. 109    if (msgObj == NULL) {
  12. 110        jclass npeClazz;
  13. 111
  14. 112        npeClazz =env->FindClass(“java/lang/NullPointerException”);
  15. 113        assert(npeClazz !=NULL);
  16. 114
  17. 115       env->ThrowNew(npeClazz, “println needs a message”);
  18. 116        return -1;
  19. 117    }
  20. 118
  21. 119    if (bufID < 0 || bufID>= LOG_ID_MAX) {
  22. 120        jclass npeClazz;
  23. 121
  24. 122        npeClazz =env->FindClass(“java/lang/NullPointerException”);
  25. 123        assert(npeClazz !=NULL);
  26. 124
  27. 125       env->ThrowNew(npeClazz, “bad bufID”);
  28. 126        return -1;
  29. 127    }
  30. 128
  31. 129    if (tagObj != NULL)
  32. 130        tag =env->GetStringUTFChars(tagObj, NULL);
  33. 131    msg =env->GetStringUTFChars(msgObj, NULL);
  34. 132
  35. 133    int res =__android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
  36. 134
  37. 135    if (tag != NULL)
  38. 136       env->ReleaseStringUTFChars(tagObj, tag);
  39. 137   env->ReleaseStringUTFChars(msgObj, msg);
  40. 138
  41. 139    return res;
  42. 140}

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

  1. 162int __android_log_buf_write(int bufID, int prio, const char *tag,const char *msg)
  2. 163{
  3. 164    struct iovec vec[3];
  4. 165
  5. 166    if (!tag)
  6. 167        tag = “”;
  7. 168
  8. 169    /* XXX: This needs to go!*/
  9. 170    if (!strcmp(tag,“HTC_RIL”) ||
  10. 171        !strncmp(tag,“RIL”, 3) || /* Any log tag with “RIL” as the prefix */
  11. 172        !strcmp(tag,“AT”) ||
  12. 173        !strcmp(tag,“GSM”) ||
  13. 174        !strcmp(tag,“STK”) ||
  14. 175        !strcmp(tag,“CDMA”) ||
  15. 176        !strcmp(tag,“PHONE”) ||
  16. 177        !strcmp(tag,“SMS”))
  17. 178            bufID =LOG_ID_RADIO;
  18. 179
  19. 180    vec[0].iov_base   = (unsigned char *) &prio;
  20. 181    vec[0].iov_len    = 1;
  21. 182    vec[1].iov_base   = (void *) tag;
  22. 183    vec[1].iov_len    = strlen(tag) + 1;
  23. 184    vec[2].iov_base   = (void *) msg;
  24. 185    vec[2].iov_len    = strlen(msg) + 1;
  25. 186
  26. 187    return write_to_log(bufID,vec, 3);
  27. 188}

170-178行,如果出现“HTC_RIL”等字符,将bufID设置为LOG_ID_RADIO。180-185行,将prio,tag,msg保存在数组vec中。

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

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

__write_to_log_init函数定义如下:

  1.  96static int__write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
  2.  97{
  3.  98#ifdef HAVE_PTHREADS
  4.  99    pthread_mutex_lock(&log_init_lock);
  5. 100#endif
  6. 101
  7. 102    if (write_to_log ==__write_to_log_init) {
  8. 103        log_fds[LOG_ID_MAIN] =log_open(“/dev/”LOGGER_LOG_MAIN, O_WRONLY);
  9. 104        log_fds[LOG_ID_RADIO] =log_open(“/dev/”LOGGER_LOG_RADIO, O_WRONLY);
  10. 105        log_fds[LOG_ID_EVENTS]= log_open(“/dev/”LOGGER_LOG_EVENTS, O_WRONLY);
  11. 106        log_fds[LOG_ID_SYSTEM]= log_open(“/dev/”LOGGER_LOG_SYSTEM, O_WRONLY);
  12. 107
  13. 108        write_to_log =__write_to_log_kernel;
  14. 109
  15. 110        if(log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
  16. 111               log_fds[LOG_ID_EVENTS] < 0) {
  17. 112           log_close(log_fds[LOG_ID_MAIN]);
  18. 113           log_close(log_fds[LOG_ID_RADIO]);
  19. 114           log_close(log_fds[LOG_ID_EVENTS]);
  20. 115           log_fds[LOG_ID_MAIN] = -1;
  21. 116           log_fds[LOG_ID_RADIO] = -1;
  22. 117           log_fds[LOG_ID_EVENTS] = -1;
  23. 118            write_to_log =__write_to_log_null;
  24. 119        }
  25. 120
  26. 121        if(log_fds[LOG_ID_SYSTEM] < 0) {
  27. 122           log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
  28. 123        }
  29. 124    }
  30. 125
  31. 126#ifdef HAVE_PTHREADS
  32. 127   pthread_mutex_unlock(&log_init_lock);
  33. 128#endif
  34. 129
  35. 130    return write_to_log(log_id,vec, nr);
  36. 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函数。

  1. 78static int__write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
  2. 79{
  3. 80    ssize_t ret;
  4. 81    int log_fd;
  5. 82
  6. 83    if (/*(int)log_id >= 0 &&*/(int)log_id < (int)LOG_ID_MAX) {
  7. 84        log_fd = log_fds[(int)log_id];
  8. 85    } else {
  9. 86        return EBADF;
  10. 87    }
  11. 88
  12. 89    do {
  13. 90        ret = log_writev(log_fd, vec, nr);
  14. 91    } while (ret < 0 && errno ==EINTR);
  15. 92
  16. 93    return ret;
  17. 94}

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

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

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

  1. 49int  writev( int  fd, const struct iovec*  vecs, int count )
  2. 50{
  3. 51    int   total = 0;
  4. 52
  5. 53    for ( ; count > 0;count–, vecs++ ) {
  6. 54        const char*  buf = (const char*)vecs->iov_base;
  7. 55        int          len = (int)vecs->iov_len;
  8. 56
  9. 57        while (len > 0) {
  10. 58            int  ret = write( fd, buf, len );
  11. 59            if (ret < 0) {
  12. 60                if (total == 0)
  13. 61                    total = -1;
  14. 62                goto Exit;
  15. 63            }
  16. 64            if (ret == 0)
  17. 65                goto Exit;
  18. 66
  19. 67            total += ret;
  20. 68            buf   += ret;
  21. 69            len   -= ret;
  22. 70        }
  23. 71    }
  24. 72Exit:
  25. 73    return total;
  26. 74}

该函数完成将字符串数组成员依次写到指定的设备中。分析到这里,我们就清楚了应用程序怎样把LOG信息写到LOG设备中了。

关于xmsg

技术面前人人平等.同时技术也不分高低贵贱.正所谓学无大小,达者为尊.
此条目发表在未分类分类目录。将固定链接加入收藏夹。