Android的logger机制分析

  • 时间:
  • 浏览:0
  • 来源:神彩UU直播现场_彩神UU直播现场官方

       return println_native(LOG_ID_MAIN, INFO, tag, msg +'\n'+ getStackTraceString(tr));

       file->private_data= log;

   ANDROID_LOG_VERBOSE,

       /* wake up any blocked readers */

       wake_up_interruptible(&log->wq);

#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \

   .compat_ioctl= logger_ioctl,

   }

 * - Atomically reads exactly one log entry

struct logger_log {

 */

 * This is the local tag used for the following simplified

     再来看logger.c文件中,其它相关数据特性的定义:

 */

}

        */

};

      init_log函数主要调用了misc_register函数来注册misc设备,misc_register函数定义在kernel/common/drivers/char/misc.c文件中:

readers成员变量用来保存当前正在读取日志的线程池池,正在读取日志的线程池池由特性体logger_reader来描述。

       return-EFAULT;

 */

staticvoid fix_up_readers(struct logger_log *log, size_t len)

#define LOGGER_FLUSH_LOG       _IO(__LOGGERIO, 4) /* flush log */

       misc->this_device= device_create(misc_class, misc->parent, dev,NULL,

 * from 'off'.

    * the current read head offset up to 'count' bytes or to the end of

   mutex_lock(&log->mutex);

   if(!log)

}

                                         "%s", misc->name);

也能注意的是,在函数以前刚结束了了的地方,表示读取日志上下文的structlogger_reader是保占据 文件指针的private_data成员变量里面的,这是在打开设备文件时设置的,设备文件打开妙招 为logger_open

       但会 调用do_write_log首先把logger_entry特性体写入到日志缓冲区中:

       return-EFAULT;

 *

       goto start;

   default:

 *

       count += nr;

       mutex_lock(&log->mutex);

           return1;

{

   ANDROID_LOG_DEFAULT,   /* only for SetMinPriority() */

       memcpy(&val, log->buffer+ off,1);

       list_add(&misc->list,&misc_list);

#define LOGGER_ENTRY_MAX_LEN       (4*1024)

               if(c->minor== misc->minor){

       int err=0;

       mutex_unlock(&log->mutex);

   while(nr_segs-->0){

 * Simplified macro to send a verbose log message using the current LOG_TAG.

/*

DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO,64*1024)

   DEFINE_WAIT(wait);

   return ret;

int misc_register(struct miscdevice * misc)

/*

/*

 * get_next_entry - return the offset of the first valid entry at least 'len'

           log->w_off= orig;

       /* figure out how much of this vector we can keep */

       }

 *     number is set to %MISC_DYNAMIC_MINOR a minor number is assigned

#define LOGGER_ENTRY_MAX_PAYLOAD   \

   .readers = LIST_HEAD_INIT(VAR .readers), \

注册完成后,通过device_create创建设备文件节点。这里,将创建/dev/log/main/dev/log/events/dev/log/radio另有兩个 设备文件,另另有兩个 ,用户空间就都也能通过读写这另有兩个 文件和驱动线程池池进行交互。

#endif

Android系统在Frameworks层中定义了Log接口(frameworks/base/core/java/android/util/Log.java):

   len = min(count, log->size- log->w_off);

       }

 *     @misc: device structure

        */

{

      USER-NAME@MACHINE-NAME:~/Android$ adb shell

    * entries that encroach on readable buffer.

       iov++;

   }

   unsignedchar*    buffer;/* the ring buffer itself */

               goto out;

   ret = init_log(&log_events);

   size_t count =0;

 */

 * entry after the new write head.

printk的使用妙招 :

     查看/proc/kmsg文件:

 * entry after (what will be) the new write offset. We do this now

       if(copy_from_user(log->buffer, buf + len, count - len))

       int priority, String tag, String msg);

{

#define LOGGER_GET_LOG_LEN     _IO(__LOGGERIO, 2) /* used log len */

static size_t get_next_entry(struct logger_log *log, size_t off, size_t len)

   if(copy_to_user(buf, log->buffer+ reader->r_off, len))

   }

 * reference counting. The structure is protected by log->mutex.

}

       schedule();

       ret =(log->w_off== reader->r_off);

       goto out;

}

   }

publicfinalclass Log{

   .w_off = 0, \

       goto out;

 *     and placed in the minor field of the structure. For other cases

   ANDROID_LOG_FATAL,

       memcpy(&val, log->buffer+ off,2);

   publicstaticfinalint ERROR=6;

       ret =-EINVAL;

                               break;

size_t         r_off; /* current read head offset */

        * earlier defaults

   .head = 0, \

                     constvoid __user*buf, size_t count)

   ANDROID_LOG_ERROR,

       mutex_lock(&misc_mtx);

       /* figure out how much of this vector we can keep */

       return println_native(LOG_ID_MAIN, ERROR, tag, msg +'\n'+ getStackTraceString(tr));

   struct mutex       mutex; /* mutex protecting buffer */

       mutex_lock(&log->mutex);

#deinfe KERN_INFO  "<6>"   /* informational            */

   do_write_log(log,&header,sizeof(struct logger_entry));

还有有些也能注意的是,但会 Logger驱动线程池池模块在退出系统时,是不不卸载的,统统一种生活 模块没有 module_exit函数,而对于模块里面定义的对象,也没有 用对引用计数技术。

   /*

   }

      即判断当前缓冲区的写入位置和当前读线程池池的读取位置与非 相等,但会 不相等,则说明有新的日志可读。

wq成员变量是另有兩个 停留队列,用于保存正在停留读取日志的线程池池。

          (unsignedlong) log->size>>10, log->misc.name);

#define LOG_TAG NULL

   struct timespec now;

   int ret;

       USER-NAME@MACHINE-NAME:~/Android$ emulator &

   int ret;

       return println_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));

#define LOGGER_ENTRY_MAX_LEN       (4*1024)

 * struct logger_log - represents a specific log, such as 'main' or 'radio'

/*

 */

   /**

   fix_up_readers(log,sizeof(struct logger_entry)+ header.len);

#define KERN_ALERT "<1>"   /* action must be taken immediately */

 */

       return println_native(LOG_ID_MAIN, VERBOSE, tag, msg +'\n'+ getStackTraceString(tr));

以上是Logger在应用开发中的使用的简单分析,除了Logger在应用开发中的使用,我将更进一步地分析Logger驱动线程池池的源代码,更加深刻的认识Android日志系统。

   size_t          w_off; /* current write head offset */

  但会 是在我本人编译的Android源代码工程中使用,则在后台中运行模拟器:

           return-ENOMEM;

struct logger_log是真正用来保存日志的特性体。

       }

   char       msg[0];/* the entry's payload */

 * struct logger_reader - a logging device open for reading

 *

       return-ENODEV;

       iov++;

 * Example:

   /**

下面我准备从应用开发和源码分析两次责来分析安卓的Logger机制。

 */

   ANDROID_LOG_UNKNOWN =0,

   ssize_t ret =0;

        但会 Android日志系统是以驱动线程池池的形式实现在内核空间的,统统也能获取Android内核源代码来分析。在下载好Android源代码工程中,Logger驱动线程池池主要由另有兩个 文件构成,分别是:

   __u16       __pad; /* no matter what, we get 2 bytes of padding */

while(nr_segs-->0){

   size_t old = log->w_off;

#deinfe KERN_ERR   "<3>"   /* error conditions         */

   }

 * - If there are no log entries to read, blocks until log is written to

 * before using the other macros to change the tag.

 * from a to b cross c?

#endif

#include <linux/ioctl.h>

 判断log->head和所有读者reader的当前读偏移reader->r_off与非 在被覆盖的区域内,但会 是,就也能调用get_next_entry来取得下另有兩个 有效的记录的起始位置来调整当前位置:

   .wq = __WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \

   /**

       if(IS_ERR(misc->this_device)){

       }

}

   while(1){

   struct logger_reader*reader;

#define LOG_PRI(priority, tag, ...) \

       struct logger_entry header;

       structtimespec now;

       now =current_kernel_time();

       header.pid= current->tgid;

       header.tid= current->pid;

       header.sec= now.tv_sec;

       header.nsec= now.tv_nsec;

       header.len= min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);

           return nr;

       /* write out this segment's payload */

       private static final String LOG_TAG ="MY_LOG_TAG";

       file->private_data= reader;

       memcpy(log->buffer, buf + len, count - len);

 * The stuff in the rest of this file should not be used directly.

 * Log macro that allows you to specify a number for priority.

       if(!ret)

   publicstaticint e(String tag, String msg, Throwable tr){

/*

    */

/*

      但会 是内存缓冲区buffer是另有兩个 循环使用的环形缓冲区,给定另有兩个 偏移值,它在buffer中的位置由下logger_offset来选者:

 * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read

                       mutex_unlock(&misc_mtx);

               }

       goto out;

}

   ret = init_log(&log_radio);

#endif

       prepare_to_wait(&log->wq,&wait, TASK_INTERRUPTIBLE);

   size_t len;

 * them above all else.

/*

       return println_native(LOG_ID_MAIN, ERROR, tag, msg);

   ret = get_entry_len(log, reader->r_off);

{

   mutex_unlock(&log->mutex);

        */

      但会 ,但会 要使用C/C++日志接口,倘若定义我本人的LOG_TAG宏和中有 头文件system/core/include/cutils/log.h就都也能了:

static __u32 get_entry_len(struct logger_log *log, size_t off)

   size_t orig = log->w_off;

       .minor = MISC_DYNAMIC_MINOR, \

       if(a< c|| b>= c)

       size_t len;

   publicstaticint d(String tag, String msg){

   }

     从这另有兩个 宏都也能看出,每条日志记录的有效负载长度上加特性体logger_entry的长度也能超过4K个字节。

   wait_queue_head_t   wq;/* wait queue for readers */

                       return-EBUSY;

};

   if(ret)

       return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);

#define LOGGER_GET_NEXT_ENTRY_LEN  _IO(__LOGGERIO, 3) /* next entry len */

 */

   /* is there still something to read or did we race? */

        */

 *

      注释说明了这另有兩个 日志设备的用途。

size_t         size;  /* size of the log */

 为什么在么在要调用fix_up_reader一种生活 函数呢?一种生活 函数又是作什么用的呢?这是但会 日志缓冲区是循环使用的,即旧的日志记录但会 没有 及时读取,而缓冲区的内容又但会 用完时,就也能覆盖旧的记录来容纳新的记录。而这次责将要被覆盖的内容,有但会 是有些reader的下一次责读取的日志所在的位置,以及为新的reader准备的日志以前刚结束了了读取位置head所在的位置。但会 ,也能调整什么位置,使它们也能指向另有兩个 新的有效的位置。让我们歌词 来看一下fix_up_reader函数的实现:

#define LOG(priority, tag, ...) \

   __s32       pid;   /* generating process's pid */

   /* get exactly one entry from the log */

   ANDROID_LOG_INFO,

#if LOG_NDEBUG

{

       return println_native(LOG_ID_MAIN, DEBUG, tag, msg +'\n'+ getStackTraceString(tr));

     以前的代码表示,但会 有新的日志可读,没有 就首先通过get_entry_len来获取下三根可读的日志记录的长度,从这里都也能看出,日志读取线程池池是以日志记录为单位进行读取的,一次只读取三根记录。get_entry_len的函数实现如下:

   (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))

#define _LINUX_LOGGER_H

{

 * logger_read - our log's read() method

       /*

       ssize_t nr;

       #definelogger_offset(n)          ((n) & (log->size -1))

static struct logger_log VAR = { \

 * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than

}

   }

       return0;

                       mutex_unlock(&misc_mtx);

 */

#deinfe KERN_NOTICE "<5>"  /* normal but significant condition */

   if(ret)

#define LOGGER_LOG_MAIN    "log_main"  /* everything else */

#ifndef _LINUX_LOGGER_H

   .open= logger_open,

    * Priority constant for the println method; use Log.v.

      start标号处的while循环是在停留日志可读,但会 但会 没有 新的日志可读了,没有 就要读线程池池就要进入休眠情況,停留新的日志写入后再唤醒,这是通过prepare_waitschedule另有兩个 调用来实现的。

/**

   publicstaticfinalint INFO=4;

   ANDROID_LOG_SILENT,/* only for SetMinPriority(); must be last */

            unsignedlong nr_segs, loff_t ppos)

           mutex_unlock(&log->mutex);

}

你这俩种生活情況是通过判断日志缓冲区的大小和要读取的日志记录在缓冲区中的位置的差值来区别的,但会 相差1,而是明是前一种生活情況了。

C/C++日志接口一般是在编写硬件抽象层模块但会 编写JNI妙招 时使用,而Java接口一般是在应用层编写APP时使用。

 * not need additional reference counting. The structure is protected by the

       break;

   publicstaticint w(String tag, String msg, Throwable tr){

#include <linux/types.h>

   if(unlikely(!header.len))

   struct list_head   list;  /* entry in logger_log's list */

staticint __init init_log(struct logger_log *log)

       root@android:/ # logcat

      接下来,我将首先分析Logger驱动线程池池的相关数据特性,但会 对Logger驱动线程池池源代码的使用情景进行分析,比如日志系统初始化情景、日志读取情景和日志写入情景。

   /**@hide */publicstaticnativeint println_native(int bufID,

   return0;

       reader->r_off= log->head;

 * clock_interval - is a < c < b in mod-space? Put another way, does the line

buffer成员变量是用于保存日志信息的内存缓冲区,它的大小由size成员变量选者。

       structlogger_entry | priority | tag | msg

   log->w_off= logger_offset(log->w_off+ count);

 一种生活 函数简单地调用copy_to_user函数来把占据 内核空间的日志缓冲区指定的内容拷贝到用户空间的内存缓冲区就都也能了,一起,把当前读取日志线程池池的上下文信息中的读偏移r_off前进到下三根日志记录的以前刚结束了了的位置上。

 * bytes after 'off'.

   /**

 */

   struct logger_log*log= file_get_log(iocb->ki_filp);

    */

   publicstaticfinalint VERBOSE=2;

 * - O_NONBLOCK works

   struct logger_log*log= reader->log;

    */

 *     failure.

 */

   list_for_each_entry(reader,&log->readers, list)

       struct logger_reader*reader;

 */

   if(unlikely(log->w_off== reader->r_off)){

 * logger_aio_write - our write method, implementing support for write(),

#define __LOGGERIO 0xAE

msg成员变量记录有效负载的内容,它的大小由len成员变量来选者。

   if(unlikely(ret)){

/*

   }

        */

      里面提到,三根小日志记录是由两大次责组成的,另有兩个 用于描述这条日志记录的特性体struct logger_entry,另另有兩个 是记录体一种生活,即有效负载。特性体structlogger_entry的长度是固定的,倘若知道有效负载的长度,就都也能知道整条日志记录的长度了。而有效负载的长度是记录在特性体struct logger_entry的成员变量len中,而len成员变量的地址与struct logger_entry的地址相同,但会 ,只也能读取记录的以前刚结束了了位置的另有兩个 字节就都也能了。

 * logging macros. You can change this preprocessor definition

       if(a< c&& b>= c)

   }while(count< len);

#deinfe KERN_DEBUG "<7>"   /* debug-level messages        */

       len = min_t(size_t, iov->iov_len, header.len- ret);

   log->w_off= logger_offset(log->w_off+ count);

               misc_minors[misc->minor>>3]|=1<<(misc->minor&7);

 * Fix up any readers, pulling them forward to the first readable

最后,把有效负载的长度val上加struct logger_entry的长度就得到了要读取的日志记录的总长度了。

    * Fix up any readers, pulling them forward to the first readable

ssize_t logger_aio_write(struct kiocb *iocb,conststruct iovec*iov,

 * entries that encroach on readable buffer.

   .poll= logger_poll,

           return-EFAULT;

   if(count!= len)

{

       struct logger_log特性体中用于保存日志信息的内存缓冲区buffer是另有兩个 循环使用的环形缓冲区,缓冲区中保存的内容是以struct logger_entry为单位的,每个单位的组成为:

   len = min(count, log->size- reader->r_off);

/*

struct logger_entry是另有兩个 用于描述三根Log记录的特性体。

fix_up_readers(log,sizeof(struct logger_entry)+ header.len);

      日志驱动线程池池模块的初始化函数为logger_init

       size_t len;

       kernel/common/drivers/staging/android/logger.h

   publicstaticint i(String tag, String msg){

       这俩使用LOGV

{

       struct miscdevice*c;

#define android_printLog(prio, tag, fmt...) \

Android内核是基于Linux Kernel 2.36的,但会 ,Linux KernelLOG机制同样适合于Android内核,这而是与C语言的printf齐名的printk。与printf这俩,printk提供格式化输入功能,一起,它也具有所有LOG机制的特点——提供日志级别过虑功能。

 * This structure lives from module insertion until module removal, so it does

   ANDROID_LOG_DEBUG,

       #defineLOGGER_LOG_RADIO       "log_radio"    /* radio-related messages */

       #define LOGGER_LOG_EVENTS      "log_events" /* system/hardware events */

       #define LOGGER_LOG_MAIN         "log_main"    /* everything else */

   now = current_kernel_time();

                  char __user*buf,

   publicstaticfinalint ASSERT=7;

//KERN_ALERT表示日志级别,里面紧跟着格式化字符串。

secnsec成员变量记录日志写的时间。

start:

       printk(KERN_ERR"logger: failed to register misc "

}

      Android系统中,printk输出的日志信息保占据 /proc/kmsg中,要查看/proc/kmsg的内容,也能在后台中运行模拟器:

       list_add_tail(&reader->list,&log->readers);

   }

Logger机制是在Android系统中提供的另有兩个 轻量级的日志系统,一种生活 日志系统是以驱动线程池池的形式在内核空间实现的,在用户空间分别提供了Java接口和C/C++接口来使用一种生活 日志系统,使用的接口取决于编写的是Android应用线程池池还是系统组件。

   ret = nonseekable_open(inode, file);

      root@android:/# cat  /proc/kmsg

#else

    android_printLog(priority, tag, __VA_ARGS__)

           reader->r_off= get_next_entry(log, reader->r_off, len);

/*

 *

   if(count!= len)

#ifndef LOG

   struct list_head   readers;/* this log's readers */

   struct logger_log*log;

       mutex_unlock(&misc_mtx);

              "device for log '%s'!\n", log->misc.name);

   publicstaticint e(String tag, String msg){

       mutex_unlock(&log->mutex);

       ret += nr;

   .size = SIZE, \

     启动adb shell工具:

   __u16       len;   /* length of the payload */

   return ret;

 * The caller needs to hold log->mutex.

#define KERN_CRIT  "<2>"   /* critical conditions         */

而判断log->head和所有读者reader的当前读偏移reader->r_off与非 在被覆盖的区域内,是通过clock_interval函数来实现的:

} android_LogPriority;

   struct logger_entry header;

#define LOGGER_GET_LOG_BUF_SIZE    _IO(__LOGGERIO, 1) /* size of log */

    * entry after (what will be) the new write offset. We do this now

      USER-NAME@MACHINE-NAME:~/Android$ emulator &

   .buffer = _buf_ ## VAR, \

           break;

   ret = misc_register(&log->misc);

   __s32       sec;   /* seconds since Epoch */

    * Priority constant for the println method; use Log.d.

printk提供了8种日志级别(<linux/kernel.h>):

  但会 ,但会 要使用Java日志接口,倘若在类中定义的LOG_TAG常量和引用android.util.Log就都也能了:

    * Priority constant for the println method; use Log.e.

但会 iov的内容是由用户空间传下来的,也能调用do_write_log_from_user来写入:

    LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)

#define LOGV(...)  ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))

 * The second argument may be NULL or "" to indicate the "global" tag.

   .owner= THIS_MODULE,

   ret = do_read_log_to_user(log, reader, buf, ret);

 *

logger.c文件,注册的写入日志设备文件的妙招 为logger_aio_write

/*

   case1:

   }, \

                       return-EBUSY;

   struct logger_reader*reader= file->private_data;

 * The caller needs to hold log->mutex.

   .read= logger_read,

   publicstaticint i(String tag, String msg, Throwable tr){

 * because if we partially fail, we can end up with clobbered log

};

   if(unlikely(ret))

    * the log, whichever comes first.

out:

       至此,Logger驱动线程池池的主要逻辑就分析完成了,还有其它的有些接口,如logger_poll logger_ioctllogger_release函数,比较简单,就不再分析了。

           return nr;

 */

      接着,通过另有兩个 while循环把iov的内容写入到日志缓冲区中,也而是日志的优先级别priority、日志Tag和日志主体Msg

 *

   /*

              size_t count, loff_t*pos)

system/core/include/android/log.h定义了日志的级别:

   len = min(count, log->size- log->w_off);

#endif

   printk(KERN_INFO"logger: created %luK log '%s'\n",

};

{

   }

   }

           return1;

   return0;

#deinfe KERN_WARNING   "<4>"   /* warning conditions          */

device_initcall(logger_init);

(LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))

................................................

       if(!reader)

   /**

   if(unlikely(ret))

#ifndef LOGV

head成员变量用来表示打开日志文件中,应该从哪另有兩个 位置以前刚结束了了读取日志。

};

 *     The structure passed is linked into the kernel and may not be

     logger.h文件中还定义了其它宏,这里就不一一分析了。

       mutex_unlock(&log->mutex);

static unsigned char _buf_ ## VAR[SIZE]; \

       /* write out this segment's payload */

      kernel/common/drivers/staging/android/logger.c

 *

      接下来,得到了要读取的记录的长度,就调用do_read_log_to_user函数来执行真正的读取动作:

 *     Register a miscellaneous device with the kernel. If the minor

               err = PTR_ERR(misc->this_device);

       }

       return println_native(LOG_ID_MAIN, DEBUG, tag, msg);

       dev_t dev;

 * writev(), and aio_write(). Writes are our fast path, and we try to optimize

#define LOGGER_ENTRY_MAX_PAYLOAD   \

#define LOGGER_LOG_EVENTS  "log_events"    /* system/hardware events */

/*

       reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL);

}

{

   }

       if(misc->minor< DYNAMIC_MINORS)

mutex成员变量是另有兩个 互斥量,用来保护log的并发访问。但会 都也能看出,这里的日志系统的读写疑问图片,我我觉得是另有兩个 生产者-消费者的疑问图片,但会 ,也能互斥量来保护log的并发访问。

   publicstaticfinalint DEBUG=3;

 *

 * Android log priority values, in ascending priority order.

   }

 *

 */

system/core/include/cutils/log.h中,定义了对应的宏。

   header.sec= now.tv_sec;

 * ================================================================

#define KERN_EMERG "<0>"   /* system is unusable          */

注册的日志设备文件操作妙招 为logger_fops

       off = logger_offset(off+ nr);

   mutex_lock(&log->mutex);

   if(b< a){

       size_t nr = get_entry_len(log, off);

      新打开日志设备文件时,是从log->head位置以前刚结束了了读取日志的,保占据 struct logger_reader的成员变量r_off中。

 * mutex 'mutex'.

typedefenum android_LogPriority{

/*

    * Second, we read any remaining bytes, starting back at the head of

       另另有兩个 就都也能看到输出的日志了。

   if(count!= len)

 *

__pad成员变量是用来对齐特性体的。

struct logger_entry {

   if(clock_interval(old, new, log->head))

        LOGV("This is the log printed by LOGV in androiduser space.");

一种生活 次责将简要地介绍一下在Android应用线程池池开发中Log的使用妙招

Android系统在用户空间中提供了轻量级的logger日志系统,它是在内核中实现的一种生活设备驱动,与用户空间的logcat工具配合使用也能方便地跟踪调试线程池池。在Android系统中,分别为C/C++Java语言提供一种生活不同的logger访问接口。

   }

 * This object lives from open to release, so we don't need additional

   .misc = { \

   header.pid= current->tgid;

   }else{

   .aio_write= logger_aio_write,

   header.nsec= now.tv_nsec;

 */

   ANDROID_LOG_WARN,

 * Note how near a no-op this is in the write-only case. Keep it that way!

}

{

/*

   mutex_unlock(&log->mutex);

      logger_init函数通过调用init_log函数来初始化了上述提到的另有兩个 日志设备:

/*

                  size_t count)

   return off;

       return ret;

               while(--i>=0)

 *

   int ret;

但会 logger_entry是内核堆栈空间分配的,直接用memcpy拷贝就都也能了。

   }

 *     A zero is returned on success and a negative errno code for

   return ret;

#define LOGGER_LOG_RADIO   "log_radio" /* radio-related messages */

    * Priority constant for the println method; use Log.i.

   log = get_log_from_minor(MINOR(inode->i_rdev));

       .parent = NULL, \

 * LONG_MAX minus LOGGER_ENTRY_MAX_LEN.

   returnsizeof(struct logger_entry)+ val;

           return-EFAULT;

       memcpy(((char*)&val)+1, log->buffer,1);

       INIT_LIST_HEAD(&misc->list);

分别是log_mainlog_eventslog_radio,名称分别LOGGER_LOG_MAINLOGGER_LOG_EVENTSLOGGER_LOG_RADIO,它们的次设备号为MISC_DYNAMIC_MINOR,即为在注册时动态分配。

   if(len&& copy_from_user(log->buffer+ log->w_off, buf, len))

         输入的参数iocb表示io上下文,iov表示要写入的内容,长度为nr_segs,表示有nr_segs个段的内容要写入。让我们歌词 知道,每个要写入的日志的特性形式为:

   struct logger_log* log;   /* associated log */

}

   switch(log->size- off){

staticint __init logger_init(void)

       if(clock_interval(old, new, reader->r_off))

   size_t new = logger_offset(old+ len);

 * Caller needs to hold log->mutex.

   .mutex = __MUTEX_INITIALIZER(VAR .mutex), \

staticstruct file_operations logger_fops={

DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN,64*1024)

.....................................................

staticvoid do_write_log(struct logger_log *log,constvoid*buf, size_t count)

           break;

       log->head= get_next_entry(log, log->head, len);

   header.tid= current->pid;

        * Add it to the front, so that later devices can "override"

       return println_native(LOG_ID_MAIN, WARN, tag, msg +'\n'+ getStackTraceString(tr));

 * lapped by the writer; also do the same for the default "start head".

   __s32       tid;   /* generating process's tid */

       return ret;

 out:

又但会 日志记录缓冲区是循环使用的,这另有兩个 节字有但会 是第另有兩个 字节存放到缓冲区最后另有兩个 字节,而第兩个字节存放到缓冲区的第另有兩个 节,除此之外,这另有兩个 字节就有连在一起的。但会 ,分一种生活情況来考虑,对于前者,分别通过读取缓冲区最后另有兩个 字节和第另有兩个 字节来得到日志记录的有效负载长度到本地变量val中,对于后者,直接读取连续另有兩个 字节的值到本地变量val中。

       if(file->f_flags& O_NONBLOCK){

   ret = init_log(&log_main);

      Log.e(LOG_TAG, "This is the logprinted by Log.e in android user space.");

   publicstaticint v(String tag, String msg){

#define logger_offset(n)   ((n) & (log->size - 1))

                  struct logger_reader*reader,

#ifndef LOG_TAG

staticint logger_open(struct inode *inode,struct file*file)

 */

   __u16 val;

       if(copy_to_user(buf+ len, log->buffer, count - len))

#endif

                       if((misc_minors[i>>3]&(1<<(i&7)))==0)

   publicstaticint d(String tag, String msg, Throwable tr){

       struct logger_entry | priority | tag | msg

 * We do this by "pulling forward" the readers and start head to the first

        要查看什么LOG的输出,都也能配合logcat工具。但会 是在Eclipse环境(ADT)下运行,直接在Eclipse就都也能查看到:

   size_t len;

           break;

 * Caller must hold log->mutex.

   .unlocked_ioctl= logger_ioctl,

如对应于ANDROID_LOG_VERBOSE的宏LOGV

    * because if we partially fail, we can end up with clobbered log

   if(count< ret){

     接着还定义了另有兩个 宏:

/* logger_offset - returns index 'n' into the log via (optimized) modulus */

   /* get the size of the next entry */

struct logger_reader {

 */

    __android_log_print(prio, tag, fmt)

   /* wake up any blocked readers */

out:

   wake_up_interruptible(&log->wq);

 *

#ifndef LOG_PRI

               if(i<0){

 * Basic log message macro.

    * Priority constant for the println method.

static ssize_t do_read_log_to_user(struct logger_log *log,

 *

       return println_native(LOG_ID_MAIN, WARN, tag, msg);

       if(signal_pending(current)){

    * the log.

   publicstaticint w(String tag, Throwable tr){

   /*

       }

}

   __s32       nsec;  /* nanoseconds */

       启动adb shell工具:

 * buffer is insufficient to hold next entry.

           log->w_off= orig;

 * logger_open - the log's open() file operation

       return ret;

      但会 在这里,还有另有兩个 重要的步骤:

 在线程池池开发过程中,LOG是广泛使用的用来记录线程池池执行过程的机制,它既都也能用于线程池池调试,也都也能用于产品运营中的事件记录。

       nr = do_write_log_from_user(log, iov->iov_base, len);

pidtid成员变量分别用来记录是哪条线程池池写入了这条记录。

               misc->minor= i;

{

 * fix_up_readers - walk the list of all readers and "fix up" any who were

Android系统中的C/C++日志接口是通过宏来使用的。

..................................................................

misc成员变量都也能看出,logger驱动线程池池使用的设备属于misc类型的设备,通过在Android模拟器上执行cat /proc/devices命令,都也能看出,misc类型设备的主设备号是10

   .release= logger_release,

       Log.i(LOG_TAG, "This is the log printed by Log.i inandroid user space.");

       len = min_t(size_t, iov->iov_len, header.len- ret);

   if(file->f_mode& FMODE_READ){

       ret += nr;

 * do_write_log - writes 'len' bytes from 'buf' to 'log'

................................................

但会 没有 新的日志可读,但会 设备文件就有以非阻塞O_NONBLOCK的妙招 打开但会 这时有信号要处里(signal_pending(current)),没有 就直接返回,不再停留新的日志写入。判断当前与与非 新的日志可读的妙招 是:

       if(unlikely(nr<0)){

   return0;

       .fops = &logger_fops, \

       dev = MKDEV(MISC_MAJOR, misc->minor);

 * Behavior:

   /**

       nr = do_write_log_from_user(log, iov->iov_base, len);

len成员变量记录了这条记录的有效负载的长度,有效负载指定的日志记录一种生活的长度,但会 不包括用于描述一种生活 记录的struct logger_entry特性体。让我们歌词 调用android.util.Log接口来使用日志系统时,会指定日志的优先级别PriorityTag字符串以及Msg字符串,Priority + Tag + Msg三者内容的长度加起来而是记录的有效负载长度。

 *     misc_register   -       register a miscellaneous device

   memcpy(log->buffer+ log->w_off, buf, len);

       .name = NAME, \

       reader->log= log;

      ret = (log->w_off == reader->r_off);

   publicstaticint w(String tag, String msg){

     struct logger_reader是用来表示另有兩个 读取日志的线程池池的特性体,log成员变量指向要读取的日志缓冲区。list成员变量用来连接其它读者线程池池。r_off成员变量表示当也能读取的日志在缓冲区中的位置。

        */

}

       if(misc->minor== MISC_DYNAMIC_MINOR){

 *     the minor number requested is used.

 *

               }

   return count;

       goto out;

       INIT_LIST_HEAD(&reader->list);

   reader->r_off= logger_offset(reader->r_off+ count);

static ssize_t do_write_log_from_user(struct logger_log *log,

       return err;

   finish_wait(&log->wq,&wait);

   size_t len;

w_off成员变量用来记录下三根日志应该从哪里以前刚结束了了写。

   struct miscdevice  misc;  /* misc device representing the log */

logger.c文件中定义了另有兩个 日志设备:

        #include <cutils/log.h>

       使用logcat命令查看日志:

static ssize_t logger_read(struct file *file,char __user*buf,

#endif/* _LINUX_LOGGER_H */

       }

printk(KERN_ALERT"This is the log printed by printk in linux kernel space.");

       其中, prioritytagmsg这另有兩个 段的内容是由iov参数从用户空间传递下来的,分别对应iov里面的另有兩个 元素。而logger_entry是由内核空间来构造的:

    * We read from the log in two disjoint operations. First, we read from

          最后,日志写入完毕,还也能唤醒正在停留新日志的reader线程池池:

       return println_native(LOG_ID_MAIN, INFO, tag, msg);

   size_t          head;  /* new readers start here */

 *

logger.h文件中,有这另有兩个 宏的定义:

   /* null writes succeed, return zero */

           ret =-EINTR;

   }

Android系统中,提供了简单、便利的LOG机制,开发人员都也能方便地使用。下面我将介绍在Android内核空间和用户空间中LOG的使用和查看妙招 。

               int i= DYNAMIC_MINORS;

}

   header.len= min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);

        */

 *     destroyed until it has been unregistered.

        #define LOG_TAG "MY LOG TAG"

    * Priority constant for the println method; use Log.w.

   do{

   }

/*

      logger.c文件中,注册的读取日志设备文件的妙招 为logger_read

       USER-NAME@MACHINE-NAME:~/Android$ adb shell

#define LOGV(...)  ((void)0)

   return count;

       ssize_t nr;

           ret =-EAGAIN;

{

DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS,256*1024)

/*

       if(unlikely(nr<0)){

   }else

   if(unlikely(ret))

   ssize_t ret;

/*

       list_for_each_entry(c,&misc_list, list){

 */

 */

           mutex_unlock(&log->mutex);

 * LOG(LOG_WARN, NULL, "Failed with error %d", errno);

static inline int clock_interval(size_t a, size_t b, size_t c)

   publicstaticint v(String tag, String msg, Throwable tr){

 * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which

   publicstaticfinalint WARN=5;

 * get_entry_len - Grabs the length of the payload of the next entry starting