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