Android架构分析之LOG模块

作者:刘昊昱 

博客: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)