其他
Android帝国之日志系统--logd、logcat
https://mp.weixin.qq.com/s/8LrY0OKcglBfIBjUfavvPw
//logd是进程的名字,/system/bin/logd 代表当init进程fork logd成功后,需要执行的可执行文件
service logd /system/bin/logd
//下面三个socket分别代表需要创建的三个server socket
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram+passcred 0222 logd logd
//kmsg代表内核会把内核的日志信息存储在这个文件中
file /proc/kmsg r
file /dev/kmsg w
user logd
group logd system package_info readproc
capabilities SYSLOG AUDIT_CONTROL
priority 10
task_profiles ServiceCapacityLow
onrestart setprop logd.ready false
省略其他信息......
//文件路径:/system/core/rootdir/init.rc,下面的内容是该文件的其中一部分
//on init:代表init触发器触发的时候会执行下面的各种命令
on init
省略其他命令......
//start logd:start命令会创建进程,logd与上面logd.rc中service后面的logd一致
start logd
int main(int argc, char* argv[]) {
// We want EPIPE when a reader disconnects, not to terminate logd.
signal(SIGPIPE, SIG_IGN);
省略其他代码.....
return EXIT_SUCCESS;
}
日志生产者:进程中的各线程调用相应的方法把需要打印的日志放入日志队列 日志队列:主要用来存放日志,是按日志存放的先后顺序存放在队列中,需要做好同步处理 日志消费者:显示日志的模块从日志队列中把日志按时间先后顺序取出来,进行显示
日志生产者:日志的生产者是其他进程,通过进程通信的方式把日志传递到日志收集分发中心 日志收集分发中心:位于logd进程内,其中日志队列是用来存放收集到的日志,同时还会把收集到的日志分发给日志消费者 日志消费者:日志的消费者也是其他进程,同样也是通过进程通信的方式把日志传递给消费者
日志生产者会有多个同时日志消费者也有多个,日志收集分发中心、日志生产者、日志消费者形成了日志系统的雏形,那我就从这三部分来介绍雏形是如何一步步衍化为日志系统的。
日志队列中的日志需要存储在内存中,不会因为日志消费者消费了对应日志,该日志就从日志队列中删除 可以通过命令来控制日志队列的状态比如清除某些日志等 日志队列中存储的日志总量是有最大限制的,不可能无上限的存下去如果这样肯定会出现内存溢出的
typedef enum log_id {
LOG_ID_MIN = 0,
//app进程的日志
/** The main log buffer. This is the only log buffer available to apps. */
LOG_ID_MAIN = 0,
/** The radio log buffer. */
LOG_ID_RADIO = 1,
//event类型日志,比如activity生命周期之类的
/** The event log buffer. */
LOG_ID_EVENTS = 2,
//systemserver进程的日志,比如AMS
/** The system log buffer. */
LOG_ID_SYSTEM = 3,
//崩溃日之惠
/** The crash log buffer. */
LOG_ID_CRASH = 4,
/** The statistics log buffer. */
LOG_ID_STATS = 5,
/** The security log buffer. */
LOG_ID_SECURITY = 6,
//kernel日志
/** The kernel log buffer. */
LOG_ID_KERNEL = 7,
LOG_ID_MAX,
/** Let the logging function choose the best log target. */
LOG_ID_DEFAULT = 0x7FFFFFFF
} log_id_t;
void SimpleLogBuffer::Init() {
//log_id_for_each方法会遍历上面 所有的日志分类
log_id_for_each(i) {
//调用SetSize方法设置每种类型的最大内存值
if (!SetSize(i, GetBufferSizeFromProperties(i))) {
SetSize(i, kLogBufferMinSize);
}
}
省略代码......
}
bool SimpleLogBuffer::SetSize(log_id_t id, size_t size) {
省略代码......
auto lock = std::lock_guard{logd_lock};
max_size_[id] = size;
return true;
}
//添加日志到队列,log_id日志类别id
int SimpleLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
const char* msg, uint16_t len) {
省略代码......
auto lock = std::lock_guard{logd_lock};
//每条日志都对应一个sequence,从1开始每次加1
auto sequence = sequence_.fetch_add(1, std::memory_order_relaxed);
//log_id, realtime, uid, pid, tid, sequence, msg, len生成LogBufferElement对象
LogInternal(LogBufferElement(log_id, realtime, uid, pid, tid, sequence, msg, len));
return len;
}
//添加LogBufferElement到队列
void SimpleLogBuffer::LogInternal(LogBufferElement&& elem) {
log_id_t log_id = elem.log_id();
//添加到logs_队列中
logs_.emplace_back(std::move(elem));
stats_->Add(logs_.back().ToLogStatisticsElement());
//如若达到上限尝试去清除老的日志
MaybePrune(log_id);
//通知监听者有新日志
reader_list_->NotifyNewLog(1 << log_id);
}
void SimpleLogBuffer::MaybePrune(log_id_t id) {
unsigned long prune_rows;
//ShouldPrune返回true则代表需要清理该日志类别的旧日志
if (stats_->ShouldPrune(id, max_size_[id], &prune_rows)) {
//清理旧日志,id为日志类别,prune_rows需要清理多少行
Prune(id, prune_rows, 0);
}
}
bool SimpleLogBuffer::Prune(log_id_t id, unsigned long prune_rows, uid_t caller_uid) {
省略代码......
return true;
//存放日志,log_id日志类别id
int SerializedLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
const char* msg, uint16_t len) {
省略代码......
//生成sequence,每个日志都对应一个sequence
auto sequence = sequence_.fetch_add(1, std::memory_order_relaxed);
auto lock = std::lock_guard{logd_lock};
//调用LogToLogBuffer开始加入日志
auto entry = LogToLogBuffer(logs_[log_id], max_size_[log_id], sequence, realtime, uid, pid, tid,
msg, len);
stats_->Add(entry->ToLogStatisticsElement(log_id));
//若超过上限值,则开始清除该日志类别的老的日志
MaybePrune(log_id);
//通知监听者有新的日志可以读取了
reader_list_->NotifyNewLog(1 << log_id);
return len;
}
//开始加入日志
static SerializedLogEntry* LogToLogBuffer(std::list<SerializedLogChunk>& log_buffer,
size_t max_size, uint64_t sequence, log_time realtime,
uid_t uid, pid_t pid, pid_t tid, const char* msg,
uint16_t len) {
//若为empty,则push一个SerializedLogChunk,它的大小是max_size / SerializedLogBuffer::kChunkSizeDivisor
if (log_buffer.empty()) {
log_buffer.push_back(SerializedLogChunk(max_size / SerializedLogBuffer::kChunkSizeDivisor));
}
auto total_len = sizeof(SerializedLogEntry) + len;
//若最后的SerializedLogChunk没有空间存储当前日志
if (!log_buffer.back().CanLog(total_len)) {
//调用FinishWriting方法会对最后的SerializedLogChunk进行压缩
log_buffer.back().FinishWriting();
//往log_buffer重新push一个SerializedLogChunk
log_buffer.push_back(SerializedLogChunk(max_size / SerializedLogBuffer::kChunkSizeDivisor));
}
//调用SerializedLogChunk的Log方法把新日志写入
return log_buffer.back().Log(sequence, realtime, uid, pid, tid, msg, len);
}
//文件路径:/system/logging/logd/SerializedLogChunk.cpp
SerializedLogEntry* SerializedLogChunk::Log(uint64_t sequence, log_time realtime, uid_t uid,
pid_t pid, pid_t tid, const char* msg, uint16_t len) {
auto new_log_address = contents_.data() + write_offset_;
auto* entry = new (new_log_address) SerializedLogEntry(uid, pid, tid, sequence, realtime, len);
memcpy(entry->msg(), msg, len);
write_offset_ += entry->total_len();
highest_sequence_number_ = sequence;
return entry;
}
int main(int argc, char* argv[]) {
省略其他代码......
//获取logd.buffer_type属性对应的值,默认值是serialized
std::string buffer_type = GetProperty("logd.buffer_type", "serialized");
// LogBuffer is the object which is responsible for holding all log entries.
LogBuffer* log_buffer = nullptr;
//根据buffer_type的值,来对日志队列log_buffer进行初始化,一般情况下都会初始化SerializedLogBuffer的日志队列
if (buffer_type == "chatty") {
log_buffer = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
} else if (buffer_type == "serialized") {
log_buffer = new SerializedLogBuffer(&reader_list, &log_tags, &log_statistics);
} else if (buffer_type == "simple") {
log_buffer = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);
} else {
LOG(FATAL) << "buffer_type must be one of 'chatty', 'serialized', or 'simple'";
}
省略其他代码......
}
int main(int argc, char* argv[]) {
省略其他代码......
// LogListener listens on /dev/socket/logdw for client
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
// 实例化LogListener
LogListener* swl = new LogListener(log_buffer);
//调用StartListener方法会启动一个线程并且不断循环监听收到的日志数据
if (!swl->StartListener()) {
return EXIT_FAILURE;
}
省略其他代码......
}
//文件路径:/system/logging/logd/LogListener.cpp
//GetLogSocket()方法会把server socket名字为logdw的server获取到,对socket_进行初始化
LogListener::LogListener(LogBuffer* buf) : socket_(GetLogSocket()), logbuf_(buf) {}
//启动一个线程,调用ThreadFunction方法
bool LogListener::StartListener() {
if (socket_ <= 0) {
return false;
}
auto thread = std::thread(&LogListener::ThreadFunction, this);
thread.detach();
return true;
}
//该方法会进入循环,不断地从socket client端读取日志信息
void LogListener::ThreadFunction() {
prctl(PR_SET_NAME, "logd.writer");
while (true) {
HandleData();
}
}
void LogListener::HandleData() {
省略代码......
//把从socket client端获取的日志信息放入logbuf中
logbuf_->Log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
}
int LogListener::GetLogSocket() {
//server socket的名字是logdw
static const char socketName[] = "logdw";
int sock = android_get_control_socket(socketName);
省略代码......
return sock;
}
struct __android_log_message {
/** Must be set to sizeof(__android_log_message) and is used for versioning. */
size_t struct_size;
//日志类别id
/** {@link log_id_t} values. */
int32_t buffer_id;
//优先级
/** {@link android_LogPriority} values. */
int32_t priority;
//tag 日志对应的tag
/** The tag for the log message. */
const char* tag;
//暂时用不到,可以忽略
/** Optional file name, may be set to nullptr. */
const char* file;
/** Optional line number, ignore if file is nullptr. */
uint32_t line;
//日志具体信息
/** The log message itself. */
const char* message;
};
日志分发中心的作用是把日志队列中的日志分发给日志消费者,而日志消费者是位于其他进程与logd不是同一进程,因此也需要一个“分发渠道”把日志队列中的日志分发出去。
与收集渠道类似,分发渠道也使用socket通信,主要原因是:首先日志队列会对应多个日志消费者,这明显是C/S模式;其次日志消费者它是非常省心的,只是一味的接收发送过来的日志即可也不需要对收到的日志进行排序等处理,因此需要串行传递日志,而socket发送接收数据是串行的。基于以上原因分发渠道选用了socket通信。
int main(int argc, char* argv[]) {
省略其他代码......
// LogReader listens on /dev/socket/logdr. When a client
// connects, log entries in the LogBuffer are written to the client.
//创建LogReader实例
LogReader* reader = new LogReader(log_buffer, &reader_list);
//调用startListener方法,会创建一个线程,不断循环去监听建立连接的client端
if (reader->startListener()) {
return EXIT_FAILURE;
}
省略其他代码......
}
//文件路径:/system/logging/logd/LogReader.cpp
LogReader::LogReader(LogBuffer* logbuf, LogReaderList* reader_list)
: SocketListener(getLogSocket(), true), log_buffer_(logbuf), reader_list_(reader_list) {}
//获取server socket
int LogReader::getLogSocket() {
//server socket name logdr
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;
}
//若有socket client连接的话,会调用这个方法,进而把client保存
bool LogReader::onDataAvailable(SocketClient* cli) {
static bool name_set;
省略代码......
return true;
}
int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
省略代码......
//构造__android_log_message对象
__android_log_message log_message = {
sizeof(__android_log_message), bufID, prio, tag, nullptr, 0, msg};
//由于代码量太大,关于__android_log_write_log_message及后续的代码就不贴出来了
__android_log_write_log_message(&log_message);
return 1;
}
int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
ErrnoRestorer errno_restorer;
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
return -EPERM;
}
va_list ap;
__attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
va_start(ap, fmt);
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
va_end(ap);
//构造__android_log_message对象,日志类别为LOG_ID_MAIN
__android_log_message log_message = {
sizeof(__android_log_message), LOG_ID_MAIN, prio, tag, nullptr, 0, buf};
//由于代码量太大,关于__android_log_write_log_message及后续的代码就不贴出来了
__android_log_write_log_message(&log_message);
return 1;
}
#! /system/bin/sh
省略代码......
//下面代码会执行logcat.cpp的main方法,同时会把adb logcat携带的参数传递过去
exec logcat "${ARGS[@]}"
//文件路径:/system/logging/logcat/logcat.cpp
int main(int argc, char** argv) {
Logcat logcat;
return logcat.Run(argc, argv);
}
int Logcat::Run(int argc, char** argv) {
省略代码......
//从logd接收日志并显示
while (!max_count_ || print_count_ < max_count_) {
struct log_msg log_msg;
int ret = android_logger_list_read(logger_list.get(), &log_msg);
省略代码......
//若是二进制则走这
if (print_binary_) {
WriteFully(&log_msg, log_msg.len());
} else {
//显示拿到的日志
ProcessBuffer(&log_msg);
if (blocking && output_file_ == stdout) fflush(stdout);
}
}
return EXIT_SUCCESS;
}