深入解析Android系统日志管理技巧

更新:11-21 神话故事 我要投稿 纠错 投诉

本文基于Android 10.0源码分析

1.概述

上一节我们看了Android日志系统的架构分析以及logd和logcat的初始化操作。本节我们来看看日志系统的读写操作。

2.写日志

写入日志的过程主要是利用liblog将日志写入到/dev/socket/logdw中。守护进程logd监控logdw的写入信息。一旦发现有日志写入,就会将日志保存到LogListener中。日志缓冲区。

日志写入调用栈如下:

日志系统3-1.PNG 日志系统3-2.PNG

2.1 logger_write.cpp#__android_log_buf_write()

JAVA层最终通过jni调用liblog的__android_log_buf_write(),组装结构体iovec,调用write_to_log()进行日志写入。

//系统/核心/liblog/logger_write.cpp

int __android_log_buf_write(int bufID, int prio, const char* 标签, const char* 消息) {

if (!tag) 标签="";

.

//组装结构体,包括tag、msg

结构iovec vec[3];

vec[0].iov_base=(无符号字符*)prio;

vec[0].iov_len=1;

vec[1].iov_base=(void*)tag;

vec[1].iov_len=strlen(标签) + 1;

vec[2].iov_base=(void*)msg;

vec[2].iov_len=strlen(msg) + 1;

//进一步调用写入日志

返回write_to_log(static_cast(bufID), vec, 3);

}

2.2 logger_write.cpp#write_to_log()

write_to_log 是一个函数指针,默认指向__write_to_log_init()。

static int (*write_to_log)(log_id_t, struct iovec* vec, size_t nr)=__write_to_log_init;

2.3 logger_write.cpp#__write_to_log_init()

write_to_log 开始指向__write_to_log_init(),首先调用__write_to_log_initialize() 初始化日志配置,然后将write_to_log 指向__write_to_log_daemon() ,调用__write_to_log_daemon() 进行日志写入。

//系统/核心/liblog/logger_write.cpp

静态int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) {

int ret, save_errno=errno;

__android_log_lock();

//第一次调用时,write_to_log指向__write_to_log_init(),满足这个条件

如果(write_to_log==__write_to_log_init){

//日志配置初始化,最后调用logdLoggerWrite配置日志

ret=__write_to_log_initialize();

如果(ret 0){

__android_log_unlock(); //解锁日志写入过程,避免死锁

if (!list_empty(__android_log_persist_write)) {

__write_to_log_daemon(log_id, vec, nr);

}

错误号=save_errno;

返回ret;

}

//write_to_log指向__write_to_log_daemon

write_to_log=__write_to_log_daemon;

}

__android_log_unlock();

//通过__write_to_log_daemon写入日志

ret=write_to_log(log_id, vec, nr);

错误号=save_errno;

返回ret;

}

2.4 日志配置

2.4.1 logger_write.cpp#__write_to_log_initialize()

- 首先配置日志,获取两个日志节点:logdLoggerWrite和pmsgLoggerWrite,其中包含open、close和write的函数指针。

//系统/核心/liblog/logger_write.cpp

静态int __write_to_log_initialize() {

结构android_log_transport_write* 传输;

结构列表节点* n;

int i=0,ret=0;

//日志配置

__android_log_config_write();

write_transport_for_each_safe(传输, n, __android_log_transport_write) {

//检查节点是否可用

__android_log_cache_available(传输);

.

++ret;

}

write_transport_for_each_safe(传输, n, __android_log_persist_write) {

//检查节点是否可用

__android_log_cache_available(传输);

.

++我;

}

如果(!ret!i){

返回-ENODEV;

}

返回ret;

}

2.4.2 config_write.cpp#__android_log_config_write()

"struct android_log_transport_write logdLoggerWrite" 和"struct android_log_transport_write pmsgLoggerWrite" 的包装器。它们分别在“/system/core/liblog/logd_writer.c”和“/system/core/liblog/pmsg_writer.c”中实现。

//系统/核心/liblog/config_write.cpp

无效__android_log_config_write() {

if ((__android_log_transport==LOGGER_DE FAULT) || (__android_log_transport LOGGER_LOGD)) {

#if (FAKE_LOG_DEVICE==0)

extern struct android_log_transport_write logdLoggerWrite;

外部结构android_log_transport_write pmsgLoggerWrite;

__android_log_add_transport(__android_log_transport_write, logdLoggerWrite);

__android_log_add_transport(__android_log_persist_write, pmsgLoggerWrite);

别的

外部结构android_log_transport_write fakeLoggerWrite;

__android_log_add_transport(__android_log_transport_write, fakeLoggerWrite);

#endif

}

如果(__android_log_transport LOGGER_STDERR){

外部结构android_log_transport_write stderrLoggerWrite;

如果(list_empty(__android_log_transport_write)){

__android_log_add_transport(__android_log_transport_write, stderrLoggerWrite);

} 别的{

结构android_log_transport_write* transp;

write_transport_for_each(transp, __android_log_transport_write) {

if (transp==stderrLoggerWrite) {

返回;

}

}

__android_log_add_transport(__android_log_persist_write, stderrLoggerWrite);

}

}

}

2.4.3 logd_writer.cpp#logdLoggerWrite

配置logdLoggerWrite的节点,有打开、关闭和写入操作。在logdOpen()中连接"/dev/socket/logdw",然后通过logdWrite()将日志写入logdw。

//系统/核心/liblog/logd_writer.cpp

结构android_log_transport_write logdLoggerWrite={

.node={logdLoggerWrite.node, logdLoggerWrite.node},

.context.sock=-EBADF,

.name="logd",

.available=logdAvailable,

.open=logdOpen,

.close=logdClose,

.write=logdWrite,

};

2.4.4 logd_writer.cpp#logdOpen()

连接到"/dev/socket/logdw"套接字并将套接字句柄记录到logdLoggerWrite.context.sock。

//系统/核心/liblog/logd_writer.cpp

静态int logdOpen() {

int i,ret=0;

i=atomic_load(logdLoggerWrite.context.sock);

如果(我0){

int sock=TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));

如果(袜子0){

ret=-errno;

} 别的{

结构sockaddr_un un;

memset(un, 0, sizeof(struct sockaddr_un));

un.sun_family=AF_UNIX;

strcpy(un.sun_path, "/dev/socket/logdw");

if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)un, sizeof(struct sockaddr_un)))

0) {

ret=-errno;

开关(ret){

案例-ENOTCONN:

案例-ECONNREFUSED:

案例-ENOENT:

i=atomic_exchange(logdLoggerWrite.context.sock, ret);

[[失败]];

默认:

休息;

}

关闭(袜子);

} 别的{

ret=atomic_exchange(logdLoggerWrite.context.sock, 袜子);

if ((ret=0) (ret !=袜子)) {

关闭(ret);

}

ret=0;

}

}

}

返回ret;

}

2.4.5 logd_writer.cpp#logdWrite()

logdWrite()通过调用writev将日志非阻塞地写入套接字。

//系统/核心/liblog/logd_writer.cpp

静态int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {

ssize_t ret;

袜子;

静态常量无符号headerLength=1;

struct iovec newVec[nr + headerLength];

android_log_header_t 标头;

size_t i,有效负载大小;

静态atomic_int 被删除;

静态atomic_int dropSecurity;

//原子操作,加载logdLoggerWrite.context.sock

袜子=atomic_load(logdLoggerWrite.context.sock);

如果(袜子0)开关(袜子){

案例-ENOTCONN:

案例-ECONNREFUSED:

案例-ENOENT:

休息;

默认:

返回-EBADF;

}

/* logd,初始化和priv drop 之后*/

如果(__android_log_uid()==AID_LOGD){

返回0;

}

header.tid=gettid();

header.realtime.tv_sec=ts-tv_sec;

header.realtime.tv_nsec=ts-tv_nsec;

newVec[0].iov_base=(unsigned char*)header;

newVec[0].iov_len=sizeof(header);

如果(袜子=0){

int32_t 快照=atomic_exchange_explicit(droppedSecurity, 0, memory_order_relaxed);

如果(快照){

android_log_event_int_t 缓冲区;

header.id=LOG_ID_SECURITY;

buffer.header.tag=htole32(LIBLOG_LOG_TAG);

buffer.payload.type=EVENT_TYPE_INT;

buffer.payload.data=htole32(快照);

newVec[headerLength].iov_base=缓冲区;

newVec[headerLength].iov_len=sizeof(buffer);

//调用writev将日志写入logdw

ret=TEMP_FAILURE_RETRY(writev(sock, newVec, 2));

if (ret !=(ssize_t)(sizeof(header) + sizeof(buffer))) {

atomic_fetch_add_explicit(droppedSecurity,快照,memory_order_relaxed);

}

}

快照=atomic_exchange_explicit(dropped, 0, memory_order_relaxed);

if (快照__android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),

ANDROID_LOG_VERBOSE)) {

android_log_event_int_t 缓冲区;

header.id=LOG_ID_EVENTS;

buffer.header.tag=htole32(LIBLOG_LOG_TAG);

buffer.payload.type=EVENT_TYPE_INT;

buffer.payload.data=htole32(快照);

newVec[headerLength].iov_base=缓冲区;

newVec[headerLength].iov_len=sizeof(buffer);

ret=TEMP_FAILURE_RETRY(writev(sock, newVec, 2));

if (ret !=(ssize_t)(sizeof(header) + sizeof(buffer))) {

atomic_fetch_add_explicit(删除,快照,memory_order_relaxed);

}

}

}

header.id=logId;

for (payloadSize=0, i=headerLength; i nr + headerLength; i++) {

newVec[i].iov_base=vec[i - headerLength].iov_base;

PayloadSize +=newVec[i].iov_len=vec[i - headerLength].iov_len;

if (payloadSize LOGGER_ENTRY_MAX_PAYLOAD) {

newVec[i].iov_len -=PayloadSize - LOGGER_ENTRY_MAX_PAYLOAD;

if (newVec[i].iov_len) {

++我;

}

休息;

}

}

.

返回ret;

}

2.4.6 pmsg_writer.cpp#pmsgLoggerWrite

配置pmsgLoggerWrite的节点,用pmsgOpen()打开"/dev/pmsg0",并通过pmsgWrite()将日志写入"/dev/pmsg0"。

//系统/核心/liblog/pmsg_writer.cpp

结构android_log_transport_write pmsgLoggerWrite={

.node={pmsgLoggerWrite.node, pmsgLoggerWrite.node},

.context.fd=-1,

.name="pmsg",

.available=pmsgAvailable,

.open=pmsgOpen,

.close=pmsgClose,

.write=pmsgWrite,

};

2.4.7 pmsg_writer.cpp#pmsgOpen()

打开“/dev/pmsg0”。

//系统/核心/liblog/pmsg_writer.cpp

静态int pmsgOpen() {

int fd=atomic_load(pmsgLoggerWrite.context.fd);

如果(fd 0){

整数我;

fd=TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));

i=atomic_exchange(pmsgLoggerWrite.context.fd, fd);

if ((i=0) (i !=fd)) {

关闭(一);

}

}

返回fd;

}

2.4.8 pmsg_writer.cpp#pmsgWrite()

日志写入“/dev/pmsg0”。

//系统/核心/liblog/pmsg_writer.cpp

斯塔

tic int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) { static const unsigned headerLength = 2; struct iovec newVec[nr + headerLength]; android_log_header_t header; android_pmsg_log_header_t pmsgHeader; size_t i, payloadSize; ssize_t ret; if ((logId == LOG_ID_EVENTS) && !__android_log_is_debuggable()) { if (vec[0].iov_len< 4) { return -EINVAL; } if (SNET_EVENT_LOG_TAG != get4LE(static_cast(vec[0].iov_base))) { return -EPERM; } } if (atomic_load(&pmsgLoggerWrite.context.fd)< 0) { return -EBADF; } pmsgHeader.magic = LOGGER_MAGIC; pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header); pmsgHeader.uid = __android_log_uid(); pmsgHeader.pid = getpid(); header.id = logId; header.tid = gettid(); header.realtime.tv_sec = ts->tv_sec; header.realtime.tv_nsec = ts->tv_nsec; newVec[0].iov_base = (unsigned char*)&pmsgHeader; newVec[0].iov_len = sizeof(pmsgHeader); newVec[1].iov_base = (unsigned char*)&header; newVec[1].iov_len = sizeof(header); for (payloadSize = 0, i = headerLength; i< nr + headerLength; i++) { newVec[i].iov_base = vec[i - headerLength].iov_base; payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len; if (payloadSize >LOGGER_ENTRY_MAX_PAYLOAD) { newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD; if (newVec[i].iov_len) { ++i; } payloadSize = LOGGER_ENTRY_MAX_PAYLOAD; break; } } pmsgHeader.len += payloadSize; ret = TEMP_FAILURE_RETRY(writev(atomic_load(&pmsgLoggerWrite.context.fd), newVec, i)); if (ret< 0) { ret = errno ? -errno : -ENOTCONN; } if (ret >(ssize_t)(sizeof(header) + sizeof(pmsgHeader))) { ret -= sizeof(header) - sizeof(pmsgHeader); } return ret; }

2.5 日志写入/dev/socket/logdw

  在前面,日志配置已经完成,那么接下来调用__write_to_log_daemon()进行日志的真正写入。

2.5.1 logger_write.cpp#__write_to_log_daemon()

  最终通过获取logdLoggerWrite和pmsgLoggerWrite的write操作,即调用logdWrite()/pmsgWrite()进行日志的写入。 // system/core/liblog/logd_writer.cpp

static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) { struct android_log_transport_write* node; int ret, save_errno; struct timespec ts; size_t len, i; for (len = i = 0; i< nr; ++i) { len += vec[i].iov_len; } if (!len) { return -EINVAL; } save_errno = errno; #if defined(__ANDROID__) clock_gettime(android_log_clockid(), &ts); if (log_id == LOG_ID_SECURITY) { if (vec[0].iov_len< 4) { errno = save_errno; return -EINVAL; } ret = check_log_uid_permissions(); if (ret< 0) { errno = save_errno; return ret; } if (!__android_log_security()) { /* If only we could reset downstream logd counter */ errno = save_errno; return -EPERM; } } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) { const char* tag; size_t len; EventTagMap *m, *f; if (vec[0].iov_len< 4) { errno = save_errno; return -EINVAL; } tag = NULL; len = 0; f = NULL; m = (EventTagMap*)atomic_load(&tagMap); if (!m) { ret = __android_log_trylock(); m = (EventTagMap*)atomic_load(&tagMap); /* trylock flush cache */ if (!m) { m = android_openEventTagMap(NULL); if (ret) { /* trylock failed, use local copy, mark for close */ f = m; } else { if (!m) { /* One chance to open map file */ m = (EventTagMap*)(uintptr_t)-1LL; } atomic_store(&tagMap, (uintptr_t)m); } } if (!ret) { /* trylock succeeded, unlock */ __android_log_unlock(); } } if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) { tag = android_lookupEventTag_len(m, &len, get4LE(static_cast(vec[0].iov_base))); } ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len, ANDROID_LOG_VERBOSE); if (f) { /* local copy marked for close */ android_closeEventTagMap(f); } if (!ret) { errno = save_errno; return -EPERM; } } else { /* Validate the incoming tag, tag content can not split across iovec */ char prio = ANDROID_LOG_VERBOSE; const char* tag = static_cast(vec[0].iov_base); size_t len = vec[0].iov_len; if (!tag) { len = 0; } if (len >0) { prio = *tag; if (len >1) { --len; ++tag; } else { len = vec[1].iov_len; tag = ((const char*)vec[1].iov_base); if (!tag) { len = 0; } } } /* tag must be nul terminated */ if (tag && strnlen(tag, len) >= len) { tag = NULL; } if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) { errno = save_errno; return -EPERM; } } #else /* simulate clock_gettime(CLOCK_REALTIME, &ts); */ { struct timeval tv; gettimeofday(&tv, NULL); ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000; } #endif ret = 0; i = 1<< log_id; write_transport_for_each(node, &__android_log_transport_write) { if (node->logMask & i) { ssize_t retval; //从logdLoggerWrite中拿到write操作,即logdWrite()进行日志的写入 retval = (*node->write)(log_id, &ts, vec, nr); if (ret >= 0) { ret = retval; } } } write_transport_for_each(node, &__android_log_persist_write) { if (node->logMask & i) { //从pmsgLoggerWrite中拿到write操作,即pmsgWrite()进行日志的写入 (void)(*node->write)(log_id, &ts, vec, nr); } } errno = save_errno; return ret; }

2.6 日志写入LogListener的LogBuffer

  在前面logd初始化时,我们能够看到LogListener中会先创建一个LogBuffer,LogBuffer在初始化init()中会初始化各个log的域的大小(如main、crash、system等默认为256k),接着会启动一个SocketListener,对/dev/socket/logdw进行监听。 // system/core/logd/main.cpp int main(int argc, char* argv[]) { ... LogListener* swl = new LogListener(logBuf, reader); if (swl->startListener(600)) { return EXIT_FAILURE; } ... }LogListene的对象创建时,会先调用getLogSocket(),对logdw进行监听。 // system/core/logd/LogListener.cpp int LogListener::getLogSocket() { static const char socketName[] = "logdw"; int sock = android_get_control_socket(socketName); if (sock< 0) { // logd started up in init.sh sock = socket_local_server( socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM); int on = 1; if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { return -1; } } return sock; }在startListener函数中创建线程,线程注册函数为SocketListener::threadStart; 执行runListener函数,如果socket监听到数据,则执行onDataAvailable函数进行处理; 调用logbuf->log(LogBuffer::log),这个函数很重要,新建一个LogBufferElement对象(用于保存log),调用mLogElements.insert将LogBufferElement加入list容器,实现log的保存。 // system/core/logd/LogListener.cpp bool LogListener::onDataAvailable(SocketClient* cli) { static bool name_set; if (!name_set) { prctl(PR_SET_NAME, "logd.writer"); name_set = true; } // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) + LOGGER_ENTRY_MAX_PAYLOAD + 1]; struct iovec iov = { buffer, sizeof(buffer) - 1 }; //将日志写入list容器 int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg, ((size_t)n<= UINT16_MAX) ? (uint16_t)n : UINT16_MAX); if (res >0) { reader->notifyNewLog(static_cast(1<< logId)); } return true; }

3.读取日志

  读取日志时,主要通过logcat工具来抓取。Logcat是通过liblog连接 "/dev/socket/logdr" 来获取日志。logcat通过调用liblog的函数android_logger_list_read()进行日志的读取,最终是连接"/dev/socket/logdr",并读取其中的日志。logdr的日志由logd的LogReader 进行写入。   日志读取调用栈如下: 日志系统3-3.pnglogcat启动后,会调用android_logcat_run_command(),最终进入__logcat()来解析command,并读取日志,我们就从__logcat()来进行分析。 日志系统3-4.png

3.1 logcat.cpp#__logcat()

  __logcat()首先解析传入的command,然后进行日志的读取,如果出现日志读取失败,则退出logcat进程,否则手动启动的logcat进行一直处于运行状态。 // system/core/logcat/logcat.cpp static int __logcat(android_logcat_context_internal* context) { ... struct logger_list* logger_list; ... //logcat command的解析 while (true) { int c = getopt_long(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options, &option_index); switch (c) { ... case "g": if (!optarg) { getLogSize = true; break; } FALLTHROUGH_INTENDED; case "b": { std::unique_ptrbuffers(strdup(optarg), free); char* arg = buffers.get(); unsigned idMask = 0; char* sv = nullptr; // protect against -ENOMEM above while (!!(arg = strtok_r(arg, delimiters, &sv))) { if (!strcmp(arg, "default")) { idMask |= (1<< LOG_ID_MAIN) | (1<< LOG_ID_SYSTEM) | (1<< LOG_ID_CRASH); } else if (!strcmp(arg, "all")) { allSelected = true; idMask = (unsigned)-1; } else { log_id_t log_id = android_name_to_log_id(arg); const char* name = android_log_id_to_name(log_id); if (!!strcmp(name, arg)) { logcat_panic(context, HELP_TRUE, "unknown buffer %sn", arg); goto exit; } if (log_id == LOG_ID_SECURITY) allSelected = false; idMask |= (1<< log_id); } arg = nullptr; } for (int i = LOG_ID_MIN; i< LOG_ID_MAX; ++i) { const char* name = android_log_id_to_name((log_id_t)i); log_id_t log_id = android_name_to_log_id(name); if (log_id != (log_id_t)i) continue; if (!(idMask & (1<< i))) continue; bool found = false; for (dev = context->devices; dev; dev = dev->next) { if (!strcmp(name, dev->device)) { found = true; break; } if (!dev->next) break; } if (found) continue; bool binary = !strcmp(name, "events") || !strcmp(name, "security") || !strcmp(name, "stats"); log_device_t* d = new log_device_t(name, binary); if (dev) { dev->next = d; dev = d; } else { context->devices = dev = d; } context->devCount++; } } ... case "h": show_help(context); show_format_help(context); goto exit; default: logcat_panic(context, HELP_TRUE, "Unrecognized Option %cn", optopt); goto exit; } } ... while (!context->stop && (!context->maxCount || (context->printCount< context->maxCount))) { struct log_msg log_msg; //调用liblog的接口,读取"/dev/socket/logdr"来读取日志 int ret = android_logger_list_read(logger_list, &log_msg); if (!ret) { logcat_panic(context, HELP_FALSE, "read: unexpected EOF!n"); break; } ... } close: // Short and sweet. Implemented generic version in android_logcat_destroy. while (!!(dev = context->devices)) { context->devices = dev->next; delete dev; } android_logger_list_free(logger_list); exit: // close write end of pipe to help things along if (context->output_fd == context->fds[1]) { android::close_output(context); } if (context->error_fd == context->fds[1]) { android::close_error(context); } if (context->fds[1] >= 0) {

// NB: should be closed by the above int save_errno = errno; close(context->fds[1]); errno = save_errno; context->fds[1] = -1; } context->thread_stopped = true; return context->retval; }下面进入liblog的android_logger_list_read()来进一步分析。

3.2 logger_read.cpp#android_logger_list_read()

  先初始化logger_list,获得logdLoggerRead的节点,在进行transport_context的赋值操作,最终调用logdLoggerRead中的logdRead()进行日志读取。 // system/core/liblog/logger_read.cpp int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) { struct android_log_logger_list* logger_list_internal = (struct android_log_logger_list*)logger_list; //1. 初始化logger_list,把logdLoggerRead 和 pmsgLoggerRead 两个节点按需传给transoprt,我们一般使用 logdLoggerRead int ret = init_transport_context(logger_list_internal); if (ret< 0) { return ret; } //2. transoport的赋值 android_log_transport_context* transport_context = &logger_list_internal->transport_context; //3.通过调用 logger_list中read方法,即logdLoggerRead中的logdRead()进行日志读取 return android_transport_read(logger_list_internal, transport_context, log_msg); }

3.3 logger_read.cpp#init_transport_context()

  根据logger_list->mode来决定使用哪种日志读取节点,我们大部分情况下使用logdLoggerRead,并进行赋值,主要内容就是得到logdLoggerRead的节点,为后续日志读取提供服务。 // system/core/liblog/logger_read.cpp static int init_transport_context(struct android_log_logger_list* logger_list) { if (!logger_list) { return -EINVAL; } if (list_empty(&logger_list->logger)) { return -EINVAL; } if (logger_list->transport_initialized) { return 0; } #if (FAKE_LOG_DEVICE == 0) extern struct android_log_transport_read logdLoggerRead; extern struct android_log_transport_read pmsgLoggerRead; struct android_log_transport_read* transport; //根据logger_list->mode来决定使用哪种日志读取节点,我们大部分情况下使用logdLoggerRead transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead; struct android_log_logger* logger; unsigned logMask = 0; //logger的校验 logger_for_each(logger, logger_list) { log_id_t logId = logger->logId; if (logId == LOG_ID_SECURITY && __android_log_uid() != AID_SYSTEM) { continue; } if (transport->read && (!transport->available || transport->available(logId) >= 0)) { logMask |= 1<< logId; } } if (!logMask) { return -ENODEV; } //节点赋值 logger_list->transport_context.transport = transport; logger_list->transport_context.logMask = logMask; logger_list->transport_context.ret = 1; #endif return 0; }logdLoggerRead中提供了read、poll、close等操作: // system/core/liblog/logd_reader.cpp struct android_log_transport_read logdLoggerRead = { .node = {&logdLoggerRead.node, &logdLoggerRead.node}, .name = "logd", .available = logdAvailable, .version = logdVersion, .read = logdRead, .poll = logdPoll, .close = logdClose, .clear = logdClear, .getSize = logdGetSize, .setSize = logdSetSize, .getReadableSize = logdGetReadableSize, .getPrune = logdGetPrune, .setPrune = logdSetPrune, .getStats = logdGetStats, };

3.4 logger_read.cpp#android_transport_read()

  调用的logdRead()来读取日志,并组装日志msg,供logcat进行展示。 // system/core/liblog/logd_reader.cpp static int android_transport_read(struct android_log_logger_list* logger_list, struct android_log_transport_context* transp, struct log_msg* log_msg) { // 根据3.3克制最终调用的是logdRead()来读取日志 int ret = (*transp->transport->read)(logger_list, transp, log_msg); if (ret >(int)sizeof(*log_msg)) { ret = sizeof(*log_msg); } transp->ret = ret; /* propagate errors, or make sure len & hdr_size members visible */ if (ret< (int)(sizeof(log_msg->entry.len) + sizeof(log_msg->entry.hdr_size))) { if (ret >= (int)sizeof(log_msg->entry.len)) { log_msg->entry.len = 0; } return ret; } /* hdr_size correction (logger_entry ->logger_entry_v2+ conversion) */ if (log_msg->entry_v2.hdr_size == 0) { log_msg->entry_v2.hdr_size = sizeof(struct logger_entry); } if ((log_msg->entry_v2.hdr_size< sizeof(log_msg->entry_v1)) || (log_msg->entry_v2.hdr_size >sizeof(log_msg->entry))) { return -EINVAL; } /* len validation */ if (ret<= log_msg->entry_v2.hdr_size) { log_msg->entry.len = 0; } else { log_msg->entry.len = ret - log_msg->entry_v2.hdr_size; } return ret; }

3.5 logd_reader.cpp#logdRead()

  连接socket "/dev/socket/lodgr",读取logdr的日志。 // system/core/liblog/logd_reader.cpp static int logdRead(struct android_log_logger_list* logger_list, struct android_log_transport_context* transp, struct log_msg* log_msg) { int ret, e; struct sigaction ignore; struct sigaction old_sigaction; unsigned int old_alarm = 0; //连接socket "/dev/socket/lodgr" ret = logdOpen(logger_list, transp); if (ret< 0) { return ret; } memset(log_msg, 0, sizeof(*log_msg)); unsigned int new_alarm = 0; if (logger_list->mode & ANDROID_LOG_NONBLOCK) { if ((logger_list->mode & ANDROID_LOG_WRAP) && (logger_list->start.tv_sec || logger_list->start.tv_nsec)) { /* b/64143705 */ new_alarm = (ANDROID_LOG_WRAP_DEFAULT_TIMEOUT * 11) / 10 + 10; logger_list->mode &= ~ANDROID_LOG_WRAP; } else { new_alarm = 30; } memset(&ignore, 0, sizeof(ignore)); ignore.sa_handler = caught_signal; sigemptyset(&ignore.sa_mask); /* particularily useful if tombstone is reporting for logd */ sigaction(SIGALRM, &ignore, &old_sigaction); old_alarm = alarm(new_alarm); } //读取logdr的日志 ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0); e = errno; if (new_alarm) { if ((ret == 0) || (e == EINTR)) { e = EAGAIN; ret = -1; } alarm(old_alarm); sigaction(SIGALRM, &old_sigaction, NULL); } if ((ret == -1) && e) { return -e; } return ret; }至此,我们知道了logcat读取日志的流程,但是logdr的日志是由谁写入的呢,让我们接下来继续分析。在前面logd的初始化时,我们注意到创建了一个LogReader对象,其中存储了一个LogBuffer。

3.6 logd LogReader

日志系统3-5.pngLogReader被创建后,会调用getLogSocket()来连接"/dev/socket/logdr"这个socket。 // system/core/logd/LogReader.cpp int LogReader::getLogSocket() { static const char socketName[] = "logdr"; int sock = android_get_control_socket(socketName); if (sock< 0) { sock = socket_local_server( socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET); } return sock; }在[2.6]中,当有日志写入logdw时,LogListener()的onDataAvailable生效,先把日志写入LogBufer后,再LogReader的notifyNewLog()来通知有新的日志写入。   当收到一个新的日志条目可用时,通知正在监视此条目的日志id的侦听socket,即我们可以进行读日志了。 // system/core/logd/LogReader.cpp void LogReader::notifyNewLog(log_mask_t logMask) { // 创建一个FlushCommand 对象,传入LogReader的对象和logmask FlushCommand command(*this, logMask); //调用socket接口,最终进入 logd的runSocketCommand() runOnEachSocket(&command); } // 运行FlushCommand 的runSocketCommand() void SocketListener::runOnEachSocket(SocketClientCommand *command) { SocketClientCollection safeList; ... while (!safeList.empty()) { ... command->runSocketCommand(c); ... } }

3.6.1 FlushCommand.cpp#runSocketCommand()

  对日志读取器套接字上的每个打开的客户端调用一次runSocketCommand。主要有三个command:LogListener、LogAudit、LogKlog。   对日志reader socket上的每个打开一次客户端就调用一次runSocketCommand。在这里,我们管理并关联reader-client的跟踪和日志区域,锁定logtimeentry的LastLogTimes列表,并产生一个临时的客户端线程来讲数据归档到socket。   全局LogTimeEntry::wrlock()用于保护访问,引用计数用于确保在不受保护时管理单个LogTimeEntry生存期。 // system/core/logd/FlushCommand.cpp void FlushCommand::runSocketCommand(SocketClient* client) { LogTimeEntry* entry = nullptr; LastLogTimes& times = mReader.logbuf().mTimes; LogTimeEntry::wrlock(); LastLogTimes::iterator it = times.begin(); while (it != times.end()) { entry = it->get(); if (entry->mClient == client) { if (!entry->isWatchingMultiple(mLogMask)) { LogTimeEntry::unlock(); return; } if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) { if (mReader.logbuf().isMonotonic()) { LogTimeEntry::unlock(); return; } // If the user changes the time in a gross manner that // invalidates the timeout, fall through and trigger. log_time now(CLOCK_REALTIME); if (((entry->mEnd + entry->mTimeout) >now) && (now >entry->mEnd)) { LogTimeEntry::unlock(); return; } } entry->triggerReader_Locked(); LogTimeEntry::unlock(); return; } it++; } LogTimeEntry::unlock(); }如果socket监听到数据,则执行onDataAvailable(),进入LogReader的onDataAvailable()。

3.6.2 LogReader.cpp#onDataAvailable()

  相应日志读操作,先读入logdr中传入的客户端参数,然后把之前LogBuffer中的日志通过flushto,最终通过socket的sendDatav()写给client,比如logcat,所以我们可以开启多个logcat来获取日志。 // system/core/logd/LogReader.cpp bool LogReader::onDataAvailable(SocketClient* cli) { static bool name_set; //响应日志读操作 if (!name_set) { prctl(PR_SET_NAME, "logd.reader"); name_set = true; } char buffer[255]; //读取客户端传入参数 int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1); if (len<= 0) { doSocketDelete(cli); return false; } buffer[len] = ""; // Clients are only allowed to send one command, disconnect them if they // send another. //客户端只允许发送一个命令,如果它们发送另一个命令,则断开它们的连接 LogTimeEntry::wrlock(); for (const auto& entry : mLogbuf.mTimes) { if (entry->mClient == cli) { entry->release_Locked(); LogTimeEntry::unlock(); return false; } } LogTimeEntry::unlock(); ... log_time sequence = start; ... if (nonBlock && (sequence != log_time::EPOCH) && timeout) { .. //LogBuffer中的日志写入传入的client,即写入logcat logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli), FlushCommand::hasSecurityLogs(cli), logFindStart.callback, &logFindStart); if (!logFindStart.found()) { doSocketDelete(cli); return false; } } ... LogTimeEntry::wrlock(); ... setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t, sizeof(t)); LogTimeEntry::unlock(); return true;

用户评论

岁岁年年

终于了解了为啥我的手机App总是卡顿,原来是日志记录太多啦!

    有15位网友表示赞同!

最迷人的危险

想深入了解APP运行原理,看来要学习一下这Android 日志系统了。

    有20位网友表示赞同!

为爱放弃

高效的日志系统能提高开发效率,还能帮助用户更快定位问题哦。

    有6位网友表示赞同!

(り。薆情海

是不是还有很多小伙伴不知道怎么看Android的日志呢?

    有12位网友表示赞同!

短发

希望新的Android日志系统更完善,功能更强大!

    有11位网友表示赞同!

琴断朱弦

这安卓日志系统真是太重要了,可以帮我更快找到Bug啊!

    有15位网友表示赞同!

青墨断笺み

以后遇到App问题就能自己去检查日志记录啦!真方便!

    有17位网友表示赞同!

ˉ夨落旳尐孩。

学习Android开发的话一定得了解日志系统,这是基础知识啊。

    有19位网友表示赞同!

孤者何惧

看这个标题我感觉很有深度啊,想深入研究一下它有哪些特点。

    有6位网友表示赞同!

麝香味

这篇文章应该会告诉我们如何使用Android 日志系统吧?期待它!

    有19位网友表示赞同!

日久见人心

Android 日志记录量很大,不知道新系统能做到高效管理吗?

    有17位网友表示赞同!

墨染天下

最近在开发一个安卓应用,日志记录真是个难题啊!希望这篇就能解决我的困扰。

    有7位网友表示赞同!

一样剩余

想知道Android 日志系统有哪些改进呢?看看这篇文章就知道了!

    有20位网友表示赞同!

情字何解ヘ

使用Android 开发环境的开发者们,一定要了解这个日志系统啊!

    有18位网友表示赞同!

优雅的叶子

这篇文章应该会介绍一些实用的工具和技巧,让我们更好地使用Android 日志系统。

    有11位网友表示赞同!

情深至命

想尝试一下新版本的Android 日志系统,期待它给我带来的惊喜!

    有8位网友表示赞同!

全网暗恋者

我曾经遇到过因为日志问题导致APP卡顿的情况,真是太不愉快了!希望这篇能让我避免这种状况!

    有6位网友表示赞同!

拥抱

学习开发安卓App,这次一定要好好看看这个日志系统的介绍!

    有8位网友表示赞同!

江山策

Android日誌系統是一个重要的工具,需要深入了解它的功能和应用场景。

    有6位网友表示赞同!

【深入解析Android系统日志管理技巧】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活

上一篇:探索24种生活方式与习惯 下一篇:中国传统二十四节气:辛丑年处暑第二候,公历2021年8月24日星期二