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

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

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

《系统启动篇》

Android系统架构
Android是怎么启动的
Android 10.0系统启动之init进程
Android10.0系统启动之Zygote进程
Android 10.0 系统启动之SystemServer进程
Android 10.0 系统服务之ActivityMnagerService
Android10.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编译系统(五)
Blueprint简介-Android10.0编译系统(六)
Blueprint代码详细分析-Android10.0编译系统(七)
Android.bp 语法浅析-Android10.0编译系统(八)
Ninja简介-Android10.0编译系统(九)
Ninja提升编译速度的方法-Android10.0编译系统(十)
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_NETLINK
    int 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 id
    if (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 id
    if (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 buffer
                log(tok, sublen);
            }
        }
    }
 
    return true;
}

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

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

int LogKlog::log(const char* buf, ssize_t len) {
  ...
    // 把日志写入logbuffer
    int 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;
}

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

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

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

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

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

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值