上一篇文章: Android中Log系统初探(http://blog.csdn.net/zzqhost/article/details/7303136)


以下转载自:http://blog.csdn.net/knock/article/details/5511255


为了调试,必须要将log怎么打印的搞清楚,于是有了以下的分析。


一. 深入跟踪:

我们通常在程序中插入LOGD(..),LOGE(..)之类的语句,但什么情况下可以查看这些打印消息呢?


1.
首先,来到定义处:system/core/include/cutils/log.h,在开头就可以看到
#ifndef LOG_TAG
#define LOG_TAG NULL
#endif
所以程序中#include "log.h"之前要定义LOG_TAG,不然就为空.




2.
再看LOGD的定义
#ifndef LOGD
#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif


跟进
#ifndef LOG
#define LOG(priority, tag, ...) /
LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif


继续
#ifndef LOG_PRI
#define LOG_PRI(priority, tag, ...) /
android_printLog(priority, tag, __VA_ARGS__)
#endif


再跟进
#define android_printLog(prio, tag, fmt...) /
__android_log_print(prio, tag, fmt)




3.
__android_log_print()是位于system/core/liblog/logd_write.c内
int __android_log_print(int prio, const char *tag, const char *fmt, ...)
{
va_list ap;
char buf[LOG_BUF_SIZE];


va_start(ap, fmt);
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
va_end(ap);


return __android_log_write(prio, tag, buf);
}


看__android_log_write()
int __android_log_write(int prio, const char *tag, const char *msg)
{
......


return write_to_log(log_id, vec, 3);
}




4.
write_to_log定义如下
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) =
__write_to_log_init;


查看一下
static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
{
#ifdef HAVE_PTHREADS
pthread_mutex_lock(&log_init_lock);
#endif


if (write_to_log == __write_to_log_init) {
log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);


write_to_log = __write_to_log_kernel;


if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
log_fds[LOG_ID_EVENTS] < 0) {
log_close(log_fds[LOG_ID_MAIN]);
log_close(log_fds[LOG_ID_RADIO]);
log_close(log_fds[LOG_ID_EVENTS]);
log_fds[LOG_ID_MAIN] = -1;
log_fds[LOG_ID_RADIO] = -1;
log_fds[LOG_ID_EVENTS] = -1;
write_to_log = __write_to_log_null;
}
}


#ifdef HAVE_PTHREADS
pthread_mutex_unlock(&log_init_lock);
#endif


return write_to_log(log_id, vec, nr);
}
这段的主要意思是打开/dev/log/main,/dev/log/radio,/dev/log/events三个设备都成功则将write_to_log指向__write_to_log_kernel,否则指向__write_to_log_null。




5.
下面就分别看看这两个
static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr)
{
return -1;
}


static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
{
ssize_t ret;
int log_fd;


if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
log_fd = log_fds[(int)log_id];
} else {
return EBADF;
}


do {
ret = log_writev(log_fd, vec, nr);
} while (ret < 0 && errno == EINTR);


return ret;
}
__write_to_log_null()什么也不做,表示丢弃log信息。__write_to_log_kernel会调用log_writev()将log写进对应的设备(/dev/log/*).








二. 为什么写进init.rc里由init来执行的程序不能输出log呢?下面再来探究一番.



1. system/core/init/init.c中:
void service_start(struct service *svc)函数启动服务,有这么一句
if (needs_console) {
setsid();
open_console();
} else {
zap_stdio();
}


2. 这两个函数为:
static void zap_stdio(void)
{
int fd;
fd = open("/dev/null", O_RDWR);
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
}


static void open_console()
{
int fd;
if ((fd = open(console_name, O_RDWR)) < 0) {
fd = open("/dev/null", O_RDWR);
}
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
}


zap_stdio()比较狠,直接将STDIN,STDOUT,STDERR都干掉了,而open_console()则只是在/dev/console不存在的情况下才干掉STDIN,STDOUT,STDERR,如果/dev/console存在,则将所有输入输出重定向到它。




3. 调用哪个取决于needs_console:
needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
而svc->flags关于SVC_CONSOLE的部分来自于system/core/init/parser.c
static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
case K_console:
svc->flags |= SVC_CONSOLE;
break;


}
这也就是说如果init.rc中service部分有请求console,则可以打印到console。




4. 但怎么样才能打印到系统的log中,可以使用logcat来查看呢?这就需要用到logwrapper。


system/core/logwrapper/logwrapper.c中,logwrapper先打开/dev/ptmx,查询到设备名后
fork()一个子进程并将STDOUT,STDERR定向到查询到的设备。
// redirect stdout and stderr
close(parent_ptty);
dup2(child_ptty, 1);
dup2(child_ptty, 2);
close(child_ptty);
然后开始执行要运行的程序
child(argc - 1, &argv[1]);






三. 总结:

系统中的程序中输出log一般是到/dev/log/下的三个设备中,可以用logcat查看。
对于init运行的程序则有两种方法查看到log信息:
1.添加/system/bin/logwrapper,可以用logcat查看,例如
service /system/bin/logwrapper /system/bin/rild
2.添加console,像sh一样直接输出到console
service console /system/bin/sh
console

更多相关文章

  1. Android 定时重启Android系统源(手机root后可用) [系统支持自定义
  2. Android studio 自定义打包APK名称
  3. android.uid.system Android中如何修改系统时间(应用程序获得系
  4. 如何修改Android应用程序能够使用的默认最大内存值
  5. 仿UCWEB自定义Menu

随机推荐

  1. Android++:为Android(安卓)App开发而生的V
  2. Android(安卓)--- 使用SAX读取xml文件
  3. 创建android文件系统
  4. android系统学习笔记三
  5. Android广播管理二--广播注册(registerRec
  6. 友盟2011第三季度Android数据报告
  7. 【android】:android实现一个加法计算器
  8. android中如何设计触摸屏驱动touch scree
  9. Android(安卓)属性动画详解
  10. Android中定时器的3种实现方法