Android Lib层打印log之------bionic库打印log

1. 前提:

    android系统把标准输出stdout从定向到/dev/null 中,所以logcat无法查看printf()打印的log信息。

2. 实际操作:

  1. 使用头文件: # include
  2. 需要打印log使用: __libc_format_log()
  3. 例子:
# include<private/logc_logging.h>  //也可以是 #include "private/libc_logging.h"

xxx_func() {
    /** 
     * int __libc_format_log(int priority, const char* tag, const char* format, ...)
     * 
     * priority : int, 是在private/libc_logging.h定义的log的level,与java层的类似。
     *   参考"/bionic/libc/bionic/private/libc_logging.h" 定义的ANDROID_LOG_DEBUG。
     * tag :    char* , log的Tag信息。
     * format : char* , 格式化的字符串, 
     */
    char * tag = "DroidMage"; 
    char * format = "%s"; 
    char * msg = "This is for test!";
    __libc_format_log(ANDROID_LOG_DEBUG, tag, format, msg);
}

Note: Google好久都没什么好的发现,有一天浏览bionic源码,发现android底层有打印log的方法。(以android源码为根目录): /bionic/libc/private/logc_logging.cpp

3. 跟踪源码:libc_logging.cpp

int __libc_format_log(int priority, const char* tag, const char* format, ...) {
  va_list args;
  // 用来获得可变参数存入args,可变参数的标准用法。
  va_start(args, format); 
  int result = __libc_format_log_va_list(priority, tag, format, args);
  va_end(args);
  return result;
}

Note:点击参看va_start说明。

  • 继续跟踪 __libc_format_log_va_list():
int __libc_format_log_va_list(int priority, const char* tag, const char* format, va_list args) {
  char buffer[1024];
  // BufferOutputStream 是一个c++类,也在libc_logging.cpp中的。可以自行查看。
  BufferOutputStream os(buffer, sizeof(buffer)); 
  // 下面对参数的操作,将参数格式化保存在buffer中。
  out_vformat(os, format, args);
  // 关键: 返回 __libc_write_log()的调用 ,下面继续跟踪。
  return __libc_write_log(priority, tag, buffer);
}
  • 继续跟踪 __libc_write_log():
static int __libc_write_log(int priority, const char* tag, const char* msg) {
#ifdef TARGET_USES_LOGD
  ... // 由于TARGET_USES_LOGD未定义,跳过这里。
#else

  // 这里打开了/dev/log/main文件,也是logcat查看log的地方。
  // TEMP_FAILURE_RETRY不断尝试操作。操作成功返回,或则发生错误返回-1,错误类型保存'errno'
  int main_log_fd = TEMP_FAILURE_RETRY(open("/dev/log/main", O_CLOEXEC | O_WRONLY));

  // main_log_fd 等于-1表示打开文件失败
  if (main_log_fd == -1) {
   // 判断出错类型是不是文件夹不存在的错误,errno是在中定义, 在TEMP_FAILURE_RETRY中使用。
      if (errno == ENOTDIR) { 
      // /dev/log isn't a directory? Maybe we're running on the host? Try stderr instead.
      // __libc_write_stderr()与__libc_write_log()类似,打开/dev/stderr并写入。请自行查看源码。
      return __libc_write_stderr(tag, msg);
    }
  return -1;
  }

  iovec vec[3];
  vec[0].iov_base = &priority;
  vec[0].iov_len = 1;
  vec[1].iov_base = const_cast<char*>(tag);
  vec[1].iov_len = strlen(tag) + 1;
  vec[2].iov_base = const_cast<char*>(msg);
  vec[2].iov_len = strlen(msg) + 1;
#endif
  // 这里就把log信息写入文件了
  int result = TEMP_FAILURE_RETRY(writev(main_log_fd, vec, sizeof(vec) / sizeof(vec[0])));
  close(main_log_fd);
  return result;
}
  • 在 unistd.h 定义了TEMP_FAILURE_RETRY 的宏:
/* Evaluate EXPRESSION, and repeat as long as it returns -1 with 'errno'
   set to EINTR.  */
/* 这里的说明:不断尝试进行操作。 结果:操作成功。或则发生错误返回-1,并把错误类型保存'errno' */
// 具体可以参看博客:http://blog.csdn.net/zhangwu416826/article/details/17438591

# define TEMP_FAILURE_RETRY(expression) \
  (__extension__                                  \
    ({ long int __result;                             \
       do __result = (long int) (expression);                     \
       while (__result == -1L && errno == EINTR);                 \
       __result; }))
#endif

4. 后记:

最初是抱着“Google的工程师是如何调试lib层代码的呢?”的疑问浏览bionic库的。
初来乍到,欢迎拍砖。

你可能感兴趣的:(android)