失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Android10.0 日志系统分析(四)-selinux kernel日志在logd中的实现​-[Android取经之路]

Android10.0 日志系统分析(四)-selinux kernel日志在logd中的实现​-[Android取经之路]

时间:2022-10-09 10:38:16

相关推荐

Android10.0 日志系统分析(四)-selinux kernel日志在logd中的实现​-[Android取经之路]

摘要:本节主要来讲解Android10.0 selinux、kernel日志在logd中的实现,包括LogAudit、LogKlog的源码分析

阅读本文大约需要花费15分钟。

文章首发微信公众号:IngresGe

专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!

欢迎关注我的公众号!

[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析

[Android取经之路] 系列文章:

《系统启动篇》

Android系统架构Android是怎么启动的Android 10.0系统启动之init进程Android10.0系统启动之Zygote进程Android 10.0 系统启动之SystemServer进程Android 10.0 系统服务之ActivityMnagerServiceAndroid10.0系统启动之Launcher(桌面)启动流程Android10.0应用进程创建过程以及Zygote的fork流程Android 10.0 PackageManagerService(一)工作原理及启动流程Android 10.0 PackageManagerService(二)权限扫描Android 10.0 PackageManagerService(三)APK扫描Android 10.0 PackageManagerService(四)APK安装流程

《日志系统篇》

Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​

《Binder通信原理》

Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要Android10.0 Binder通信原理(二)-Binder入门篇Android10.0 Binder通信原理(三)-ServiceManager篇Android10.0 Binder通信原理(四)-Native-C\C++实例分析Android10.0 Binder通信原理(五)-Binder驱动分析Android10.0 Binder通信原理(六)-Binder数据如何完成定向打击Android10.0 Binder通信原理(七)-Framework binder示例Android10.0 Binder通信原理(八)-Framework层分析Android10.0 Binder通信原理(九)-AIDL Binder示例Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub设计模式Android10.0 Binder通信原理(十一)-Binder总结

《HwBinder通信原理》

HwBinder入门篇-Android10.0 HwBinder通信原理(一)HIDL详解-Android10.0 HwBinder通信原理(二)HIDL示例-C++服务创建Client验证-Android10.0 HwBinder通信原理(三)HIDL示例-JAVA服务创建-Client验证-Android10.0 HwBinder通信原理(四)HwServiceManager篇-Android10.0 HwBinder通信原理(五)Native层HIDL服务的注册原理-Android10.0 HwBinder通信原理(六)Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)JAVA层HIDL服务的注册原理-Android10.0 HwBinder通信原理(八)JAVA层HIDL服务的获取原理-Android10.0 HwBinder通信原理(九)HwBinder驱动篇-Android10.0 HwBinder通信原理(十)HwBinder原理总结-Android10.0 HwBinder通信原理(十一)

《编译原理》

编译系统入门篇-Android10.0编译系统(一)编译环境初始化-Android10.0编译系统(二)make编译过程-Android10.0编译系统(三)Image打包流程-Android10.0编译系统(四)Kati详解-Android10.0编译系统(五)

上一节我们看了Android日志系统的架构分析以及logd、logcat的初始化操作,这一节我们来看看日志系统的读写操作

7.5 LogAudit的写入

从logd初始化时,我们可以看到,如果配置了属性“ro.logd.auditd”,则会创建LogAudit,LogAudit 在NETLINK_AUDIT的socket上侦听selinux启动的日志消息。

可以在手机root后,进行属性设置:setprop ro.logd.auditd true

[/system/core/logd/main.cpp] main()

int main(int argc, char* argv[]) {...bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);if (drop_privs(klogd, auditd) != 0) {return EXIT_FAILURE;}...LogAudit* al = nullptr;if (auditd) {al = new LogAudit(logBuf, reader,__android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)? fdDmesg: -1);}readDmesg(al, kl);...}

在LogAudit()被创建时,会去调用getLogSocket(),创建socket PF_NETLINK,并与内核进行连接,把pid发给内核,告诉内核,用来获取selinux日志。

[/system/core/logd/LogAudit.cpp] getLogSocket()

int LogAudit::getLogSocket() {//创建socket PF_NETLINKint fd = audit_open();if (fd < 0) {return fd;}//与内核建立连接,让内核知道这个pid用来获取selinux信息if (audit_setup(fd, getpid()) < 0) {audit_close(fd);fd = -1;}return fd;}

启动socket监听后,调用onDataAvailable(),与logd.auditd建立连接,调用recvfrom()接收socket传来的数据,最终调用logPrint把/dev/kmsg 内容写入“log to events”\"log to main",并通知LogReader有日志写入。

[/system/core/logd/LogAudit.cpp] onDataAvailable()

bool LogAudit::onDataAvailable(SocketClient* cli) {if (!initialized) {prctl(PR_SET_NAME, "logd.auditd");initialized = true;}struct audit_message rep;rep.nlh.nlmsg_type = 0;rep.nlh.nlmsg_len = 0;rep.data[0] = '\0';if (audit_get_reply(cli->getSocket(), &rep, GET_REPLY_BLOCKING, 0) < 0) {SLOGE("Failed on audit_get_reply with error: %s", strerror(errno));return false;}logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);return true;}

把日志内容写入LogBuffer 中的events和main的日志中,并通知LogReader有日志写入,供其他客户端进行读取。

[/system/core/logd/LogAudit.cpp] logPrint()

int LogAudit::logPrint(const char* fmt, ...) {...//把selinux写入Log buffer中的events idif (events) { // begin scope for event buffer...rc = logbuf->log(LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),(message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);if (rc >= 0) {notify |= 1 << LOG_ID_EVENTS;}// end scope for event buffer}...//把selinux写入Log buffer中的main idif (main) { // begin scope for main buffer...rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,(message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);if (rc >= 0) {notify |= 1 << LOG_ID_MAIN;}// end scope for main buffer}free(const_cast<char*>(commfree));free(str);if (notify) {//通知LogReader有日志写入reader->notifyNewLog(notify);if (rc < 0) {rc = message_len;}}return rc;}

7.6 kernel日志的写入

从logd初始化时,我们可以看到,如果配置了属性“ro.logd.kernel”,则会创建LogKlog,用来抓取Kernel日志。

可以在手机root后,进行属性设置:setprop ro.logd.kernel true

实现原理其实是把 "/proc/kmsg" 和 "/dev/kmsg" 文件当作socket 文件来使用。

[/system/core/logd/main.cpp] main()

int main(int argc, char* argv[]) {...static const char dev_kmsg[] = "/dev/kmsg";fdDmesg = android_get_control_file(dev_kmsg);if (fdDmesg < 0) {fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));}bool klogd = __android_logger_property_get_bool("ro.logd.kernel",BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);if (klogd) {static const char proc_kmsg[] = "/proc/kmsg";fdPmesg = android_get_control_file(proc_kmsg);if (fdPmesg < 0) {fdPmesg = TEMP_FAILURE_RETRY(open(proc_kmsg, O_RDONLY | O_NDELAY | O_CLOEXEC));}if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);}...LogKlog* kl = nullptr;if (klogd) {kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);}readDmesg(al, kl);...}

LogKlog 创建后,启动listener,调用onDataAvailable(),与logd.klog建立连接,最终调用log()把日志存入kernel buffer。

[/system/core/logd/LogKlog.cpp] onDataAvailable()

bool LogKlog::onDataAvailable(SocketClient* cli) {if (!initialized) {prctl(PR_SET_NAME, "logd.klogd");initialized = true;enableLogging = false;}char buffer[LOGGER_ENTRY_MAX_PAYLOAD];ssize_t len = 0;for (;;) {ssize_t retval = 0;if (len < (ssize_t)(sizeof(buffer) - 1)) {retval =read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len);}if ((retval == 0) && (len <= 0)) {break;}if (retval < 0) {return false;}len += retval;bool full = len == (sizeof(buffer) - 1);char* ep = buffer + len;*ep = '\0';ssize_t sublen;for (char *ptr = nullptr, *tok = buffer;!!(tok = android::log_strntok_r(tok, len, ptr, sublen));tok = nullptr) {if (((tok + sublen) >= ep) && (retval != 0) && full) {if (sublen > 0) memmove(buffer, tok, sublen);len = sublen;break;}if ((sublen > 0) && *tok) {//调用log(),把日志写入kernel bufferlog(tok, sublen);}}}return true;}

log()主要通过LogBuffer把日志写入kernel的buffer,再通知LogReader有日志写入。

[/system/core/logd/LogKlog.cpp] log()

int LogKlog::log(const char* buf, ssize_t len) {...// 把日志写入logbufferint rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);// 通知LogReader,有日志写入。if (rc > 0) {reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));}return rc;}

8.总结

至此,Android10.0的日志系统全部总结完成。

Android日志系统现在主要由Logd守护进行进行管理,liblog提供读写日志的接口,logcat提供读取日志的参数命令。

我们在日常调试或者CTS测试时,会遇到日志丢失或者不全的情况,主要原因是日志量很大,但是日志缓冲区很小,此时只要把日志的缓冲区调大即可。

方法1:setpropro.logd.size 5120 即把日志缓冲区都调整为5M

方法2:开发者模式->日志记录缓冲区大小-> 选择相应的缓冲区大小,可以选择64K -16M等5个大小

微信公众号:IngresGe

如果觉得《Android10.0 日志系统分析(四)-selinux kernel日志在logd中的实现​-[Android取经之路]》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。