赞
踩
摘要:本节主要来讲解Android10.0 logd、logcat读写日志源码内容
阅读本文大约需要花费20分钟。
文章首发微信公众号:IngresGe
专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!
欢迎关注我的公众号!
[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析
[Android取经之路]系列文章:
《系统启动篇》
《日志系统篇》
《Binder通信原理》
《HwBinder通信原理》
《编译原理》
上一节我们看了Android日志系统的架构分析以及logd、logcat的初始化操作,这一节我们来看看日志系统的读写操作
写日志的过程,主要是通过liblog,把日志写入到/dev/socket/logdw, 守护进程logd监控logdw的写入信息,一旦发现有日志写入后,会把日志存入到LogListener的LogBuffer中。
日志写入调用栈如下:
说明:JAVA层通过jni最终调用到liblog的 __android_log_buf_write(),组装结构体iovec,调用write_to_log()进行日志写入
源码:
- int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
- if (!tag) tag = "";
- ...
- //组装结构体,包括tag、msg
- struct iovec vec[3];
- vec[0].iov_base = (unsigned char*)&prio;
- vec[0].iov_len = 1;
- vec[1].iov_base = (void*)tag;
- vec[1].iov_len = strlen(tag) + 1;
- vec[2].iov_base = (void*)msg;
- vec[2].iov_len = strlen(msg) + 1;
- //进一步调用,进行日志的写入
- return write_to_log(static_cast<log_id_t>(bufID), vec, 3);
- }
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;
说明:write_to_log 开始指向__write_to_log_init(),首先调用__write_to_log_initialize()进行日志配置初始化,再把write_to_log 指向__write_to_log_daemon(),调用__write_to_log_daemon()进行日志写入。
源码:
-
- static 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(),此条件满足
- if (write_to_log == __write_to_log_init) {
- //日志配置初始化,最终调用到logdLoggerWrite来进行日志的配置
- ret = __write_to_log_initialize();
- if (ret < 0) {
- __android_log_unlock(); //解锁log写入进程,避免死锁
- if (!list_empty(&__android_log_persist_write)) {
- __write_to_log_daemon(log_id, vec, nr);
- }
- errno = save_errno;
- return 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);
- errno = save_errno;
- return ret;
- }
[logger_write.cpp] __write_to_log_initialize()
说明:首先进行日志的配置,得到logdLoggerWrite 和pmsgLoggerWrite两个日志节点,包含了open、close、write的函数指针
源码:
-
- static int __write_to_log_initialize() {
- struct android_log_transport_write* transport;
- struct listnode* n;
- int i = 0, ret = 0;
- //日志的配置
- __android_log_config_write();
- write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
- //检查节点是否可用
- __android_log_cache_available(transport);
- ...
- ++ret;
- }
- write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
- //检查节点是否可用
- __android_log_cache_available(transport);
- ...
- ++i;
- }
- if (!ret && !i) {
- return -ENODEV;
- }
-
- return ret;
- }
[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" 中实现
源码:
-
- void __android_log_config_write() {
- if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
- #if (FAKE_LOG_DEVICE == 0)
- extern struct android_log_transport_write logdLoggerWrite;
- extern struct android_log_transport_write pmsgLoggerWrite;
-
- __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite);
- __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
- #else
- extern struct android_log_transport_write fakeLoggerWrite;
-
- __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);
- #endif
- }
-
- if (__android_log_transport & LOGGER_STDERR) {
- extern struct android_log_transport_write stderrLoggerWrite;
-
- if (list_empty(&__android_log_transport_write)) {
- __android_log_add_transport(&__android_log_transport_write, &stderrLoggerWrite);
- } else {
- struct android_log_transport_write* transp;
- write_transport_for_each(transp, &__android_log_transport_write) {
- if (transp == &stderrLoggerWrite) {
- return;
- }
- }
- __android_log_add_transport(&__android_log_persist_write, &stderrLoggerWrite);
- }
- }
- }
[logd_writer.c] logdLoggerWrite
说明:配置logdLoggerWrite的节点,有open、close、write操作,
logdOpen()中连接"/dev/socket/logdw",再通过logdWrite()把日志写入到logdw中
- struct android_log_transport_write logdLoggerWrite = {
- .node = {&logdLoggerWrite.node, &logdLoggerWrite.node},
- .context.sock = -EBADF,
- .name = "logd",
- .available = logdAvailable,
- .open = logdOpen,
- .close = logdClose,
- .write = logdWrite,
- };
[logd_writer.c] logdOpen()
说明:连接"/dev/socket/logdw" socket,记录socket句柄到 logdLoggerWrite.context.sock
- static int logdOpen() {
- int i, ret = 0;
-
- i = atomic_load(&logdLoggerWrite.context.sock);
- if (i < 0) {
- int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
- if (sock < 0) {
- ret = -errno;
- } else {
- struct 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;
- switch (ret) {
- case -ENOTCONN:
- case -ECONNREFUSED:
- case -ENOENT:
- i = atomic_exchange(&logdLoggerWrite.context.sock, ret);
- [[fallthrough]];
- default:
- break;
- }
- close(sock);
- } else {
- ret = atomic_exchange(&logdLoggerWrite.context.sock, sock);
- if ((ret >= 0) && (ret != sock)) {
- close(ret);
- }
- ret = 0;
- }
- }
- }
-
- return ret;
- }
[logd_writer.c] logdWrite()
说明:logdWrite() 通过调用writev,把log非阻塞地写入到socket .
-
- static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
- ssize_t ret;
- int sock;
- static const unsigned headerLength = 1;
- struct iovec newVec[nr + headerLength];
- android_log_header_t header;
- size_t i, payloadSize;
- static atomic_int dropped;
- static atomic_int droppedSecurity;
-
- //原子操作,load logdLoggerWrite.context.sock
- sock = atomic_load(&logdLoggerWrite.context.sock);
- if (sock < 0) switch (sock) {
- case -ENOTCONN:
- case -ECONNREFUSED:
- case -ENOENT:
- break;
- default:
- return -EBADF;
- }
-
- /* logd, after initialization and priv drop */
- if (__android_log_uid() == AID_LOGD) {
- return 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);
-
- if (sock >= 0) {
- int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
- if (snapshot) {
- android_log_event_int_t buffer;
-
- header.id = LOG_ID_SECURITY;
- buffer.header.tag = htole32(LIBLOG_LOG_TAG);
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = htole32(snapshot);
-
- newVec[headerLength].iov_base = &buffer;
- 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, snapshot, memory_order_relaxed);
- }
- }
- snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
- if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
- ANDROID_LOG_VERBOSE)) {
- android_log_event_int_t buffer;
-
- header.id = LOG_ID_EVENTS;
- buffer.header.tag = htole32(LIBLOG_LOG_TAG);
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = htole32(snapshot);
-
- newVec[headerLength].iov_base = &buffer;
- 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(&dropped, snapshot, 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) {
- ++i;
- }
- break;
- }
- }
-
- /*
- * The write below could be lost, but will never block.
- *
- * ENOTCONN occurs if logd has died.
- * ENOENT occurs if logd is not running and socket is missing.
- * ECONNREFUSED occurs if we can not reconnect to logd.
- * EAGAIN occurs if logd is overloaded.
- */
- if (sock < 0) {
- ret = sock;
- } else {
- ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
- if (ret < 0) {
- ret = -errno;
- }
- }
- switch (ret) {
- case -ENOTCONN:
- case -ECONNREFUSED:
- case -ENOENT:
- if (__android_log_trylock()) {
- return ret; /* in a signal handler? try again when less stressed */
- }
- __logdClose(ret);
- ret = logdOpen();
- __android_log_unlock();
-
- if (ret < 0) {
- return ret;
- }
-
- ret = TEMP_FAILURE_RETRY(writev(atomic_load(&logdLoggerWrite.context.sock), newVec, i));
- if (ret < 0) {
- ret = -errno;
- }
- [[fallthrough]];
- default:
- break;
- }
-
- if (ret > (ssize_t)sizeof(header)) {
- ret -= sizeof(header);
- } else if (ret == -EAGAIN) {
- atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
- if (logId == LOG_ID_SECURITY) {
- atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed);
- }
- }
-
- return ret;
- }
[pmsg_writer.c] pmsgLoggerWrite
说明:配置pmsgLoggerWrite的节点,pmsgOpen()打开"/dev/pmsg0",在通过pmsgWrite()把日志写入到"/dev/pmsg0"中
- struct android_log_transport_write pmsgLoggerWrite = {
- .node = {&pmsgLoggerWrite.node, &pmsgLoggerWrite.node},
- .context.fd = -1,
- .name = "pmsg",
- .available = pmsgAvailable,
- .open = pmsgOpen,
- .close = pmsgClose,
- .write = pmsgWrite,
- };
[pmsg_writer.c] pmsgOpen()
说明:打开"/dev/pmsg0"
- static int pmsgOpen() {
- int fd = atomic_load(&pmsgLoggerWrite.context.fd);
- if (fd < 0) {
- int i;
-
- fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
- i = atomic_exchange(&pmsgLoggerWrite.context.fd, fd);
- if ((i >= 0) && (i != fd)) {
- close(i);
- }
- }
-
- return fd;
- }
[pmsg_writer.c] pmsgWrite()
说明:日志写入到"/dev/pmsg0"
-
- static 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<uint8_t*>(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;
- }
在前面,日志配置已经完成,那么接下来调用__write_to_log_daemon()进行日志的真正写入
[logger_write.cpp] __write_to_log_daemon()
说明:最终通过获取logdLoggerWrite 和 pmsgLoggerWrite的write操作,即 调用logdWrite()\pmsgWrite()进行日志的写入
源码:
-
- 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<uint8_t*>(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<const char*>(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;
- }
在前面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进行监听。
- 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的保存。
源码:
- 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<log_mask_t>(1 << logId));
- }
-
- return true;
- }
读取日志时,主要通过logcat工具来抓取。
Logcat是通过liblog 连接 "/dev/socket/logdr" 来获取日志。logcat通过调用liblog 的函数android_logger_list_read()进行日志的读取,最终是连接"/dev/socket/logdr",并读取其中的日志。
logdr的日志由 logd的LogReader 进行写入。
日志读取调用栈如下:
logcat启动后,会调用android_logcat_run_command(),最终进入__logcat()来解析command,并读取日志,我们就从__logcat()来进行分析
说明:__logcat()首先解析传入的command,然后进行日志的读取,如果出现日志读取失败,则退出logcat进程,否则手动启动的logcat进行一直处于运行状态
源码:
- 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_ptr<char, void (*)(void*)> buffers(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 %s\n", 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 %c\n", 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()来进一步分析:
说明:先初始化logger_list,获得logdLoggerRead的节点,在进行transport_context的赋值操作,最终调用logdLoggerRead中的logdRead()进行日志读取
源码:
- 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);
- }
说明:根据logger_list->mode来决定使用哪种日志读取节点,我们大部分情况下使用logdLoggerRead,并进行赋值,主要内容就是得到 logdLoggerRead的节点,为后续日志读取提供服务
源码:
- 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等操作:
- 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,
- };
说明: 调用的logdRead()来读取日志,并组装日志 msg,供logcat进行展示
源码:
- static int android_transport_read(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp,
- struct log_msg* log_msg) {
- // 根据7.4.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;
- }
说明:连接socket "/dev/socket/lodgr",读取logdr 的日志
源码:
- 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.
LogReader被创建后,会调用getLogSocket()来连接。"/dev/socket/logdr"这个socket。
- 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;
- }
在 [7.3.5] 中,当有日志写入logdw时,LogListener()的onDataAvailable生效,先把日志写入LogBufer后,再LogReader的notifyNewLog()来通知有新的日志写入。
当收到一个新的日志条目可用时,通知正在监视此条目的日志id的侦听socket,即我们可以进行读日志了。
- 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);
- ...
- }
- }
[FlushCommand.cpp] runSocketCommand()
说明:对日志读取器套接字上的每个打开的客户端调用一次runSocketCommand。主要有三个command:LogListener、LogAudit、LogKlog。
对日志reader socket上的每个打开一次客户端就调用一次runSocketCommand。
在这里,我们管理并关联reader-client的跟踪和日志区域,锁定logtimeentry的LastLogTimes列表,并产生一个临时的客户端线程来讲数据归档到socket。
全局LogTimeEntry::wrlock()用于保护访问,引用计数用于确保在不受保护时管理单个LogTimeEntry生存期。
源码:
- 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()
[LogReader.cpp] onDataAvailable()
说明:相应日志读操作,先读入logdr中传入的客户端参数,然后把之前LogBuffer中的日志通过flushto,最终通过socket的sendDatav()写给client,比如logcat,所以我们可以开启多个logcat来获取日志。
源码:
-
- 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] = '\0';
-
- // 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;
- }
至此日志的读取操作完成。下一节我们一起来看看<logd中如何进行selinux、kernel日志的读写>
微信公众号:IngresGe
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。