作者:刘昊昱

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

Android架构分析之LOG模块的更多相关文章

  1. Android架构分析之使用自定义硬件抽象层(HAL)模块

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本:2.3.7_r1 Linux内核版本:android-goldfish-2.6.29 在上一篇博 ...

  2. Android架构分析之Android消息处理机制(二)

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在上一篇文章中我们看了一个使用Handler处理Message消息的样例,本文我们 ...

  3. Android架构分析之Android消息处理机制(一)

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在这个系列文章中我们将来分析Android消息处理机制. 本文介绍了一个使用Han ...

  4. Qualcomm Android display架构分析

    Android display架构分析(一) http://blog.csdn.net/BonderWu/archive/2010/08/12/5805961.aspx http://hi.baidu ...

  5. 高通Android display架构分析

    目录(?)[-] Kernel Space Display架构介绍 函数和数据结构介绍 函数和数据结构介绍 函数和数据结构介绍 数据流分析 初始化过程分析 User Space display接口 K ...

  6. Android基础-系统架构分析,环境搭建,下载Android Studio,AndroidDevTools,Git使用教程,Github入门,界面设计介绍

    系统架构分析 Android体系结构 安卓结构有四大层,五个部分,Android分四层为: 应用层(Applications),应用框架层(Application Framework),系统运行层(L ...

  7. (转)App工程结构搭建:几种常见Android代码架构分析

    关于Android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的角度,看到整齐的代码,优美的分层总是一种舒服的享受的. 从艺术的角度看,其实我们是在追求一种美. 本文先分析几个当 ...

  8. Android MVP架构分析

    App架构在Android开发者中一直是讨论比较多的一个话题,目前讨论较多的有MVP.MVVM.Clean这三种.google官方对于架构的态度一直是非常开放的,让开发者自主选择组织和架构app的方式 ...

  9. 分布式MySQL数据库TDSQL架构分析

    摘要:腾讯计费平台部为了解决基于内存的NoSQL解决方式HOLD平台在应对多种业务接入时的不足.结合团队在MySQL领域多年应用和优化经验,终于在MySQL存储引擎基础上,打造一套分布式SQL系统TD ...

随机推荐

  1. mysqladmin在SuSE linux系统中--sleep參数使用不准确问题

    我们都知道,在MySQL中.能够使用mysqladmin命令的extended-status选项来查看MySQL的执行状态,比方获取我们经常关注的几个值: # mysqladmin -uroot -p ...

  2. hprose rpc使用实例(同时有Java和Delphi客户端的例子)

    php server <?php require_once('src/Hprose.php'); function hello($name) { echo "Hello $name!& ...

  3. [Android学习笔记]LayoutInflater的使用

    LayoutInflater用于动态载入布局,然后获取到布局中定义完成的控件引用 常在动态加载布局,和Adapter中用到 使用步骤:1.通过LayoutInflater加载xml布局文件2.从载入的 ...

  4. 44个JAVA代码质量管理工具(转)

    1. CodePro AnalytixIt’s a great tool (Eclipse plugin) for improving software quality. It has the nex ...

  5. java学习笔记01--数据类型

    java学习笔记01--数据类型 java数据类型划分 分为两大类型: 1)基本数据类型:类似于普通的值. 2)引用数据类型:传递的是内存的地址. 浮点类型实际上就是表示小数. java基本数据类型 ...

  6. Android - JNI静态(static)载入OpenCV

    JNI静态(static)载入OpenCV 本文地址: http://blog.csdn.net/caroline_wendy 步骤: 1. 准备OpenCV-Android库 复制OpenCV的sd ...

  7. 分布式发布订阅消息系统Kafka

    高吞吐量的分布式发布订阅消息系统Kafka--安装及测试   一.Kafka概述 Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据. 这种动作(网页浏览, ...

  8. 理解 Python 中的线程

    原地址:http://blog.jobbole.com/52060/ 本文由 伯乐在线 - acmerfight 翻译自 Akshar Raaj.欢迎加入技术翻译小组.转载请参见文章末尾处的要求. 我 ...

  9. 用TinyXml2读取XML文件的一个简单Demo

    废话少说直接上代码,需要的人自然一看便懂,对于第一次接触TinyXml2的人来说还是有帮助的. <?xml version="1.0"?> <Table name ...

  10. HDU 4611 Balls Rearrangement (数学-思维逻辑题)

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4611 题意:给你一个N.A.B,要你求 AC代码: #include <iostream> ...