Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析-[Android取经之路]

摘要:本节主要来讲解Android10.0 logd、logcat读写日志源码内容

阅读本文大约需要花费20分钟。

文章首发微信公众号:IngresGe

专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!

欢迎关注我的公众号!

[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析

[Android取经之路]系列文章:

《系统启动篇》

  1. Android系统架构
  2. Android是怎么启动的
  3. Android 10.0系统启动之init进程
  4. Android10.0系统启动之Zygote进程
  5. Android 10.0 系统启动之SystemServer进程
  6. Android 10.0 系统服务之ActivityMnagerService
  7. Android10.0系统启动之Launcher(桌面)启动流程
  8. Android10.0应用进程创建过程以及Zygote的fork流程
  9. Android 10.0 PackageManagerService(一)工作原理及启动流程
  10. Android 10.0 PackageManagerService(二)权限扫描
  11. Android 10.0 PackageManagerService(三)APK扫描
  12. Android 10.0 PackageManagerService(四)APK安装流程

《日志系统篇》

  1. Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性
  2. Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
  3. Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析
  4. Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​

《Binder通信原理》

  1. Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
  2. Android10.0 Binder通信原理(二)-Binder入门篇
  3. Android10.0 Binder通信原理(三)-ServiceManager篇
  4. Android10.0 Binder通信原理(四)-Native-C\C++实例分析
  5. Android10.0 Binder通信原理(五)-Binder驱动分析
  6. Android10.0 Binder通信原理(六)-Binder数据如何完成定向打击
  7. Android10.0 Binder通信原理(七)-Framework binder示例
  8. Android10.0 Binder通信原理(八)-Framework层分析
  9. Android10.0 Binder通信原理(九)-AIDL Binder示例
  10. Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub设计模式
  11. Android10.0 Binder通信原理(十一)-Binder总结

  《HwBinder通信原理》

  1. HwBinder入门篇-Android10.0 HwBinder通信原理(一)
  2.  HIDL详解-Android10.0 HwBinder通信原理(二)
  3. HIDL示例-C++服务创建Client验证-Android10.0 HwBinder通信原理(三)
  4. HIDL示例-JAVA服务创建-Client验证-Android10.0 HwBinder通信原理(四)
  5. HwServiceManager篇-Android10.0 HwBinder通信原理(五)
  6. Native层HIDL服务的注册原理-Android10.0 HwBinder通信原理(六)
  7. Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)
  8. JAVA层HIDL服务的注册原理-Android10.0 HwBinder通信原理(八)
  9. JAVA层HIDL服务的获取原理-Android10.0 HwBinder通信原理(九)
  10. HwBinder驱动篇-Android10.0 HwBinder通信原理(十)
  11. HwBinder原理总结-Android10.0 HwBinder通信原理(十一)

《编译原理》

  1. 编译系统入门篇-Android10.0编译系统(一)
  2. 编译环境初始化-Android10.0编译系统(二)
  3. make编译过程-Android10.0编译系统(三)
  4. Image打包流程-Android10.0编译系统(四)
  5. Kati详解-Android10.0编译系统(五)

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

7.3 写日志

    写日志的过程,主要是通过liblog,把日志写入到/dev/socket/logdw, 守护进程logd监控logdw的写入信息,一旦发现有日志写入后,会把日志存入到LogListener的LogBuffer中。

日志写入调用栈如下

7.3.1  [logger_write.cpp] __android_log_buf_write()

说明: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);
}

7.3.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;

7.3.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()进行日志写入。

源码:


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;
}

 

7.3.4 日志配置

[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;
}

7.3.4 日志写入 /dev/socket/logdw

    在前面,日志配置已经完成,那么接下来调用__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;
}

7.3.5 日志写入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进行监听。

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;
}

7.4 读取日志

    读取日志时,主要通过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()来进行分析

7.4.1 [logcat.cpp] __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()来进一步分析:

 

7.4.2 [logger_read.cpp] 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);
}

7.4.3 [logger_read.cpp] init_transport_context()

说明:根据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,
};

 

7.4.4 [logger_read.cpp] android_transport_read()

说明: 调用的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;
}

7.4.5 [logd_reader.cpp] logdRead()

说明:连接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.

    

7.4.6 logd  LogReader

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

  • 9
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值