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

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

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

文章首发微信公众号:大猫玩程序

专注于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安装流程

 [13]Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性

 [14]Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化

 [15]Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析

 [16]Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​

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

    日志系统系列文章:

 [13]Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性

 [14]Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化

 [15]Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析

 [16]Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​

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日志的读写>

发布了16 篇原创文章 · 获赞 37 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/yiranfeng/article/details/104246174
今日推荐