Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析-[Android取经之路]
阅读本文大约需要花费20分钟。
系列文章:
Android系统启动之init进程(一)-「Android取经之路」
Android系统启动之init进程(二)-「Android取经之路」
Android 10.0系统启动之init进程(三)-「Android取经之路」
Android 10.0系统启动之init进程(四)-「Android取经之路」
Android 10.0系统启动之Zygote进程(一)-「Android取经之路」
Android 10.0系统启动之Zygote进程(二)-「Android取经之路」
Android 10.0系统启动之Zygote进程(三)-「Android取经之路」
Android 10.0系统启动之Zygote进程(四)-「Android取经之路」
Android 10.0系统启动之SystemServer进程(一)-「Android取经之路」
Android 10.0系统启动之SystemServer进程(二)-「Android取经之路」
Android 10.0系统服务之AMS启动流程-「Android取经之路」
Android10.0系统启动之Launcher(桌面)启动流程-[Android取经之路]
Android10.0应用进程创建过程以及Zygote的fork流程-[Android取经之路]
Android 10.0 PackageManagerService(一)工作原理及启动流程-[Android取经之路]
Android 10.0 PackageManagerService(二)权限扫描-[Android取经之路]
Android 10.0 PackageManagerService(三)APK扫描-[Android取经之路]
Android 10.0 PackageManagerService(四)APK安装流程-[Android取经之路]
Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性-[Android取经之路]
Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化-[Android取经之路]
上一节我们看了Android日志系统的架构分析以及logd、logcat的初始化操作,这一节我们来看看日志系统的读写操作
日志系统系列文章:
Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性-[Android取经之路]
Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化-[Android取经之路]
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日志的读写>