添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
public static int i(String tag, String msg) {
    // [见小节3.2]
    return println_native(LOG_ID_MAIN, INFO, tag, msg);

Log.java中的方法都是输出到main buffer, 其中println_native是Native方法, 通过JNI调用如下方法。

3.2 println_native

[-> android_util_Log.cpp]

static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
        jint bufID, jint priority, jstring tagObj, jstring msgObj)
    const char* tag = NULL;
    const char* msg = NULL;
    //获取log标签和内容
    if (tagObj != NULL)
        tag = env->GetStringUTFChars(tagObj, NULL);
    msg = env->GetStringUTFChars(msgObj, NULL);
    // [见小节3.3]
    int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
    if (tag != NULL)
        env->ReleaseStringUTFChars(tagObj, tag);
    env->ReleaseStringUTFChars(msgObj, msg);
    return res;

3.3 __android_log_buf_write

[-> logd_write.c]

int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
    struct iovec vec[3];
    char tmp_tag[32];
    if ((bufID != LOG_ID_RADIO) &&
         (!strcmp(tag, "HTC_RIL") ||
        !strncmp(tag, "RIL", 3) || // RIL为前缀
        !strncmp(tag, "IMS", 3) || // IMS为前缀
        !strcmp(tag, "AT") ||
        !strcmp(tag, "GSM") ||
        !strcmp(tag, "STK") ||
        !strcmp(tag, "CDMA") ||
        !strcmp(tag, "PHONE") ||
        !strcmp(tag, "SMS"))) {
            bufID = LOG_ID_RADIO;
            //满足以上条件的tag,则默认输出到radio缓冲区,并修改相应的tags
            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
            tag = tmp_tag;
    vec[0].iov_base   = (unsigned char *) &prio;
    vec[0].iov_len    = 1;
    vec[1].iov_base   = (void *) tag;
    vec[1].iov_len    = strlen(tag) + 1;
    vec[2].iov_base   = (void *) msg;
    vec[2].iov_len    = strlen(msg) + 1;
    // [见小节3.4]
    return write_to_log(bufID, vec, 3);
  
  • 对于满足特殊条件的tag,则会输出到LOG_ID_RADIO缓冲区;
  • vec数组依次记录着log的级别,tag, msg.
  • 其中write_to_log函数指针指向__write_to_log_init

    static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
    

    3.4 write_to_log

    [-> logd_write.c]

    static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
        if (write_to_log == __write_to_log_init) {
            int ret;
            ret = __write_to_log_initialize(); //执行log初始化【见小节3.4.1】
            if (ret < 0) {
                if (pstore_fd >= 0) {
                    __write_to_log_daemon(log_id, vec, nr); //【见小节3.5】
                return ret;
            write_to_log = __write_to_log_daemon;
        return write_to_log(log_id, vec, nr);
    

    3.4.1 __write_to_log_initialize

    static int __write_to_log_initialize()
        int i, ret = 0;
        if (pstore_fd < 0) {
            //打开pstore文件, 用于panic时记录上次重启前的log
            pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
        if (logd_fd < 0) { //首次执行logd_fd = -1
            // 初始化socket
            i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
            if (i < 0) {
                ret = -errno;
            //设置为非阻塞模式
            } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) {
                ret = -errno;
                close(i);
            } else {
                struct sockaddr_un un;
                memset(&un, 0, sizeof(struct sockaddr_un));
                un.sun_family = AF_UNIX;
                strcpy(un.sun_path, "/dev/socket/logdw"); // socket通道
                // 连接该socket
                if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un,
                                               sizeof(struct sockaddr_un))) < 0) {
                    ret = -errno;
                    close(i);
                } else {
                    logd_fd = i; // 将新打开的socket的文件描述符赋予该logd_fd.
        return ret;
    

    该方法主要功能:

  • 打开pstore文件, 用于panic时记录上次重启前的log; (只打开一次);
  • 初始化并连接socket (“/dev/socket/logdw”), 设置为非阻塞模式;
  • 将socket的文件描述符赋予该logd_fd.
  • 接下来进入真正写log的方法.

    3.5 __write_to_log_daemon

    static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr)
        ssize_t ret;
        static const unsigned header_length = 2;
        struct iovec newVec[nr + header_length];
        android_log_header_t header;
        android_pmsg_log_header_t pmsg_header;
        struct timespec ts;
        size_t i, payload_size;
        static uid_t last_uid = AID_ROOT;
        static pid_t last_pid = (pid_t) -1;
        static atomic_int_fast32_t dropped;
        if (last_uid == AID_ROOT) {
            last_uid = getuid(); //获取uid
        if (last_pid == (pid_t) -1) {
            last_pid = getpid(); //获取pid
        clock_gettime(CLOCK_REALTIME, &ts); //获取realtime
        pmsg_header.magic = LOGGER_MAGIC;
        pmsg_header.len = sizeof(pmsg_header) + sizeof(header);
        pmsg_header.uid = last_uid;
        pmsg_header.pid = last_pid;
        header.tid = gettid(); //获取tid
        header.realtime.tv_sec = ts.tv_sec;
        header.realtime.tv_nsec = ts.tv_nsec;
        newVec[0].iov_base   = (unsigned char *) &pmsg_header;
        newVec[0].iov_len    = sizeof(pmsg_header);
        newVec[1].iov_base   = (unsigned char *) &header;
        newVec[1].iov_len    = sizeof(header);
        if (logd_fd > 0) {
            int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
            if (snapshot) {
                android_log_event_int_t buffer;
                header.id = LOG_ID_EVENTS;
                buffer.header.tag = htole32(LIBLOG_LOG_TAG);
                buffer.payload.type = EVENT_TYPE_INT;
                buffer.payload.data = htole32(snapshot);
                newVec[2].iov_base = &buffer;
                newVec[2].iov_len  = sizeof(buffer);
                ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
                if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
                    atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
        header.id = log_id;
        for (payload_size = 0, i = header_length; i < nr + header_length; i++) {
            newVec[i].iov_base = vec[i - header_length].iov_base;
            payload_size += newVec[i].iov_len = vec[i - header_length].iov_len;
            // 限制每条log最大为4076b
            if (payload_size > LOGGER_ENTRY_MAX_PAYLOAD) {
                newVec[i].iov_len -= payload_size - LOGGER_ENTRY_MAX_PAYLOAD;
                if (newVec[i].iov_len) {
                payload_size = LOGGER_ENTRY_MAX_PAYLOAD;
                break;
        pmsg_header.len += payload_size;
        if (pstore_fd >= 0) {
            //写入pstore
            TEMP_FAILURE_RETRY(writev(pstore_fd, newVec, i));
        // 写入logd[见小节3.6]
        ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1));
        if (ret > (ssize_t)sizeof(header)) {
            ret -= sizeof(header);
        } else if (ret == -EAGAIN) {
            atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
        return ret;
    

    该方法的主要功能, 准备log相关的信息:

  • realtime
  • 3.6 writev

    [-> uio.c]

    int  writev( int  fd, const struct iovec*  vecs, int  count )
        int   total = 0;
        for ( ; count > 0; count--, vecs++ ) {
            const char*  buf = vecs->iov_base;
            int          len = vecs->iov_len;
            while (len > 0) {
                //将数据写入fd
                int  ret = write( fd, buf, len );
                total += ret;
                buf   += ret;
                len   -= ret;
    Exit:
        return total;
    

    3.7 小节

    一句话总结就是Log.i()最终是通过调用write()向logd守护进程的socket(“/dev/socket/logdw”)端写入需要打印的日志信息。 接下来再来看logd的工作过程。

    四. logd守护进程

    logd是由init进程所启动的守护进程

    service logd /system/bin/logd
        class core
        socket logd stream 0666 logd logd
        socket logdr seqpacket 0666 logd logd
        socket logdw dgram 0222 logd logd
        group root system
    

    创建3个socket通道,用于进程间通信.

    4.1 main()

    [-> /system/core/logd/main.cpp]

    int main(int argc, char *argv[]) {
        int fdPmesg = -1;
        bool klogd = true;
        if (klogd) {
            //以只读方式 打开内核log的/proc/kmsg
            fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY);
        //以读写方式打开/dev/kmsg
        fdDmesg = open("/dev/kmsg", O_WRONLY);
        //处理reinit命令
        if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
            int sock = TEMP_FAILURE_RETRY(socket_local_client("logd",
                            ANDROID_SOCKET_NAMESPACE_RESERVED,SOCK_STREAM));
            static const char reinit[] = "reinit";
            //写入"reinit"
            ssize_t ret = TEMP_FAILURE_RETRY(write(sock, reinit, sizeof(reinit)));
            struct pollfd p;
            memset(&p, 0, sizeof(p));
            p.fd = sock;
            p.events = POLLIN;
            ret = TEMP_FAILURE_RETRY(poll(&p, 1, 100)); //进入poll轮询
            static const char success[] = "success";
            char buffer[sizeof(success) - 1];
            memset(buffer, 0, sizeof(buffer));
            //读取数据,保存到buffer
            ret = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
            //比较读取的数据是否为"success"
            return strncmp(buffer, success, sizeof(success) - 1) != 0;
        sem_init(&reinit, 0, 0);
        sem_init(&uidName, 0, 0);
        sem_init(&sem_name, 0, 1);
        pthread_attr_t attr;
        if (!pthread_attr_init(&attr)) {
            if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
                pthread_t thread;
                reinit_running = true;
                //创建线程"logd.daemon", 该线程入口函数reinit_thread_start()
                if (pthread_create(&thread, &attr, reinit_thread_start, NULL)) {
                    reinit_running = false;
            pthread_attr_destroy(&attr);
        LastLogTimes *times = new LastLogTimes();
        //创建LogBuffer对象
        logBuf = new LogBuffer(times);
        signal(SIGHUP, reinit_signal_handler);
        if (property_get_bool_svelte("logd.statistics")) {
            logBuf->enableStatistics();
        //监听/dev/socket/logdr, 当client连接上则将buffer信息写入client.
        LogReader *reader = new LogReader(logBuf);
        if (reader->startListener()) {
            exit(1);
        //监听/dev/socket/logdw, 新日志添加到LogBuffer, 并且LogReader发送更新给已连接的client
        LogListener *swl = new LogListener(logBuf, reader);
        if (swl->startListener(300)) {
            exit(1);
        //监听/dev/socket/logd, 处理logd管理命令
        CommandListener *cl = new CommandListener(logBuf, reader, swl);
        if (cl->startListener()) {
            exit(1);
        bool auditd = property_get_bool("logd.auditd", true);
        LogAudit *al = NULL;
        if (auditd) {
            bool dmesg = property_get_bool("logd.auditd.dmesg", true);
            al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1);
        LogKlog *kl = NULL;
        if (klogd) {
            kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL);
        readDmesg(al, kl);
        if (kl && kl->startListener()) {
            delete kl;
        if (al && al->startListener()) {
            delete al;
        TEMP_FAILURE_RETRY(pause());
        exit(0);
    

    该方法功能:

  • LogReader: 监听/dev/socket/logdr, 当client连接上则将buffer信息写入client. 所对应线程名”logd.reader”
  • LogListener: 监听/dev/socket/logdw, 新日志添加到LogBuffer, 并且LogReader发送更新给已连接的client. 所对应线程名”logd.writer”
  • CommandListener: 监听/dev/socket/logd, 处理logd管理命令. 所对应线程名”logd.control”
  • LogAudit: 所对应线程名”logd.auditd”
  • LogKlog: 所对应线程名”logd.klogd”
  • 入口reinit_thread_start: 所对应线程名”logd.daemon”
  • LogTimeEntry::threadStart: 所对应线程名”ogd.reader.per”
  • 另外, ANDROID_SOCKET_NAMESPACE_RESERVED代表位于/dev/socket名字空间。 通过adb命令, 可以看到logd进程有9个子线程。

    logd      381   1     21880  9132  sigsuspend 7f8301fdac S /system/bin/logd
    system    382   381   21880  9132  futex_wait 7f82fcf9c4 S logd.daemon
    logd      383   381   21880  9132  poll_sched 7f8301fd1c S logd.reader
    logd      384   381   21880  9132  poll_sched 7f8301fd1c S logd.writer
    logd      385   381   21880  9132  poll_sched 7f8301fd1c S logd.control
    logd      392   381   21880  9132  poll_sched 7f8301fd1c S logd.klogd
    logd      393   381   21880  9132  poll_sched 7f8301fd1c S logd.auditd
    logd      3716  381   21880  9132  futex_wait 7f82fcf9c4 S logd.reader.per
    logd      4329  381   21880  9132  futex_wait 7f82fcf9c4 S logd.reader.per
    logd      5224  381   21880  9132  futex_wait 7f82fcf9c4 S logd.reader.per
    

    接下来, 继续回到前面log输出过程, 接下来进入logd的LogListener处理过程, 如下:

    4.2 LogListener

    [-> LogListener.cpp]

    int main(int argc, char *argv[]) {
        logBuf = new LogBuffer(times); //[见小节4.2.1]
        LogListener *swl = new LogListener(logBuf, reader); //[见小节4.2.2]
        if (swl->startListener(300)) { //[见小节4.3]
            exit(1);
    

    4.2.1 LogBuffer.init

    [-> LogBuffer.cpp]

    void LogBuffer::init() {
        static const char global_tuneable[] = "persist.logd.size";
        static const char global_default[] = "ro.logd.size";
        //获取buffer默认大小
        unsigned long default_size = property_get_size(global_tuneable);
        if (!default_size) {
            default_size = property_get_size(global_default);
        log_id_for_each(i) {
            char key[PROP_NAME_MAX];
            snprintf(key, sizeof(key), "%s.%s",
                     global_tuneable, android_log_id_to_name(i));
            unsigned long property_size = property_get_size(key);
            if (!property_size) {
                snprintf(key, sizeof(key), "%s.%s",
                         global_default, android_log_id_to_name(i));
                //比如获取的是persist.logd.size.system所对应的值
                property_size = property_get_size(key);
            if (!property_size) {
                property_size = default_size;
            if (!property_size) {
                //此值为256k
                property_size = LOG_BUFFER_SIZE;
            if (setSize(i, property_size)) {
                //此值为64k
                setSize(i, LOG_BUFFER_MIN_SIZE);
    

    buffer大小设置的优先级顺序为:

  • persist.logd.size.xxx; 比如persist.logd.size.system;
  • persist.logd.size;
  • ro.logd.size;
  • LOG_BUFFER_SIZE, 即256k;
  • LOG_BUFFER_MIN_SIZE, 即64k。
  • 4.2.2 LogListener

    [-> LogListener.cpp]

    LogListener::LogListener(LogBuffer *buf, LogReader *reader) :
            SocketListener(getLogSocket(), false),
            logbuf(buf),
            reader(reader) {
    

    此处getLogSocket()过程创建logdw的服务端,并监听客户端消息.

    4.2.3 SocketListener

    SocketListener::SocketListener(int socketFd, bool listen) {
        init(NULL, socketFd, listen, false);
    void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
        mListen = listen; // mListen=false
        mSocketName = socketName;
        mSock = socketFd;
        mUseCmdNum = useCmdNum;
        pthread_mutex_init(&mClientsLock, NULL);
        mClients = new SocketClientCollection();
    

    SocketListener对象创建完成,则开始执行SocketListener来监听socket请求。

    4.3 startListener

    [-> SocketListener.cpp]

    int SocketListener::startListener() {
        return startListener(4);
    int SocketListener::startListener(int backlog) {
        if (!mSocketName && mSock == -1) {
        } else if (mSocketName) {
            if ((mSock = android_get_control_socket(mSocketName)) < 0) {
            fcntl(mSock, F_SETFD, FD_CLOEXEC);
        if (mListen && listen(mSock, backlog) < 0) {
            return -1;
        } else if (!mListen)
            mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
        if (pipe(mCtrlPipe)) {
            return -1;
        //创建线程[见小节4.4]
        if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
            return -1;
        return 0;
    

    通过调用pthread_create创建完线程,在新创建的线程中执行threadStart()过程。

    4.4 threadStart

    [-> SocketListener.cpp]

    void *SocketListener::threadStart(void *obj) {
        SocketListener *me = reinterpret_cast<SocketListener *>(obj);
        me->runListener(); //[见小节4.5]
        pthread_exit(NULL);
        return NULL;
    

    4.5 runListener

    [-> SocketListener.cpp]

    void SocketListener::runListener() {
        SocketClientCollection pendingList;
        while(1) {
            while (!pendingList.empty()) {
                it = pendingList.begin(); //找到第一个即将要处理的客户端
                SocketClient* c = *it;
                pendingList.erase(it);
                if (!onDataAvailable(c)) { //处理该消息[见小节4.6]
                    release(c, false);
                c->decRef();
    

    4.6 onDataAvailable

    [-> LogListener.cpp]

    bool LogListener::onDataAvailable(SocketClient *cli) {
        static bool name_set;
        if (!name_set) {
            prctl(PR_SET_NAME, "logd.writer");
            name_set = true;
        char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time)
            + LOGGER_ENTRY_MAX_PAYLOAD];
        struct iovec iov = { buffer, sizeof(buffer) };
        memset(buffer, 0, sizeof(buffer));
        char control[CMSG_SPACE(sizeof(struct ucred))];
        struct msghdr hdr = {
            NULL,
            &iov, //记录buffer地址指针
            control,
            sizeof(control),
        int socket = cli->getSocket();
         //通过socket接收消息,保存到hdr,其n代表消息长度
        ssize_t n = recvmsg(socket, &hdr, 0);
        struct ucred *cred = NULL;
        struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
        //获取ucred信息
        while (cmsg != NULL) {
            if (cmsg->cmsg_level == SOL_SOCKET
                    && cmsg->cmsg_type  == SCM_CREDENTIALS) {
                cred = (struct ucred *)CMSG_DATA(cmsg);
                break;
            cmsg = CMSG_NXTHDR(&hdr, cmsg);
        if (cred == NULL) {
            return false;
        //获取android_log_header_t结构体指针
        android_log_header_t *header = reinterpret_cast<android_log_header_t *>(buffer);
        if (header->id >= LOG_ID_MAX || header->id == LOG_ID_KERNEL) {
            return false;
        char *msg = ((char *)buffer) + sizeof(android_log_header_t);
        n -= sizeof(android_log_header_t);
        //[见小节4.7]
        if (logbuf->log((log_id_t)header->id, header->realtime,
                cred->uid, cred->pid, header->tid, msg,
                ((size_t) n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX) >= 0) {
            //[见小节4.8]
            reader->notifyNewLog();
        return true;
    

    LogBuffer.log()的参数说明:

  • android_log_header_t提供 log_id, realtime, tid
  • ucred提供 uid, pid.
  • msghdr提供 msg
  • 4.7 LogBuffer.log

    [-> LogBuffer.cpp]

    int LogBuffer::log(log_id_t log_id, log_time realtime,
                       uid_t uid, pid_t pid, pid_t tid,
                       const char *msg, unsigned short len) {
        //创建一条log信息
        LogBufferElement *elem = new LogBufferElement(log_id, realtime,
                                                      uid, pid, tid, msg, len);
        int prio = ANDROID_LOG_INFO;
        const char *tag = NULL;
        if (log_id == LOG_ID_EVENTS) {
            tag = android::tagToName(elem->getTag());
        } else {
            prio = *msg;
            tag = msg + 1;
        if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
            pthread_mutex_lock(&mLogElementsLock);
            stats.add(elem); //对于不运行输出log的状态下, 只统计log信息, 记录log本身
            stats.subtract(elem);
            pthread_mutex_unlock(&mLogElementsLock);
            delete elem;
            return -EACCES;
        pthread_mutex_lock(&mLogElementsLock);
        LogBufferElementCollection::iterator it = mLogElements.end();
        LogBufferElementCollection::iterator last = it;
        //根据时间排序,找到应该插入的点
        while (last != mLogElements.begin()) {
            --it;
            if ((*it)->getRealTime() <= realtime) {
                break;
            last = it;
        //将log信息插入合适的位置
        if (last == mLogElements.end()) {
            mLogElements.push_back(elem);
        } else {
            uint64_t end = 1;
            bool end_set = false;
            bool end_always = false;
            LogTimeEntry::lock();
            LastLogTimes::iterator t = mTimes.begin();
            while(t != mTimes.end()) {
                LogTimeEntry *entry = (*t);
                if (entry->owned_Locked()) {
                    if (!entry->mNonBlock) {
                        end_always = true;
                        break;
                    if (!end_set || (end <= entry->mEnd)) {
                        end = entry->mEnd;
                        end_set = true;
            if (end_always|| (end_set && (end >= (*last)->getSequence()))) {
                mLogElements.push_back(elem);
            } else {
                mLogElements.insert(last,elem);
            LogTimeEntry::unlock();
        stats.add(elem);  //[4.7.1]
        maybePrune(log_id); //[4.7.2]
        pthread_mutex_unlock(&mLogElementsLock);
        return len;
    

    4.7.1 stats.add

    [-> LogStatistics.cpp]

    void LogStatistics::add(LogBufferElement *e) {
        log_id_t log_id = e->getLogId();
        unsigned short size = e->getMsgLen();
        mSizes[log_id] += size; //对应的buffer所使用大小增加
        ++mElements[log_id]; //对应的buffer中log记录加1;
        mSizesTotal[log_id] += size;
        ++mElementsTotal[log_id];
        if (log_id == LOG_ID_KERNEL) {
            return;
        //以uid为单位, 添加到uidTable表格
        uidTable[log_id].add(e->getUid(), e);
        if (!enable) {
            return;
        pidTable.add(e->getPid(), e);
        tidTable.add(e->getTid(), e);
        uint32_t tag = e->getTag();
        if (tag) {
            tagTable.add(tag, e);
    

    将log信息分别记录到uidTable, pidTable, tidTable, tagTable.

    4.7.2 maybePrune

    [-> LogBuffer.cpp]

    void LogBuffer::maybePrune(log_id_t id) {
        size_t sizes = stats.sizes(id); //log占用内存大小
        unsigned long maxSize = log_buffer_size(id); //最大上限,比如2M
        if (sizes > maxSize) {
            size_t sizeOver = sizes - ((maxSize * 9) / 10); //超出90%的部分大小
            size_t elements = stats.realElements(id); // 真实的log行数
            size_t minElements = elements / 100; // 真实的log行数的1%行
            //minPrune = 4, 保证1%的log行数>=4
            if (minElements < minPrune) {
                minElements = minPrune;
            unsigned long pruneRows = elements * sizeOver / sizes;
            if (pruneRows < minElements) { //保证>= 1%的log行数
                pruneRows = minElements;
            //maxPrune = 256, 保证pruneRows<=256;
            if (pruneRows > maxPrune) {
                pruneRows = maxPrune;
            prune(id, pruneRows); //[见小节4.7.3]
    

    假设某个buffer的大小为2M:

    pruneRows = elements * sizeOver / sizes
              = elements * (1 - 0.9*(maxSize/sizes))
              = elements * (1 - 1.8/sizes);
    

    其中elements代表的是当前buffer的log总行数;sizes代表对的是当前buffer的log总大小。

    这就意味着某个buffer中的log行数越多,或者log占用内存越大,则需要裁剪的日志行数越多。每次裁剪日志行数等于总行数的10%,并且需要大于等于4行,且不超过256行。

    4.7.3 prune

    [-> LogBuffer.cpp]

    bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
        LogTimeEntry *oldest = NULL;
        bool busy = false;
        bool clearAll = pruneRows == ULONG_MAX;
        LogTimeEntry::lock();
        LastLogTimes::iterator times = mTimes.begin();
        while(times != mTimes.end()) {
            LogTimeEntry *entry = (*times);
            if (entry->owned_Locked() && entry->isWatching(id)
                    && (!oldest ||
                        (oldest->mStart > entry->mStart) ||
                        ((oldest->mStart == entry->mStart) &&
                         (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
                oldest = entry;
            times++;
        LogBufferElementCollection::iterator it;
        if (caller_uid != AID_ROOT) {
            it = mLastSet[id] ? mLast[id] : mLogElements.begin();
            while (it != mLogElements.end()) {
                LogBufferElement *element = *it;
                if ((element->getLogId() != id) || (element->getUid() != caller_uid)) {
                    ++it;
                    continue;
                if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
                    mLast[id] = it;
                    mLastSet[id] = true;
                if (oldest && (oldest->mStart <= element->getSequence())) {
                    busy = true;
                    if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
                        oldest->triggerReader_Locked();
                    } else {
                        oldest->triggerSkip_Locked(id, pruneRows);
                    break;
                it = erase(it);
                pruneRows--;
            LogTimeEntry::unlock();
            return busy;
        //修剪 log最多的内容: 黑名单, uid, system uid的pid
        bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
        while (!clearAll && (pruneRows > 0)) {
            uid_t worst = (uid_t) -1;
            size_t worst_sizes = 0;
            size_t second_worst_sizes = 0;
            pid_t worstPid = 0;
            if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
                    std::unique_ptr<const UidEntry *[]> sorted = stats.sort(
                        AID_ROOT, (pid_t)0, 2, id);
                    if (sorted.get() && sorted[0] && sorted[1]) {
                        worst_sizes = sorted[0]->getSizes();
                        //buffer总大小的1/8为阈值
                        size_t threshold = log_buffer_size(id) / 8;
                        if ((worst_sizes > threshold)
                                && (worst_sizes > (10 * sorted[0]->getDropped()))) {
                            worst = sorted[0]->getKey();
                            second_worst_sizes = sorted[1]->getSizes();
                            if (second_worst_sizes < threshold) {
                                second_worst_sizes = threshold;
                if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
                    // 对于system_server进程,根据pid来决策
                    std::unique_ptr<const PidEntry *[]> sorted = stats.sort(
                        worst, (pid_t)0, 2, id, worst);
                    if (sorted.get() && sorted[0] && sorted[1]) {
                        worstPid = sorted[0]->getKey(); //最糟糕的pid
                        second_worst_sizes = worst_sizes
                                           - sorted[0]->getSizes()
                                           + sorted[1]->getSizes();
            if ((worst == (uid_t) -1) && !hasBlacklist) {
                break;
            bool kick = false;
            bool leading = true;
            it = mLastSet[id] ? mLast[id] : mLogElements.begin();
            bool gc = pruneRows <= 1;
            if (!gc && (worst != (uid_t) -1)) {
                    LogBufferIteratorMap::iterator found = mLastWorstUid[id].find(worst);
                    if ((found != mLastWorstUid[id].end())
                            && (found->second != mLogElements.end())) {
                        leading = false;
                        it = found->second;
                if (worstPid) {
                    // begin scope for pid worst found iterator
                    LogBufferPidIteratorMap::iterator found
                        = mLastWorstPidOfSystem[id].find(worstPid);
                    if ((found != mLastWorstPidOfSystem[id].end())
                            && (found->second != mLogElements.end())) {
                        leading = false;
                        it = found->second;
            static const timespec too_old = {
                EXPIRE_HOUR_THRESHOLD * 60 * 60, 0
            LogBufferElementCollection::iterator lastt;
            lastt = mLogElements.end();
            --lastt;
            LogBufferElementLast last;
            while (it != mLogElements.end()) {
                LogBufferElement *element = *it;
                if (oldest && (oldest->mStart <= element->getSequence())) {
                    busy = true;
                    if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
                        oldest->triggerReader_Locked();
                    break;
                if (element->getLogId() != id) {
                    ++it;
                    continue;
                if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
                    mLast[id] = it;
                    mLastSet[id] = true;
                unsigned short dropped = element->getDropped();
                // remove any leading drops
                if (leading && dropped) {
                    it = erase(it);
                    continue;
                if (dropped && last.coalesce(element, dropped)) {
                    it = erase(it, true);
                    continue;
                if (hasBlacklist && mPrune.naughty(element)) {
                    last.clear(element);
                    it = erase(it);
                    if (dropped) {
                        continue;
                    pruneRows--;
                    if (pruneRows == 0) {
                        break;
                    if (element->getUid() == worst) {
                        kick = true;
                        if (worst_sizes < second_worst_sizes) {
                            break;
                        worst_sizes -= element->getMsgLen();
                    continue;
                if ((element->getRealTime() < ((*lastt)->getRealTime() - too_old))
                        || (element->getRealTime() > (*lastt)->getRealTime())) {
                    break;
                if (dropped) {
                    last.add(element);
                    if (worstPid
                            && ((!gc && (element->getPid() == worstPid))
                                || (mLastWorstPidOfSystem[id].find(element->getPid())
                                    == mLastWorstPidOfSystem[id].end()))) {
                        mLastWorstPidOfSystem[id][element->getUid()] = it;
                    if ((!gc && !worstPid && (element->getUid() == worst))
                            || (mLastWorstUid[id].find(element->getUid())
                                == mLastWorstUid[id].end())) {
                        mLastWorstUid[id][element->getUid()] = it;
                    ++it;
                    continue;
                if ((element->getUid() != worst)
                        || (worstPid && (element->getPid() != worstPid))) {
                    leading = false;
                    last.clear(element);
                    ++it;
                    continue;
                pruneRows--;
                if (pruneRows == 0) {
                    break;
                kick = true;
                unsigned short len = element->getMsgLen();
                // do not create any leading drops
                if (leading) {
                    it = erase(it);
                } else {
                    stats.drop(element);
                    element->setDropped(1);
                    if (last.coalesce(element, 1)) {
                        it = erase(it, true);
                    } else {
                        last.add(element);
                        if (worstPid && (!gc
                                    || (mLastWorstPidOfSystem[id].find(worstPid)
                                        == mLastWorstPidOfSystem[id].end()))) {
                            mLastWorstPidOfSystem[id][worstPid] = it;
                        if ((!gc && !worstPid) || (mLastWorstUid[id].find(worst)
                                    == mLastWorstUid[id].end())) {
                            mLastWorstUid[id][worst] = it;
                        ++it;
                if (worst_sizes < second_worst_sizes) {
                    break;
                worst_sizes -= len;
            last.clear();
            if (!kick || !mPrune.worstUidEnabled()) {
                break; // the following loop will ask bad clients to skip/drop
        bool whitelist = false;
        bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
        while((pruneRows > 0) && (it != mLogElements.end())) {
            LogBufferElement *element = *it;
            if (element->getLogId() != id) {
                it++;
                continue;
            if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
                mLast[id] = it;
                mLastSet[id] = true;
            if (oldest && (oldest->mStart <= element->getSequence())) {
                busy = true;
                if (whitelist) {
                    break;
                if (stats.sizes(id) > (2 * log_buffer_size(id))) {
                    // kick a misbehaving log reader client off the island
                    oldest->release_Locked();
                } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
                    oldest->triggerReader_Locked();
                } else {
                    oldest->triggerSkip_Locked(id, pruneRows);
                break;
            if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
                // WhiteListed
                whitelist = true;
                it++;
                continue;
            it = erase(it);
            pruneRows--;
        // Do not save the whitelist if we are reader range limited
        if (whitelist && (pruneRows > 0)) {
            it = mLastSet[id] ? mLast[id] : mLogElements.begin();
            while((it != mLogElements.end()) && (pruneRows > 0)) {
                LogBufferElement *element = *it;
                if (element->getLogId() != id) {
                    ++it;
                    continue;
                if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
                    mLast[id] = it;
                    mLastSet[id] = true;
                if (oldest && (oldest->mStart <= element->getSequence())) {
                    busy = true;
                    if (stats.sizes(id) > (2 * log_buffer_size(id))) {
                        // kick a misbehaving log reader client off the island
                        oldest->release_Locked();
                    } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
                        oldest->triggerReader_Locked();
                    } else {
                        oldest->triggerSkip_Locked(id, pruneRows);
                    break;
                it = erase(it);
                pruneRows--;
        LogTimeEntry::unlock();
        return (pruneRows > 0) && busy;
    

    在PruneList::init()过程会完成黑白名单.

    日志裁剪功能说明:

  • 裁剪黑名单以及log打印最多的那个uid, 以及system uid中打印日志最多的pid进程中的日志;
  • 白名单的不删除
  • 4.8 小节

    每一行log记录为LogBufferElement,logd的执行调用链如下

    SocketListener::runListener()
        LogListener.onDataAvailable
            LogBuffer::log
                LogBuffer::maybePrune
            LogReader::notifyNewLog
                SocketListener::runOnEachSocket
                FlushCommand::runSocketCommand
    

    五. 总结

    当日志输出过于频繁或者日志占用内存过大时,会有日志裁剪的动作:每次裁剪日志行数等于总行数的10%,并且需要大于等于4行,且不超过256行,优先裁剪黑名单以及log打印最多的那个uid, 以及system uid中打印日志最多的pid进程中的日志,也可以设置不裁剪的白名单。

    buffer大小设置的优先级顺序为:

  • persist.logd.size.xxx; 比如persist.logd.size.system;
  • persist.logd.size;
  • ro.logd.size;
  • LOG_BUFFER_SIZE, 即256k;
  • LOG_BUFFER_MIN_SIZE, 即64k。
  • 参数说明: