Android LOG系统源码解析(二)
上一篇了解了log系统的启动,这次主要梳理一下写入和读取的流程
Log写入
从上一篇知道logd启动的时候创建了 /dev/socket/logdw,那么写入肯定分两步,一步是往socket里写数据,一步是log服务从socket中读数据,然后处理。
如何往logdw写入数据
从最常用的Log.v来学习这个流程
1
2
3
4
5# framework/base/core/java/android/util/Log.java public static int v(@Nullable String tag, @NonNull String msg) { return println_native(LOG_ID_MAIN, VERBOSE, tag, msg); }
所有的Log.x函数调用的都是println_native 方法,区别只是传入log等级的参数不同。
1
2
3
4
5
6
7
8
9# frameworks/base/core/jni/android_util_Log.cpp static jint android_util_Log_println_native(JNIEnv* env, jobject clazz, jint bufID, jint priority, jstring tagObj, jstring msgObj) { //去除无关代码,核心就是调用__android_log_buf_write int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg); return res; }
__android_log_buf_write来自liblog
1
2
3
4
5
6
7
8
9
10system/core/liblog/logger_write.cpp __android_log_write_log_message() logger_function(log_message); 定义 #ifdef __ANDROID__ static __android_logger_function logger_function = __android_log_logd_logger; #else static __android_logger_function logger_function = __android_log_stderr_logger; #endif
在安卓中调用的就是 __android_log_logd_logger了,在这个函数中只是封装了一下message,然后调用了write_to_log
1
2
3
4
5
6
7
8
9
10
11
12
13
14void __android_log_logd_logger(const struct __android_log_message* log_message) { int buffer_id = log_message->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : log_message->buffer_id; struct iovec vec[3]; vec[0].iov_base = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&log_message->priority)); vec[0].iov_len = 1; vec[1].iov_base = const_cast<void*>(static_cast<const void*>(log_message->tag)); vec[1].iov_len = strlen(log_message->tag) + 1; vec[2].iov_base = const_cast<void*>(static_cast<const void*>(log_message->message)); vec[2].iov_len = strlen(log_message->message) + 1; write_to_log(static_cast<log_id_t>(buffer_id), vec, 3); }
去除一些判断之后write_to_log就是调用了LogdWrite和PmsgWrite。
1
2
3
4
5
6
7
8
9
10
11static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) { int ret; ret = LogdWrite(log_id, &ts, vec, nr); PmsgWrite(log_id, &ts, vec, nr); return ret; }
这两个方法很相似可以一起看,都是打开一个fd然后往里写入数据
1
2
31. LogdWrite是往socket */dev/socket/logdw* 中写入数据,这个socket在创建logd进程时候被创建,并在初始化之后由LogListener监听 2. PmsgWrite是往*/dev/pmsg0* 中写,在内核奔溃时,会使用pstore机制储存log,就存在这里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35//都去除了一些判断和重新封装msg的代码方便查看 #system/logging/liblog/logd_writer.cpp int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) { ssize_t ret; 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; GetSocket();//获取的就是在logd进程打开的时候创建的/dev/socket/logdw ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i)); return ret; } #system/core/liblog/pmsg_writer.cpp 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; GetPmsgFd();//就是"/dev/pmsg0"这个设备 ret = TEMP_FAILURE_RETRY(writev(pmsg_fd, newVec, i)); if (ret < 0) { ret = errno ? -errno : -ENOTCONN; } return ret; }
如何处理logdw数据
在初始化的时候启动了LogListener来监听logdw,它是SocketListener的子类,所以数据处理就看onDataAvailable()
1
2
3
4
5
6
7
8
9
10
11
12
13
14#system/core/logd/LogListener.cpp bool LogListener::onDataAvailable(SocketClient* cli) { ... 通过logbuf->log将消息存在logbuf中 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; }
logbuf是LogBuffer对象,它的作用就是储存log信息
1
2
3
4
5
6
7#system/core/logd/LogBuffer.h typedef std::list<LogBufferElement*> LogBufferElementCollection;//一个储存LogBufferElement的list对象 class LogBuffer { LogBufferElementCollection mLogElements;//LogBuffer中真正储存log的对象 }
在LogBuffer.log方法中就是把msg封装成了LogBufferElement,然后添加到mLogElements中,当log数到了最大值后会有一系列判断来删除多余的log,而不是像以前版本一样简单的循环覆盖。到这里log写入就结束了
Log读取
log一般通过logcat来读取
Logcat
logcat系统启动通过logcatd.rc,也是启动一个服务logcatd,而这个服务唯一的作用就是执行logcat
1
2
3
4
5
6
7#system/core/logcat/logcat.cpp int main(int argc, char** argv) { Logcat logcat; return logcat.Run(argc, argv);//所有的逻辑都在RUN方法里 }
Run方法特别长,里面通过一个switch做了各种参数的处理,但是不管什么处理最后都是要通过这个方法拿到log数据的
int ret = android_logger_list_read(logger_list.get(), &log_msg)
android_logger_list_read是liblog的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33#system/core/liblog/logger_read.cpp //同样只保留了核心的代码 int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) { if (logger_list == nullptr || logger_list->log_mask == 0) { return -EINVAL; } int ret = 0; #ifdef __ANDROID__ if (logger_list->mode & ANDROID_LOG_PSTORE) { ret = PmsgRead(logger_list, log_msg); } else { ret = LogdRead(logger_list, log_msg);//正常使用logcat会进入这个判断,调用的是LogdRead } #endif return ret; } #system/core/liblog/logd_reader.cpp int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg) { int ret = logdOpen(logger_list);//打开了/dev/socket/logdr if (ret < 0) { return ret; } ret = TEMP_FAILURE_RETRY(recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0)); if ((logger_list->mode & ANDROID_LOG_NONBLOCK) && ret == 0) { return -EAGAIN; } if (ret == -1) { return -errno; } return ret; }
到这里,就已经知道了logcat最终就是打开了/dev/socket/logdr,将socket传输过来的数据处理之后write到其他地方。
LogReader
LogReader同样是SocketListener的子类,也是在onDataAvailable中处理连接数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26# system/core/logd/LogReader.cpp onDataAvailable(){ logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli), FlushCommand::hasSecurityLogs(cli), logFindStart.callback, &logFindStart); } #system/core/logd/LogBuffer.cpp log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start, pid_t* lastTid, bool privileged, bool security, int (*filter)(const LogBufferElement* element, void* arg), void* arg) { LogBufferElementCollection::iterator it; for (; it != mLogElements.end(); ++it) {//去掉了一些预处理的代码,这里只会将一部分log循环调用LogBufferElement的flushTo方法 LogBufferElement* element = *it; curr = element->flushTo(reader, this, sameTid);//这方法会将log封装成logger_entry 然后调用reader->sendDatav()也就是往/dev/socket/logdr中写数据。这时logcat就会接受到数据了。 if (curr == element->FLUSH_ERROR) { return curr; } } return curr; }
总结
log系统的分析就结束了,相关的源码涉及三个目录
1
2
3
4system/core/logd //log相关的读写socket创建,logbuff初始化 system/core/logcat //logcat读取log相关 system/core/liblog //log相关操作的api
log流程也就是围绕/dev/socket/logdr和/dev/socket/logdw两个socket的读写和logbuff这个log缓存来展开的
最后
以上就是羞涩汉堡最近收集整理的关于Android LOG系统源码解析(二)的全部内容,更多相关Android内容请搜索靠谱客的其他文章。
发表评论 取消回复