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