版权声明:本文为博主原创文章,未经博主允许不得转载。
作者:刘昊昱
博客:http://blog.csdn.net/liuhaoyutz
Android版本:2.3.7_r1
Linux内核版本:android-goldfish-2.6.29
Android的LOG模块分为内核驱动部分和用户空间接口部分。
一、内核LOG模块分析
我们先来看内核驱动部分,其代码位于drivers/staging/android/logger.c文件中。按照分析Linux内核驱动程序的惯例,我们从模块初始化函数开始分析:
- 588static int __init logger_init(void)
- 589{
- 590 int ret;
- 591
- 592 ret =init_log(&log_main);
- 593 if (unlikely(ret))
- 594 goto out;
- 595
- 596 ret =init_log(&log_events);
- 597 if (unlikely(ret))
- 598 goto out;
- 599
- 600 ret =init_log(&log_radio);
- 601 if (unlikely(ret))
- 602 goto out;
- 603
- 604out:
- 605 return ret;
- 606}
- 607device_initcall(logger_init);
logger_init函数即是LOG模块初始化函数,其调用了3次init_log函数,注册了log_main,log_events,log_radio三个LOG设备,init_log函数定义如下:
- 571static int __init init_log(struct logger_log *log)
- 572{
- 573 int ret;
- 574
- 575 ret =misc_register(&log->misc);
- 576 if (unlikely(ret)) {
- 577 printk(KERN_ERR“logger: failed to register misc “
- 578 “device forlog ‘%s’!\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};
log_main,log_events,log_radio三个LOG设备都是logger_log结构体类型的变量,其定义如下:
- 533/*
- 534 * Defines a log structure with name ‘NAME’ and a size of ‘SIZE’bytes, which
- 535 * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, andless than
- 536 * LONG_MAX minus LOGGER_ENTRY_MAX_LEN.
- 537 */
- 538#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \
- 539static unsigned char _buf_ ## VAR[SIZE]; \
- 540static struct logger_log VAR = { \
- 541 .buffer = _buf_ ## VAR, \
- 542 .misc = { \
- 543 .minor =MISC_DYNAMIC_MINOR, \
- 544 .name = NAME, \
- 545 .fops =&logger_fops, \
- 546 .parent = NULL, \
- 547 }, \
- 548 .wq =__WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \
- 549 .readers = LIST_HEAD_INIT(VAR.readers), \
- 550 .mutex =__MUTEX_INITIALIZER(VAR .mutex), \
- 551 .w_off = 0, \
- 552 .head = 0, \
- 553 .size = SIZE, \
- 554};
- 555
- 556DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)
- 557DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024)
- 558DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024)
在drivers/staging/android/logger.h文件中,有如下定义:
- 33#define LOGGER_LOG_RADIO “log_radio” /* radio-related messages */
- 34#define LOGGER_LOG_EVENTS “log_events” /*system/hardware events */
- 35#define LOGGER_LOG_MAIN “log_main” /*everything else */
由上面代码的注释,可以理解log_main,log_events,log_radio三种LOG设备的作用。综合以上分析,可知在LOG模块初始化函数logger_init中,以misc设备类型注册了3个LOG设备log_main,log_events和log_radio,分别对应/dev/log/main,/dev/log/events,/dev/log/radio,应用空间程序就可以通过对这三个设备进行读写操作与LOG内核驱动模块交互。
由DEFINE_LOGGER_DEVICE 宏定义可知,LOG设备的操作函数集被设置为logger_fops,其定义如下:
- 522static struct file_operations logger_fops = {
- 523 .owner = THIS_MODULE,
- 524 .read = logger_read,
- 525 .aio_write =logger_aio_write,
- 526 .poll = logger_poll,
- 527 .unlocked_ioctl =logger_ioctl,
- 528 .compat_ioctl =logger_ioctl,
- 529 .open = logger_open,
- 530 .release = logger_release,
- 531};
我们先来看open函数:
- 384/*
- 385 * logger_open – the log’s open() file operation
- 386 *
- 387 * Note how near a no-op this is in the write-only case. Keep it thatway!
- 388 */
- 389static int logger_open(struct inode *inode, struct file *file)
- 390{
- 391 struct logger_log *log;
- 392 int ret;
- 393
- 394 ret =nonseekable_open(inode, file);
- 395 if (ret)
- 396 return ret;
- 397
- 398 log =get_log_from_minor(MINOR(inode->i_rdev));
- 399 if (!log)
- 400 return -ENODEV;
- 401
- 402 if (file->f_mode &FMODE_READ) {
- 403 struct logger_reader*reader;
- 404
- 405 reader =kmalloc(sizeof(struct logger_reader), GFP_KERNEL);
- 406 if (!reader)
- 407 return -ENOMEM;
- 408
- 409 reader->log = log;
- 410 INIT_LIST_HEAD(&reader->list);
- 411
- 412 mutex_lock(&log->mutex);
- 413 reader->r_off =log->head;
- 414 list_add_tail(&reader->list, &log->readers);
- 415 mutex_unlock(&log->mutex);
- 416
- 417 file->private_data =reader;
- 418 } else
- 419 file->private_data =log;
- 420
- 421 return 0;
- 422}
- 423
398行,调用get_log_from_minor函数,通过次设备号来取得对应的logger_log结构体变量。该函数定义如下:
- 560static struct logger_log * get_log_from_minor(int minor)
- 561{
- 562 if (log_main.misc.minor ==minor)
- 563 return &log_main;
- 564 if (log_events.misc.minor== minor)
- 565 return &log_events;
- 566 if (log_radio.misc.minor ==minor)
- 567 return &log_radio;
- 568 return NULL;
- 569}
回到logger_open函数,402-418行,如果打开的LOG设备是可读的,创建一个logger_reader结构体变量,并初始化。logger_reader结构体代表被打开进行读操作的LOG设备,其定义如下:
- 48/*
- 49 * struct logger_reader – alogging device open for reading
- 50 *
- 51 * This object lives from opento release, so we don’t need additional
- 52 * reference counting. Thestructure is protected by log->mutex.
- 53 */
- 54struct logger_reader {
- 55 struct logger_log * log; /* associated log */
- 56 struct list_head list; /* entry in logger_log’s list */
- 57 size_t r_off; /* current read head offset */
- 58};
下面我们来看read函数:
- 143/*
- 144 * logger_read – our log’s read() method
- 145 *
- 146 * Behavior:
- 147 *
- 148 * – O_NONBLOCK works
- 149 * – If there are no logentries to read, blocks until log is written to
- 150 * – Atomically reads exactlyone log entry
- 151 *
- 152 * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno toEINVAL if read
- 153 * buffer is insufficient to hold next entry.
- 154 */
- 155static ssize_t logger_read(struct file *file, char __user *buf,
- 156 size_t count,loff_t *pos)
- 157{
- 158 struct logger_reader*reader = file->private_data;
- 159 struct logger_log *log =reader->log;
- 160 ssize_t ret;
- 161 DEFINE_WAIT(wait);
- 162
- 163start:
- 164 while (1) {
- 165 prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);
- 166
- 167 mutex_lock(&log->mutex);
- 168 ret = (log->w_off ==reader->r_off);
- 169 mutex_unlock(&log->mutex);
- 170 if (!ret)
- 171 break;
- 172
- 173 if (file->f_flags& O_NONBLOCK) {
- 174 ret = -EAGAIN;
- 175 break;
- 176 }
- 177
- 178 if(signal_pending(current)) {
- 179 ret = -EINTR;
- 180 break;
- 181 }
- 182
- 183 schedule();
- 184 }
- 185
- 186 finish_wait(&log->wq, &wait);
- 187 if (ret)
- 188 return ret;
- 189
- 190 mutex_lock(&log->mutex);
- 191
- 192 /* is there still somethingto read or did we race? */
- 193 if (unlikely(log->w_off== reader->r_off)) {
- 194 mutex_unlock(&log->mutex);
- 195 goto start;
- 196 }
- 197
- 198 /* get the size of the nextentry */
- 199 ret = get_entry_len(log,reader->r_off);
- 200 if (count < ret) {
- 201 ret = -EINVAL;
- 202 goto out;
- 203 }
- 204
- 205 /* get exactly one entryfrom the log */
- 206 ret =do_read_log_to_user(log, reader, buf, ret);
- 207
- 208out:
- 209 mutex_unlock(&log->mutex);
- 210
- 211 return ret;
- 212}
164-184行,这个while循环是用来处理如果没有LOG可读,则进入休眠等待。但是如果文件打开时被设置为非阻塞模式O_NONBLOCK或者有信号需要处理signal_pending(current),则不休眠等待,直接返回。LOG内容保存在一个循环缓冲区中,代码中通过log->w_off == reader->r_off判断是否有LOG可读。
198-203行,如果有LOG可读,则调用get_entry_len函数取得下一条LOG的长度(LOG的长度包括loger_entry结构体的大小和有效负载payload的长度),该函数定义如下:
- 86/*
- 87 * get_entry_len – Grabs thelength of the payload of the next entry starting
- 88 * from ‘off’.
- 89 *
- 90 * Caller needs to holdlog->mutex.
- 91 */
- 92static __u32get_entry_len(struct logger_log *log, size_t off)
- 93{
- 94 __u16 val;
- 95
- 96 switch (log->size – off) {
- 97 case 1:
- 98 memcpy(&val, log->buffer + off,1);
- 99 memcpy(((char *) &val) + 1,log->buffer, 1);
- 100 break;
- 101 default:
- 102 memcpy(&val,log->buffer + off, 2);
- 103 }
- 104
- 105 return sizeof(structlogger_entry) + val;
- 106}
LOG缓冲区中的每一条LOG由两部分组成,一是用于描述LOG信息的logger_entry结构体,二是LOG本身,又称为payload。在drivers/staging/android/logger.h文件中,logger_entry结构体定义如下:
- 23struct logger_entry {
- 24 __u16 len; /* length of the payload */
- 25 __u16 __pad; /* no matter what, we get 2 bytes of padding */
- 26 __s32 pid; /* generating process’s pid */
- 27 __s32 tid; /* generating process’s tid */
- 28 __s32 sec; /* seconds since Epoch */
- 29 __s32 nsec; /* nanoseconds */
- 30 char msg[0]; /* the entry’s payload */
- 31};
get_entry_len函数用于取得整个LOG的长度,包括logger_entry结构体大小和payload的长度,logger_entry的大小是固定的,关键是怎样取得payload的长度。payload的长度记录在logger_entry第一个成员len中,16位,占2个字节。因为LOG缓冲区是一个循环缓冲区,所以这2个字节存放的位置有一种特殊情况是,第一个字节在最后一个位置,第二个字节在第一个位置。所以在get_entry_len函数的实现中,分两种情况处理,case 1就是分别拷贝第一个字节和第二个字节到val变量中,其它的情况都是直接将2个节的内容拷贝到val变量中。
最后,注意get_entry_len函数的105行,返回值是sizeof(struct logger_entry) + val,即我们前面所说的,返回logger_entry结构体的大小加上payload的长度。
回到logger_read函数,206行,调用do_read_log_to_user函数,该函数真正将LOG信息读到用户空间,定义如下:
- 108/*
- 109 * do_read_log_to_user – reads exactly ‘count’ bytes from ‘log’ intothe
- 110 * user-space buffer ‘buf’. Returns ‘count’ on success.
- 111 *
- 112 * Caller must hold log->mutex.
- 113 */
- 114static ssize_t do_read_log_to_user(struct logger_log *log,
- 115 structlogger_reader *reader,
- 116 char __user*buf,
- 117 size_tcount)
- 118{
- 119 size_t len;
- 120
- 121 /*
- 122 * We read from the log intwo disjoint operations. First, we read from
- 123 * the current read headoffset up to ‘count’ bytes or to the end of
- 124 * the log, whichever comesfirst.
- 125 */
- 126 len = min(count, log->size- reader->r_off);
- 127 if (copy_to_user(buf,log->buffer + reader->r_off, len))
- 128 return -EFAULT;
- 129
- 130 /*
- 131 * Second, we read anyremaining bytes, starting back at the head of
- 132 * the log.
- 133 */
- 134 if (count != len)
- 135 if (copy_to_user(buf +len, log->buffer, count – len))
- 136 return -EFAULT;
- 137
- 138 reader->r_off =logger_offset(reader->r_off + count);
- 139
- 140 return count;
- 141}
因为LOG保存在循环缓冲区中,所以do_read_log_to_user函数考虑到这种情况,分两步通过copy_to_user函数拷贝LOG到用户空间。最后注意138行,通过logger_offset宏设置下一次的读取位置。该宏定义如下:
- 60/* logger_offset- returns index ‘n’ into the log via (optimized) modulus */
- 61#define logger_offset(n) ((n) & (log->size – 1))
下面我们来看LOG模块的write函数:
- 317/*
- 318 * logger_aio_write – our write method, implementing support forwrite(),
- 319 * writev(), and aio_write(). Writes are our fast path, and we try tooptimize
- 320 * them above all else.
- 321 */
- 322ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,
- 323 unsigned longnr_segs, loff_t ppos)
- 324{
- 325 struct logger_log *log =file_get_log(iocb->ki_filp);
- 326 size_t orig =log->w_off;
- 327 struct logger_entry header;
- 328 struct timespec now;
- 329 ssize_t ret = 0;
- 330
- 331 now =current_kernel_time();
- 332
- 333 header.pid =current->tgid;
- 334 header.tid =current->pid;
- 335 header.sec = now.tv_sec;
- 336 header.nsec = now.tv_nsec;
- 337 header.len = min_t(size_t,iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);
- 338
- 339 /* null writes succeed,return zero */
- 340 if (unlikely(!header.len))
- 341 return 0;
- 342
- 343 mutex_lock(&log->mutex);
- 344
- 345 /*
- 346 * Fix up any readers,pulling them forward to the first readable
- 347 * entry after (what willbe) the new write offset. We do this now
- 348 * because if we partiallyfail, we can end up with clobbered log
- 349 * entries that encroach onreadable buffer.
- 350 */
- 351 fix_up_readers(log,sizeof(struct logger_entry) + header.len);
- 352
- 353 do_write_log(log,&header, sizeof(struct logger_entry));
- 354
- 355 while (nr_segs– > 0) {
- 356 size_t len;
- 357 ssize_t nr;
- 358
- 359 /* figure out how muchof this vector we can keep */
- 360 len = min_t(size_t,iov->iov_len, header.len – ret);
- 361
- 362 /* write out thissegment’s payload */
- 363 nr =do_write_log_from_user(log, iov->iov_base, len);
- 364 if (unlikely(nr <0)) {
- 365 log->w_off = orig;
- 366 mutex_unlock(&log->mutex);
- 367 return nr;
- 368 }
- 369
- 370 iov++;
- 371 ret += nr;
- 372 }
- 373
- 374 mutex_unlock(&log->mutex);
- 375
- 376 /* wake up any blockedreaders */
- 377 wake_up_interruptible(&log->wq);
- 378
- 379 return ret;
- 380}
325行,调用file_get_log函数取得要读取的LOG设备:
- 77static inlinestruct logger_log * file_get_log(struct file *file)
- 78{
- 79 if (file->f_mode & FMODE_READ) {
- 80 struct logger_reader *reader =file->private_data;
- 81 return reader->log;
- 82 } else
- 83 return file->private_data;
- 84}
327行,定义了一个logger_entry结构体变量header,logger_entry结构体用于描述一个LOG的信息,定义在drivers/staging/android/logger.h文件中:
- 23struct logger_entry {
- 24 __u16 len; /* length of the payload */
- 25 __u16 __pad; /* no matter what, we get 2 bytes of padding */
- 26 __s32 pid; /* generating process’s pid */
- 27 __s32 tid; /* generating process’s tid */
- 28 __s32 sec; /* seconds since Epoch */
- 29 __s32 nsec; /* nanoseconds */
- 30 char msg[0]; /* the entry’s payload */
- 31};
333-337行,对logger_entry结构体变量header进行初始化。351行,调用fix_up_readers函数,修正某些logger_read的读取位置指针。因为LOG缓冲区是循环使用的,当进行写操作后,可能会覆盖一些末读取的内容,因此,需要修正某些logger_read的读取位置指针。
- 250/*
- 251 * fix_up_readers – walk the list of all readers and “fixup” any who were
- 252 * lapped by the writer; also do the same for the default “starthead”.
- 253 * We do this by “pulling forward” the readers and starthead to the first
- 254 * entry after the new write head.
- 255 *
- 256 * The caller needs to hold log->mutex.
- 257 */
- 258static void fix_up_readers(struct logger_log *log, size_t len)
- 259{
- 260 size_t old = log->w_off;
- 261 size_t new =logger_offset(old + len);
- 262 struct logger_reader*reader;
- 263
- 264 if (clock_interval(old,new, log->head))
- 265 log->head =get_next_entry(log, log->head, len);
- 266
- 267 list_for_each_entry(reader,&log->readers, list)
- 268 if (clock_interval(old,new, reader->r_off))
- 269 reader->r_off =get_next_entry(log, reader->r_off, len);
- 270}
264行,调用clock_interval(old, new, log->head)函数,判断第三个参数log->head是否在第一个和第二个参数范围之内,即判断第三个参数log->head指定的位置是否会被本次write操作覆盖。clock_interval函数定义如下:
- 233/*
- 234 * clock_interval – is a < c < b in mod-space? Put another way,does the line
- 235 * from a to b cross c?
- 236 */
- 237static inline int clock_interval(size_t a, size_t b, size_t c)
- 238{
- 239 if (b < a) { // 转到循环缓冲区前面
- 240 if (a < c || b >=c)
- 241 return 1;
- 242 } else {
- 243 if (a < c &&b >= c)
- 244 return 1;
- 245 }
- 246
- 247 return 0;
- 248}
回到fix_up_readers 函数,265行,如果log->head指定的位置会被本次write操作覆盖,则调用get_next_entry获得下一条LOG记录的起始位置,并赋值给log->head。get_next_entry函数定义如下:
- 214/*
- 215 * get_next_entry – return the offset of the first valid entry atleast ‘len’
- 216 * bytes after ‘off’.
- 217 *
- 218 * Caller must hold log->mutex.
- 219 */
- 220static size_t get_next_entry(struct logger_log *log, size_t off,size_t len)
- 221{
- 222 size_t count = 0;
- 223
- 224 do {
- 225 size_t nr =get_entry_len(log, off); // 取得一下条记录的长度
- 226 off = logger_offset(off+ nr); // off指向一条记录
- 227 count += nr;
- 228 } while (count < len);
- 229
- 230 return off;
- 231}
回到fix_up_readers 函数,267-269行,遍历log->readers列表。对于每个logger_reader,如果logger_reader.r_off被覆盖,则向后做偏移。回到logger_aio_write函数,353行,调用do_write_log函数,将logger_entry写入LOG缓冲区。do_write_log函数定义如下:
- 272/*
- 273 * do_write_log – writes ‘len’ bytes from ‘buf’ to ‘log’
- 274 *
- 275 * The caller needs to hold log->mutex.
- 276 */
- 277static void do_write_log(struct logger_log *log, const void *buf,size_t count)
- 278{
- 279 size_t len;
- 280
- 281 len = min(count,log->size – log->w_off); // 处理后半部分
- 282 memcpy(log->buffer +log->w_off, buf, len);
- 283
- 284 if (count != len) // 如果有需要,处理前半部分
- 285 memcpy(log->buffer,buf + len, count – len);
- 286
- 287 log->w_off =logger_offset(log->w_off + count);
- 288
- 289}
回到logger_aio_write函数,355-372行,通过这个while循环将用户空间提供的LOG内容写入LOG设备中。真正的写操作是通过do_write_log_from_user函数完成的,该函数定义如下:
- 291/*
- 292 * do_write_log_user – writes ‘len’ bytes from the user-space buffer’buf’ to
- 293 * the log ‘log’
- 294 *
- 295 * The caller needs to hold log->mutex.
- 296 *
- 297 * Returns ‘count’ on success, negative error code on failure.
- 298 */
- 299static ssize_t do_write_log_from_user(struct logger_log *log,
- 300 constvoid __user *buf, size_t count)
- 301{
- 302 size_t len;
- 303
- 304 len = min(count,log->size – log->w_off);
- 305 if (len &©_from_user(log->buffer + log->w_off, buf, len))
- 306 return -EFAULT;
- 307
- 308 if (count != len)
- 309 if(copy_from_user(log->buffer, buf + len, count – len))
- 310 return -EFAULT;
- 311
- 312 log->w_off =logger_offset(log->w_off + count);
- 313
- 314 return count;
- 315}
回到logger_aio_write函数,377行,调用wake_up_interruptible函数唤醒在log->wq上等待的logger_reader。至此,内核空间的LOG模块我们就分析完了。
二、用户空间LOG模块分析
Android应用程序是通过应用程序框架层的JAVA接口android.util.Log来使用LOG系统的,该接口定义在frameworks/base/core/java/android/util/Log.java文件中:
- 52public finalclass Log {
- 53
- 54 /**
- 55 * Priority constant for the printlnmethod; use Log.v.
- 56 */
- 57 public static final int VERBOSE = 2;
- 58
- 59 /**
- 60 * Priority constant for the printlnmethod; use Log.d.
- 61 */
- 62 public static final int DEBUG = 3;
- 63
- 64 /**
- 65 * Priority constant for the printlnmethod; use Log.i.
- 66 */
- 67 public static final int INFO = 4;
- 68
- 69 /**
- 70 * Priority constant for the printlnmethod; use Log.w.
- 71 */
- 72 public static final int WARN = 5;
- 73
- 74 /**
- 75 * Priority constant for the printlnmethod; use Log.e.
- 76 */
- 77 public static final int ERROR = 6;
- 78
- 79 /**
- 80 * Priority constant for the printlnmethod.
- 81 */
- 82 public static final int ASSERT = 7;
- 83
- 84 /**
- 85 * Exception class used to capture a stacktrace in {@link #wtf()}.
- 86 */
- 87 private static class TerribleFailureextends Exception {
- 88 TerribleFailure(String msg, Throwablecause) { super(msg, cause); }
- 89 }
- 90
- 91 /**
- 92 * Interface to handle terrible failuresfrom {@link #wtf()}.
- 93 *
- 94 * @hide
- 95 */
- 96 public interface TerribleFailureHandler {
- 97 void onTerribleFailure(String tag, TerribleFailurewhat);
- 98 }
- 99
- 100 private staticTerribleFailureHandler sWtfHandler = new TerribleFailureHandler() {
- 101 public voidonTerribleFailure(String tag, TerribleFailure what) {
- 102 RuntimeInit.wtf(tag, what);
- 103 }
- 104 };
- 105
- 106 private Log() {
- 107 }
- 108
- 109 /**
- 110 * Send a {@link #VERBOSE}log message.
- 111 * @param tag Used toidentify the source of a log message. Itusually identifies
- 112 * the class or activity where the logcall occurs.
- 113 * @param msg The messageyou would like logged.
- 114 */
- 115 public static int v(Stringtag, String msg) {
- 116 returnprintln_native(LOG_ID_MAIN, VERBOSE, tag, msg);
- 117 }
- 118
- 119 /**
- 120 * Send a {@link #VERBOSE}log message and log the exception.
- 121 * @param tag Used toidentify the source of a log message. Itusually identifies
- 122 * the class or activity where the logcall occurs.
- 123 * @param msg The messageyou would like logged.
- 124 * @param tr An exceptionto log
- 125 */
- 126 public static int v(Stringtag, String msg, Throwable tr) {
- 127 returnprintln_native(LOG_ID_MAIN, VERBOSE, tag, msg + ‘\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};
由这段代码可以看出,JAVA层调用的本地函数println_native,在这里是指向android_util_Log_println_native函数,该函数定义如下:
- 99/*
- 100 * In class android.util.Log:
- 101 * public static native intprintln_native(int buffer, int priority, String tag, String msg)
- 102 */
- 103static jint android_util_Log_println_native(JNIEnv* env, jobjectclazz,
- 104 jint bufID, jintpriority, jstring tagObj, jstring msgObj)
- 105{
- 106 const char* tag = NULL;
- 107 const char* msg = NULL;
- 108
- 109 if (msgObj == NULL) {
- 110 jclass npeClazz;
- 111
- 112 npeClazz =env->FindClass(“java/lang/NullPointerException”);
- 113 assert(npeClazz !=NULL);
- 114
- 115 env->ThrowNew(npeClazz, “println needs a message”);
- 116 return -1;
- 117 }
- 118
- 119 if (bufID < 0 || bufID>= LOG_ID_MAX) {
- 120 jclass npeClazz;
- 121
- 122 npeClazz =env->FindClass(“java/lang/NullPointerException”);
- 123 assert(npeClazz !=NULL);
- 124
- 125 env->ThrowNew(npeClazz, “bad bufID”);
- 126 return -1;
- 127 }
- 128
- 129 if (tagObj != NULL)
- 130 tag =env->GetStringUTFChars(tagObj, NULL);
- 131 msg =env->GetStringUTFChars(msgObj, NULL);
- 132
- 133 int res =__android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
- 134
- 135 if (tag != NULL)
- 136 env->ReleaseStringUTFChars(tagObj, tag);
- 137 env->ReleaseStringUTFChars(msgObj, msg);
- 138
- 139 return res;
- 140}
开始是进行一些参数的检查,133行,调用运行时库函数__android_log_buf_write执行写操作,该函数定义在system/core/liblog/logd_write.c文件中:
- 162int __android_log_buf_write(int bufID, int prio, const char *tag,const char *msg)
- 163{
- 164 struct iovec vec[3];
- 165
- 166 if (!tag)
- 167 tag = “”;
- 168
- 169 /* XXX: This needs to go!*/
- 170 if (!strcmp(tag,“HTC_RIL”) ||
- 171 !strncmp(tag,“RIL”, 3) || /* Any log tag with “RIL” as the prefix */
- 172 !strcmp(tag,“AT”) ||
- 173 !strcmp(tag,“GSM”) ||
- 174 !strcmp(tag,“STK”) ||
- 175 !strcmp(tag,“CDMA”) ||
- 176 !strcmp(tag,“PHONE”) ||
- 177 !strcmp(tag,“SMS”))
- 178 bufID =LOG_ID_RADIO;
- 179
- 180 vec[0].iov_base = (unsigned char *) &prio;
- 181 vec[0].iov_len = 1;
- 182 vec[1].iov_base = (void *) tag;
- 183 vec[1].iov_len = strlen(tag) + 1;
- 184 vec[2].iov_base = (void *) msg;
- 185 vec[2].iov_len = strlen(msg) + 1;
- 186
- 187 return write_to_log(bufID,vec, 3);
- 188}
170-178行,如果出现“HTC_RIL”等字符,将bufID设置为LOG_ID_RADIO。180-185行,将prio,tag,msg保存在数组vec中。
187行,调用write_to_log函数,该函数定义如下:
- 45static int __write_to_log_init(log_id_t, struct iovec *vec, size_tnr);
- 46static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) =__write_to_log_init;
__write_to_log_init函数定义如下:
- 96static int__write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
- 97{
- 98#ifdef HAVE_PTHREADS
- 99 pthread_mutex_lock(&log_init_lock);
- 100#endif
- 101
- 102 if (write_to_log ==__write_to_log_init) {
- 103 log_fds[LOG_ID_MAIN] =log_open(“/dev/”LOGGER_LOG_MAIN, O_WRONLY);
- 104 log_fds[LOG_ID_RADIO] =log_open(“/dev/”LOGGER_LOG_RADIO, O_WRONLY);
- 105 log_fds[LOG_ID_EVENTS]= log_open(“/dev/”LOGGER_LOG_EVENTS, O_WRONLY);
- 106 log_fds[LOG_ID_SYSTEM]= log_open(“/dev/”LOGGER_LOG_SYSTEM, O_WRONLY);
- 107
- 108 write_to_log =__write_to_log_kernel;
- 109
- 110 if(log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
- 111 log_fds[LOG_ID_EVENTS] < 0) {
- 112 log_close(log_fds[LOG_ID_MAIN]);
- 113 log_close(log_fds[LOG_ID_RADIO]);
- 114 log_close(log_fds[LOG_ID_EVENTS]);
- 115 log_fds[LOG_ID_MAIN] = -1;
- 116 log_fds[LOG_ID_RADIO] = -1;
- 117 log_fds[LOG_ID_EVENTS] = -1;
- 118 write_to_log =__write_to_log_null;
- 119 }
- 120
- 121 if(log_fds[LOG_ID_SYSTEM] < 0) {
- 122 log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
- 123 }
- 124 }
- 125
- 126#ifdef HAVE_PTHREADS
- 127 pthread_mutex_unlock(&log_init_lock);
- 128#endif
- 129
- 130 return write_to_log(log_id,vec, nr);
- 131}
如果write_to_log等于__write_to_log_init,即第一次调用write_to_log,则调用log_open打开4个LOG设备,并将write_to_log设置为__write_to_log_kernel,所以130行再调用write_to_log,即是调用__write_to_log_kernel函数。
- 78static int__write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
- 79{
- 80 ssize_t ret;
- 81 int log_fd;
- 82
- 83 if (/*(int)log_id >= 0 &&*/(int)log_id < (int)LOG_ID_MAX) {
- 84 log_fd = log_fds[(int)log_id];
- 85 } else {
- 86 return EBADF;
- 87 }
- 88
- 89 do {
- 90 ret = log_writev(log_fd, vec, nr);
- 91 } while (ret < 0 && errno ==EINTR);
- 92
- 93 return ret;
- 94}
核心函数是第90行调用的log_writev,该函数实现了写入操作。
- 40#definelog_open(pathname, flags) open(pathname, flags)
- 41#define log_writev(filedes,vector, count) writev(filedes, vector, count)
- 42#define log_close(filedes)close(filedes)
log_writev是一个宏,对应writev函数,定义在system/core/libcutils/uio.c文件中:
- 49int writev( int fd, const struct iovec* vecs, int count )
- 50{
- 51 int total = 0;
- 52
- 53 for ( ; count > 0;count–, vecs++ ) {
- 54 const char* buf = (const char*)vecs->iov_base;
- 55 int len = (int)vecs->iov_len;
- 56
- 57 while (len > 0) {
- 58 int ret = write( fd, buf, len );
- 59 if (ret < 0) {
- 60 if (total == 0)
- 61 total = -1;
- 62 goto Exit;
- 63 }
- 64 if (ret == 0)
- 65 goto Exit;
- 66
- 67 total += ret;
- 68 buf += ret;
- 69 len -= ret;
- 70 }
- 71 }
- 72Exit:
- 73 return total;
- 74}
该函数完成将字符串数组成员依次写到指定的设备中。分析到这里,我们就清楚了应用程序怎样把LOG信息写到LOG设备中了。