Android LOG系统源码解析(二)

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缓存来展开的

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值