Android log日志系统分析-logcat源码解析

Android为我们提供了一个十分方便的命令行工具来读日志。Logcat 是一个命令行工具,用于转储系统消息日志,其中包括设备引发错误时的堆叠追踪以及从您的应用。
借用别人的一张图,显示 logd的框架
在这里插入图片描述
在Android 开机时候 在系统启动到init处理的时候,会解析init.rc启动logd service如下

service logd /system/bin/logd
class core
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram 0222 logd logd
group root system

同时会创建和初始化3个socket
logd 接收logcat 传递的指令然后处理 ,比如logcat -g
logdr logcat从此端口读取log
logdw log.d log.e SLOGE 等写log到此端口对应的buffer

Logcat工具源代码位于system/core/logcat目录下,编译后生成的可执行文件位于out/target/product/generic/system/bin目录下。在6.0之前的代码中可能只有一个logcat.cpp.现在是把所有的功能都封装到liblog库中
logcat_main.cpp只是简短的几行代码

# Copyright 2006-2014 The Android Open Source Project

LOCAL_PATH := $(call my-dir)

logcatLibs := liblog libbase libcutils libpcrecpp

include $(CLEAR_VARS)

LOCAL_MODULE := logcat
LOCAL_SRC_FILES := logcat_main.cpp event.logtags
LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
LOCAL_CFLAGS := -Werror

include $(BUILD_EXECUTABLE)

android 8.1 的logcat 源码程序入库函数:

#include <signal.h>
#include <stdlib.h>

#include <log/logcat.h>

int main(int argc, char** argv, char** envp) {
    android_logcat_context ctx = create_android_logcat();
    if (!ctx) return -1;
    signal(SIGPIPE, exit);
    int retval = android_logcat_run_command(ctx, -1, -1, argc, argv, envp);
    int ret = android_logcat_destroy(&ctx);
    if (!ret) ret = retval;
    return ret;
}

获取adb logcat传入的参数并执行,这里我们用 adb logcat -g 演示

android_logcat_run_command_thread{
		return __logcat(context);
}
static int __logcat(android_logcat_context_internal* context) {
	.....
	for(;;){
		static const struct option long_options[] = {
          { "binary",        no_argument,       nullptr, 'B' },
          { "buffer",        required_argument, nullptr, 'b' },
          { "buffer-size",   optional_argument, nullptr, 'g' },
          .....
          }
	}
}

我们执行adb logcat -g 实际是尝在这里插入代码片试获取当前logcat 的buffer大小buffer-size

      switch (ret) {
          

            case 's':
                // default to all silent
                android_log_addFilterRule(context->logformat, "*:s");
                break;
           case 'g':
                if (!optctx.optarg) {
                    getLogSize = true; //设置获取buffer的标志为true
                    break;
                }

我们知道adb logcat 分为 main 和system log。他们是分别从main 和 system 的缓存中取出的

if (!context->devices) {
        dev = context->devices = new log_device_t("main", false);
        context->devCount = 1;
        if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
            dev = dev->next = new log_device_t("system", false);
            context->devCount++;
        }
        if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
            dev = dev->next = new log_device_t("crash", false);
            context->devCount++;
        }
    }

这里新建三个log_device_t结构体,分别存我们从logd读取出的log信息。这里的log_device_t中有一个链表struct logger_list* logger_list 保存 main 、system 、crash等 节点。在源码中病没有找到logger_list定义,但是在代码中发现logger_list会被强制转换成android_log_logger_list。代表logcat 要读取log 模式。

1、读取 pid 
2、-t  count  非阻塞模式读
3、不加任何参数  阻塞模式读
struct android_log_logger_list {
  struct listnode node;
  struct listnode logger;
  struct listnode transport;
  int mode; // 这里有阻塞和非阻塞模式 
  unsigned int tail; 
  log_time start; // 打印从start time 开始 到当前的log
  pid_t pid; //打印指定pid 的log
};

从上面
log_device_t代表一个节点 比如main,log_device_t则是对应一个日志设备文件上下文

struct log_device_t {
    const char* device;  // 目前有 main  system  crash 这三个
    bool binary; 
    struct logger* logger;
    struct logger_list* logger_list;
    bool printed;

    log_device_t* next;

这里是初始化三个 main system 和crash 三个结构体。一般我们执行adb logcat ,
--------- beginning of main
--------- beginning of system
这就是我们三个 缓冲区,保存不同不同的log。 main主要是java层的log 比如 Log.x 系列,system 保存 Slog ,crash就是崩溃log

struct log_msg {
  union {
    unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
    struct logger_entry_v4 entry;
    struct logger_entry_v4 entry_v4;
    struct logger_entry_v3 entry_v3;
    struct logger_entry_v2 entry_v2;
    struct logger_entry entry_v1;
  } __attribute__((aligned(4)));
#ifdef __cplusplus
  /* Matching log_time operators */
  bool operator==(const log_msg& T) const {

代表一条log信息
继续往下执行

dev = context->devices; //依次赋值 main->system->crash  
if (tail_time != log_time::EPOCH) {
    logger_list = android_logger_list_alloc_time(mode, tail_time, pid); //非阻塞模式初始化logger_list
} else {
    logger_list = android_logger_list_alloc(mode, tail_lines, pid); //阻塞模式初始化
}
// We have three orthogonal actions below to clear, set log size and
// get log size. All sharing the same iteration loop.

其中第一种方式以tail_time作为参数,另一种以tail_lines为参数.
对于第一个非阻塞的logger_list初始化:

LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_alloc_time(
    int mode, log_time start, pid_t pid) {
  struct android_log_logger_list* logger_list;

  logger_list = calloc(1, sizeof(*logger_list));//申请logger_list 结构体内存
  if (!logger_list) {
    return NULL; //申请失败返回
  }

  list_init(&logger_list->logger);
  list_init(&logger_list->transport); 
  //分别初始化 logger  transport 
  logger_list->mode = mode; //这里应该是 非阻塞模式
  logger_list->start = start;
  logger_list->pid = pid; //若有参数 -p 这里是 对应要读取的pid

  logger_list_wrlock();
  list_add_tail(&__android_log_readers, &logger_list->node); //将当前链表添加到__android_log_readers 末尾
  logger_list_unlock(); 

  return (struct logger_list*)logger_list;
}

android_logger_list_alloc 大同小异 就不分析了

while (dev) { // 依次执行 main -> system -> crash 
        dev->logger_list = logger_list;  //这里dev也是一个链表依次会连起来
        dev->logger = android_logger_open(logger_list,
                                          android_name_to_log_id(dev->device));
                                         //android_name_to_log_id = 依次为 main  system  crash
LIBLOG_ABI_PUBLIC struct logger* android_logger_open(
    struct logger_list* logger_list, log_id_t logId) {
  struct android_log_logger_list* logger_list_internal =
      (struct android_log_logger_list*)logger_list; //强制logger_list 转换 android_log_logger_list ,这就是为什么找不到 logger_list定义的原因。
  struct android_log_logger* logger;

  if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
    goto err;
  }

  logger_for_each(logger, logger_list_internal) { //一个个遍历 如果存在就不添加了
    if (logger->logId == logId) {
      goto ok;
    }
  }

  logger = calloc(1, sizeof(*logger));
  if (!logger) {
    goto err;
  }

  logger->logId = logId;
  list_add_tail(&logger_list_internal->logger, &logger->node); //通过node组成一个链表
  logger->parent = logger_list_internal; 初始化赋值 

  /* Reset known transports to re-evaluate, we just added one */
  while (!list_empty(&logger_list_internal->transport)) {
    struct listnode* node = list_head(&logger_list_internal->transport);
    struct android_log_transport_context* transp =
        node_to_item(node, struct android_log_transport_context, node);

    list_remove(&transp->node);
    free(transp);
  }
  goto ok;

err:
  logger = NULL;
ok:
  return (struct logger*)logger;
}

struct listnode
{
struct listnode *next;
struct listnode *prev;
};
就是一单纯的 链表 可以 很好的根据链表的地址转化为结构体的地址,以取得结构体中的数据。
可以参考这篇文章
上面一段就是初始化logger_list和 logger 并赋值给 dev 结构体,这样整个logcat初始化基本完成,下面就开始真正的读取log或者控制logd。

  if (setLogSize) {
            if (android_logger_set_log_size(dev->logger, setLogSize)) {//设置buffer大小
                reportErrorName(&setSizeFail, dev->device, allSelected);
            }
        }

        if (getLogSize) {
            long size = android_logger_get_log_size(dev->logger);//获取当前buffer大小
            long readable = android_logger_get_log_readable_size(dev->logger);//获取当前已使用buffer大小

            if ((size < 0) || (readable < 0)) {
                reportErrorName(&getSizeFail, dev->device, allSelected);
            } else {
                std::string str = android::base::StringPrintf(
                       "%s: ring buffer is %ld%sb (%ld%sb consumed),"
                         " max entry is %db, max payload is %db\n",
                       dev->device,
                       value_of_size(size), multiplier_of_size(size),
                       value_of_size(readable), multiplier_of_size(readable),
                       (int)LOGGER_ENTRY_MAX_LEN,
                       (int)LOGGER_ENTRY_MAX_PAYLOAD);
                TEMP_FAILURE_RETRY(write(context->output_fd,
                                         str.data(), str.length()));//输出到文件中
            }
        }

        dev = dev->next;
    }

如果要是获取buffer 大小就会执行

LIBLOG_ABI_PUBLIC long android_logger_get_log_size(struct logger* logger) {
  LOGGER_FUNCTION(logger, -ENODEV, getSize);
}

这里LOGGER_FUNCTION 是一个宏定义函数 ,

#define LOGGER_FUNCTION(logger, def, func, args...)                   \
  ssize_t ret = -EINVAL;                                              \
  struct android_log_transport_context* transp;                       \
  struct android_log_logger* logger_internal =                        \
      (struct android_log_logger*)(logger);                           \
                                                                      \
  if (!logger_internal) {                                             \
    return ret;                                                       \
  }                                                                   \
  ret = init_transport_context(logger_internal->parent);              \
  if (ret < 0) {                                                      \
    return ret;                                                       \
  }                                                                   \
                                                                      \
  ret = (def);                                                        \
  transport_context_for_each(transp, logger_internal->parent) {       \
    if ((transp->logMask & (1 << logger_internal->logId)) &&          \
        transp->transport && transp->transport->func) {               \
      ssize_t retval =                                                \
          (transp->transport->func)(logger_internal, transp, ##args); \
      if ((ret >= 0) || (ret == (def))) {                             \
        ret = retval;                                                 \
      }                                                               \
    }                                                                 \
  }                                                                   \
  return ret

最重要的函数是
init_transport_context

#define LOGGER_FUNCTION(logger, def, func, args...)                   \
  ssize_t ret = -EINVAL;                                              \
  struct android_log_transport_context* transp;                       \
  struct android_log_logger* logger_internal =                        \
      (struct android_log_logger*)(logger);                           \
                                                                      \
  if (!logger_internal) {                                             \
    return ret;                                                       \
  }                                                                   \
  ret = init_transport_context(logger_internal->parent);              \
  //为每个 main  system crash 都设定一个传输通道
  if (ret < 0) {                                                      \
    return ret;                                                       \
  }                                                                   \
                                                                      \
  ret = (def);                                                        \
  transport_context_for_each(transp, logger_internal->parent) {       \
    if ((transp->logMask & (1 << logger_internal->logId)) &&          \
        transp->transport && transp->transport->func) {               \
      ssize_t retval =                                                \
          (transp->transport->func)(logger_internal, transp, ##args); \ // 依次执行 main  system  carsh 对应的func 。如果是 setsize,就依次执行 三次
      if ((ret >= 0) || (ret == (def))) {                             \
        ret = retval;                                                 \
      }                                                               \
    }                                                                 \
  }                                                                   \
  return ret

上面的看不懂只能大致猜测,如果有谁理解很深入可以发给我。

static int init_transport_context(struct android_log_logger_list* logger_list) {
  struct android_log_transport_read* transport;
  struct listnode* node;

  if (!logger_list) {
    return -EINVAL;
  }

  if (list_empty(&logger_list->logger)) {
    return -EINVAL;
  }

  if (!list_empty(&logger_list->transport)) {
    return 0;
  }

  __android_log_lock();
  /* mini __write_to_log_initialize() to populate transports */
  if (list_empty(&__android_log_transport_read) &&
      list_empty(&__android_log_persist_read)) {
    __android_log_config_read(); //这个的意思应该是 把localLoggerRead 对应的 setsize  getSize 等函数一一加入到 链表 __android_log_persist_read 和 __android_log_persist_read中
  }
  __android_log_unlock();

  node = (logger_list->mode & ANDROID_LOG_PSTORE)
             ? &__android_log_persist_read
             : &__android_log_transport_read; 

  read_transport_for_each(transport, node) {
    struct android_log_transport_context* transp;
    struct android_log_logger* logger;
    unsigned logMask = 0;

    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) {
      continue;
    }
    transp = calloc(1, sizeof(*transp)); // 初始化传输上下文,transp 用于传输 main  system  和 crash 等log 。这里的transp 是localLoggerRead  
    if (!transp) {
      return -ENOMEM;
    }
    transp->parent = logger_list;
    transp->transport = transport;
    transp->logMask = logMask;
    transp->ret = 1;
    list_add_tail(&logger_list->transport, &transp->node);
  }
  if (list_empty(&logger_list->transport)) {
    return -ENODEV;
  }
  return 0;
}
LIBLOG_HIDDEN void __android_log_config_read() {
  if (__android_log_transport & LOGGER_LOCAL) {
    extern struct android_log_transport_read localLoggerRead;

    __android_log_add_transport(&__android_log_transport_read, &localLoggerRead);
  }

最后调用到下面的函数
函数写的真是牛,看不懂

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

绕道真远,最终 adb logcat -g 调用的函数是 logdGetSize,adb logcat -G xx 调用的是 logdSetSize
继续查看 logdGetSize

/* returns the total size of the log's ring buffer */
static ssize_t logdGetSize(struct android_log_logger* logger,
                           struct android_log_transport_context* transp __unused) {
  char buf[512];
  
// 写 logd 的socket 命令 "getLogsize "。读取到的buffer值保存到buf中
  ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
  if (ret < 0) {
    return ret;
  }
  printf("logdGetSize==========%s\n",buf);

  if ((buf[0] < '0') || ('9' < buf[0])) {
    return -1;
  }

  return atol(buf);
}

static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg,
                            char* buf, size_t buf_size) {
 
  int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
  获取logd socket端口,文章开始我们已经介绍过logd用于接收logcat传递的指令,然后解析处理

  if (msg) {
    snprintf(buf, buf_size, msg, logger ? logger->logId : (unsigned)-1);
  }

  len = strlen(buf) + 1;
 
  ret = TEMP_FAILURE_RETRY(write(sock, buf, len));//写 getlogsize 到此logd端口中

  while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {//等待处理的返回结果。
    struct pollfd p;


  return ret;
}

同理 logcat 读取log的流程是

 while (!context->stop &&
           (!context->maxCount || (context->printCount < context->maxCount))) {
        struct log_msg log_msg;
        int ret = android_logger_list_read(logger_list, &log_msg);
        if (!ret) {
            logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
            break;
        }

也是调用init_transport_context 函数,不过最终 logcat 读log 调用的函数是 logdRead

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;

  ret = logdOpen(logger_list, transp); //获取logdr 的socket ,不存在就新建
  if (ret < 0) {
    return ret;
  }

  memset(log_msg, 0, sizeof(*log_msg));
//设置定时器,超过2小时就终止logcat输出
  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);
  }

  /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
  ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0); //读取log 保存到log_msg中
  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;
}
  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九霄的爸爸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值