概述
Android LOG系统源码解析(二)
上一篇了解了log系统的启动,这次主要梳理一下写入和读取的流程
Log写入
从上一篇知道logd启动的时候创建了 /dev/socket/logdw,那么写入肯定分两步,一步是往socket里写数据,一步是log服务从socket中读数据,然后处理。
如何往logdw写入数据
从最常用的Log.v来学习这个流程
# 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等级的参数不同。
# 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
system/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
void __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。
static 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. LogdWrite是往socket */dev/socket/logdw* 中写入数据,这个socket在创建logd进程时候被创建,并在初始化之后由LogListener监听
2. PmsgWrite是往*/dev/pmsg0* 中写,在内核奔溃时,会使用pstore机制储存log,就存在这里
//都去除了一些判断和重新封装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()
#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信息
#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
#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的接口
#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中处理连接数据
# 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系统的分析就结束了,相关的源码涉及三个目录
system/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 LOG系统源码解析(二)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复