Android中Log机制

Android中Log机制

写Log过程

这里写图片描述

首先从Java层入手

    下面是定义的Log级别:
    public static final int VERBOSE = 2;
    public static final int DEBUG = 3;
    public static final int INFO = 4;
    public static final int WARN = 5;
    public static final int ERROR = 6;
    public static final int ASSERT = 7;
    对应不同的Log缓冲区
    public static final int LOG_ID_MAIN = 0;
    public static final int LOG_ID_RADIO = 1;
    public static final int LOG_ID_EVENTS = 2;
    public static final int LOG_ID_SYSTEM = 3;
    public static final int LOG_ID_CRASH = 4;
    public static final int LOG_ID_WSEVENTS = 5;
    我们着重看一个方法,顺藤摸瓜摸下去:
    public static int i(String tag, String msg) {
        return println_native(LOG_ID_MAIN, INFO, tag, msg);
    }

println_native是调用jni层的方法
对应到jni方法

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;
    ...
    //bufID代表不同缓冲区
    //priority代表自己定义的log级别
    //tag代表自己设定的标记
    //msg代表自己要打印的信息
    int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
    ...
    return res;
}

继续顺着__android_log_buf_write()追寻下去

int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
{
    struct iovec vec[3];
    char tmp_tag[32];
    if (!tag)
        tag = "";
    if ((bufID != LOG_ID_RADIO) &&(!strcmp(tag, "HTC_RIL") ||!strncmp(tag, "RIL", 3) ||
        !strncmp(tag, "IMS", 3) ||!strcmp(tag, "AT") ||!strcmp(tag, "GSM") ||!strcmp(tag, "STK") ||
        !strcmp(tag, "CDMA") ||!strcmp(tag, "PHONE") ||!strcmp(tag, "SMS"))) {
            bufID = LOG_ID_RADIO;
            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
            tag = tmp_tag;
    }

#if __BIONIC__
    if (prio == ANDROID_LOG_FATAL) {
        android_set_abort_message(msg);
    }
#endif
    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;

    return write_to_log(bufID, vec, 3);
}

struct iovec {
    void*  iov_base;
    size_t iov_len;
};

这里面把信息复制到了iovec结构体里面,然后继续执行write_to_log()方法
在Logd_write.c中定义的核心数组:
static const char *LOG_NAME[LOG_ID_MAX] = {
[LOG_ID_MAIN] = “main”,
[LOG_ID_RADIO] = “radio”,
[LOG_ID_EVENTS] = “events”,
[LOG_ID_SYSTEM] = “system”,
[LOG_ID_CRASH] = “crash”,
[LOG_ID_KERNEL] = “kernel”,
};


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

可见write_to_log是一个函数指针,并且指向_write_to_log_init入口,我么现在继续看看_write_to_log_init函数

static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
{
#if !defined(_WIN32)
    pthread_mutex_lock(&log_init_lock);
#endif
    if (write_to_log == __write_to_log_init) {
        int ret;
        ret = __write_to_log_initialize();//第一次会先进入这个方法
        if (ret < 0) {
#if !defined(_WIN32)
            pthread_mutex_unlock(&log_init_lock);
#endif
#if (FAKE_LOG_DEVICE == 0)
            if (pstore_fd >= 0) {
                __write_to_log_daemon(log_id, vec, nr);
            }
#endif
            return ret;
        }
        write_to_log = __write_to_log_daemon;//以后会进入这个方法
    }
#if !defined(_WIN32)
    pthread_mutex_unlock(&log_init_lock);
#endif
    return write_to_log(log_id, vec, nr);
}

既然第一次进入__write_to_log_initialize()方法,那么我们来看看这个方法干了什么事情

static int __write_to_log_initialize()
{
    int i, ret = 0;
#if FAKE_LOG_DEVICE
    for (i = 0; i < LOG_ID_MAX; i++) {
        char buf[sizeof("/dev/log_system")];
        snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
        log_fds[i] = fakeLogOpen(buf, O_WRONLY);
    }
#else
    if (pstore_fd < 0) {
        pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
    }
    if (logd_fd < 0) {
        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");

            if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un,
                                           sizeof(struct sockaddr_un))) < 0) {
                ret = -errno;
                close(i);
            } else {
                logd_fd = i;
            }
        }
    }
#endif
    return ret;
}

我们看到了:

  • socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)创建socket
  • strcpy(un.sun_path, “/dev/socket/logdw”);设备节点路径/dev/socket/logdw
  • connect(i, (struct sockaddr *)&un, sizeof(struct sockaddr_un))链接socket,
    connect链接成功则返回0,所以logd_fd = i;logd_fd记录了链接句柄

既然第一次进入函数已经分析,下面也来说下第二次进入的函数__write_to_log_daemon

static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr)
{
    ...
    if (logd_fd > 0) {
        int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
        if (snapshot) {
           ...

                /*struct timespec {
                  time_t tv_sec;//long
                  long tv_nsec;
                };
                header.tid = gettid();//当前线程id
                header.realtime.tv_sec = ts.tv_sec;
                header.realtime.tv_nsec = ts.tv_nsec;*/
           //logd_fd是传递进来的句柄文件
            newVec[1].iov_base   = (unsigned char *) &header;
            newVec[1].iov_len    = sizeof(header);
            ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
           ...
        }
    }
   ...
    return ret;
}

此时注意传递进去的参数有:
bufID=LOG_ID_MAIN=0
vec为结构体:
{
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;
}
nr=3
其中调用了writev函数,继续追寻writev函数

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) {
            int  ret = write( fd, buf, len );
            if (ret < 0) {
                if (total == 0)
                    total = -1;
                goto Exit;
            }
            if (ret == 0)
                goto Exit;

            total += ret;
            buf   += ret;
            len   -= ret;
        }
    }
Exit:    
    return total;
}

上面传递的值有:
传递进来的值有:

logd_fd:socket句柄信息
struct timespec {
      time_t tv_sec;//long
      long tv_nsec;
    };
header.tid = gettid();//当前线程id
header.realtime.tv_sec = ts.tv_sec;
header.realtime.tv_nsec = ts.tv_nsec;
newVec[1].iov_base   = (unsigned char *) &header;
newVec[1].iov_len    = sizeof(header);
vecs=newVec[1];
count = 2;

可以看出里面调用了write函数,这个函数进行写入操作,这下我们就将我们的log写入到log缓冲区了.
由于代码中调用writev多次,多次的结果是将时间信息,还有消息信息
但是总结来说都是传递newVec数组,所以只要看下newVec数组都有那些信息就好

pmsg_header.magic = LOGGER_MAGIC;//#define LOGGER_MAGIC 'l'
pmsg_header.len = sizeof(pmsg_header) + sizeof(header);
pmsg_header.uid = last_uid;//getuid()返回实际用户的ID。
pmsg_header.pid = last_pid;//getpid();进程识别码
header.tid = gettid();
header.realtime.tv_sec = ts.tv_sec;
header.realtime.tv_nsec = ts.tv_nsec;
buffer.header.tag = htole32(LIBLOG_LOG_TAG);
buffer.payload.type = EVENT_TYPE_INT;//0
buffer.payload.data = htole32(snapshot);
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);
newVec[2].iov_base = &buffer;
newVec[2].iov_len  = sizeof(buffer);

读取Log过程

我们每次把手机链接到AS或者Eclipse的时候,会有很多Log输出,那么Log是在什么时候就一直输出的.
答案是:在系统启动的时候Log系统就会启动,也就是已经在init.rc文件中配置好了.我们暂时不把重点放到这块,先来看看到底启动服务了之后,Log怎么就被读出来了.

这里写图片描述

@(system\core\logd\Main.cpp)

我们先来查看一下进程和线程信息:

shell@cancro:/ $ ps |grep logd                                               
logd      220   1     33996  16500 sys_rt_sig 00000000 S /system/bin/logd
--------------------------------------------------------------------------------
shell@cancro:/ $ ps -t|grep 220 
system    221   220   33996  16296 futex_wait 00000000 S logd.daemon
logd      222   220   33996  16296 poll_sched 00000000 S logd.reader
logd      223   220   33996  16296 poll_sched 00000000 S logd.writer
logd      224   220   33996  16296 poll_sched 00000000 S logd.control
logd      226   220   33996  16296 poll_sched 00000000 S logd.auditd
logd      5193  220   33996  16296 futex_wait 00000000 S logd.reader.per
logd      14611 220   33996  16296 futex_wait 00000000 S logd.reader.per
logd      15660 220   33996  16296 futex_wait 00000000 S logd.reader.per
//main方法进入
int main(int argc, char *argv[]) {
  ...
    LastLogTimes *times = new LastLogTimes();
    logBuf = new LogBuffer(times);
    signal(SIGHUP, reinit_signal_handler);
    if (property_get_bool_svelte("logd.statistics")) {
        logBuf->enableStatistics();
    }
    LogReader *reader = new LogReader(logBuf);
    if (reader->startListener()) {
        exit(1);
    }
    LogListener *swl = new LogListener(logBuf, reader);
    if (swl->startListener(300)) {
        exit(1);
    }
    CommandListener *cl = new CommandListener(logBuf, reader, swl);
    if (cl->startListener()) {
        exit(1);
    }
  ...
    exit(0);
}

LastLogTimes():作用于管理最后的日志的时间,在socket链接时,并且锁定一系列log
然后我们看下LogBuffer类做些啥东东

void LogBuffer::init() {
    static const char global_tuneable[] = "persist.logd.size"; // Settings App
...
    unsigned long default_size = property_get_size(global_tuneable);//得到log打印的最大条目个数
...
}

我们看到有三个监听类,他们都继承于SocketListener类
我们先看第一个类的构造做了什么事情

LogReader::LogReader(LogBuffer *logbuf) :
        SocketListener(getLogSocket(), true),
        mLogbuf(*logbuf) {
}
--------------------------------------------------------------------------------------------
int LogReader::getLogSocket() {
    static const char socketName[] = "logdr";
    /*android_get_control_socket - 简单的帮助函数来获取我们的init管理的Unix域套接字的文件描述符。 `name'是套接字的名称,如init.rc.中给出。 在出错时返回-1。*/
    int sock = android_get_control_socket(socketName);

    if (sock < 0) {
        sock = socket_local_server(socketName,
                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
                                   SOCK_SEQPACKET);
    }

    return sock;
}

也就是说这个构造干了两件事情,一个是logdr明明的socket通道打开,其中logdr对应的socket在init.rc中给出
一个是将logbuf赋值给mLogbuf

然后我们看一看reader->startListener()干了什么事情

    if (pipe(mCtrlPipe)) {
        SLOGE("pipe failed (%s)", strerror(errno));
        return -1;
    }
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
        SLOGE("pthread_create (%s)", strerror(errno));
        return -1;
    }
--------------------------------------------------------------------------------------------
void *SocketListener::threadStart(void *obj) {
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);

    me->runListener();
    pthread_exit(NULL);
    return NULL;
}

开启管道,创建线程并且开始监听,在threadStart运行
继续看runListener()


void SocketListener::runListener(){
    SocketClientCollection pendingList;
    while(1) {
       ...
        if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
            char c = CtrlPipe_Shutdown;
            TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
           ...
            continue;
        }
        if (mListen && FD_ISSET(mSock, &read_fds)) {
            struct sockaddr addr;
            socklen_t alen;
            int c;

            do {
                alen = sizeof(addr);
                c = accept(mSock, &addr, &alen);
                SLOGV("%s got %d from accept", mSocketName, c);
            } while (c < 0 && errno == EINTR);
            if (c < 0) {
                SLOGE("accept failed (%s)", strerror(errno));
                sleep(1);
                continue;
            }
            fcntl(c, F_SETFD, FD_CLOEXEC);
            pthread_mutex_lock(&mClientsLock);
            mClients->push_back(new SocketClient(c, true, mUseCmdNum));
            pthread_mutex_unlock(&mClientsLock);
        }

        ...
        while (!pendingList.empty()) {
            /* Pop the first item from the list */
            it = pendingList.begin();
            SocketClient* c = *it;
            pendingList.erase(it);
            /* Process it, if false is returned, remove from list */
            if (!onDataAvailable(c)) {
                release(c, false);
            }
            c->decRef();
        }
    }
}

可以看出先接收链接,new一个SocketClient放到SocketClientCollection 队列中
最后当SocketClientCollection不为空,调用方法SocketListener的回调onDataAvailable

然后我们看看回调方法:

bool LogReader::onDataAvailable(SocketClient *cli){
...
    char buffer[255];
    int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
    if (len <= 0) {
        doSocketDelete(cli);
        return false;
    }
    buffer[len] = '\0';

   ...
    uint64_t sequence = 1;
    // Convert realtime to sequence number
    if (start != log_time::EPOCH) {
        class LogFindStart {
            const pid_t mPid;
            const unsigned mLogMask;
            bool startTimeSet;
            log_time &start;
            uint64_t &sequence;
            uint64_t last;

        public:
            LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) :
                    mPid(pid),
                    mLogMask(logMask),
                    startTimeSet(false),
                    start(start),
                    sequence(sequence),
                    last(sequence) {
            }

            static int callback(const LogBufferElement *element, void *obj) {
                LogFindStart *me = reinterpret_cast<LogFindStart *>(obj);
                if ((!me->mPid || (me->mPid == element->getPid()))
                        && (me->mLogMask & (1 << element->getLogId()))) {
                    if (me->start == element->getRealTime()) {
                        me->sequence = element->getSequence();
                        me->startTimeSet = true;
                        return -1;
                    } else {
                        if (me->start < element->getRealTime()) {
                            me->sequence = me->last;
                            me->startTimeSet = true;
                            return -1;
                        }
                        me->last = element->getSequence();
                    }
                }
                return false;
            }

            bool found() { return startTimeSet; }
        } logFindStart(logMask, pid, start, sequence);

        logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli), logFindStart.callback, &logFindStart);//核心代码

        if (!logFindStart.found()) {
            if (nonBlock) {
                doSocketDelete(cli);
                return false;
            }
            sequence = LogBufferElement::getCurrentSequence();
        }
    }

    FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence);
    ...
    struct timeval t = { 32, 0 };
    setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char *)&t, sizeof(t));
    command.runSocketCommand(cli);
    return true;
}

到这里我们必须小结一下了,我们现在知道onDataAvailable这个回调的函数有如下操作:
- read函数读取客户端传入参数
- 将logbuf中的日志全部写到链接的客户端
- 然后执行客户端过来的命令 command.runSocketCommand(cli);

查看核心代码:

uint64_t LogBuffer::flushTo(
        SocketClient *reader, const uint64_t start, bool privileged,
        int (*filter)(const LogBufferElement *element, void *arg), void *arg){
    ...
    max = element->flushTo(reader, this);
    ...
    return max;
}

然后调用LogBufferElement的flushTo方法:

uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent){
    struct logger_entry_v3 entry;
    memset(&entry, 0, sizeof(struct logger_entry_v3));
    entry.hdr_size = sizeof(struct logger_entry_v3);
    entry.lid = mLogId;
    entry.pid = mPid;
    entry.tid = mTid;
    entry.sec = mRealTime.tv_sec;
    entry.nsec = mRealTime.tv_nsec;
    struct iovec iovec[2];
    iovec[0].iov_base = &entry;
    iovec[0].iov_len = sizeof(struct logger_entry_v3);
    char *buffer = NULL;
    if (!mMsg) {
        entry.len = populateDroppedMessage(buffer, parent);
        if (!entry.len) {
            return mSequence;
        }
        iovec[1].iov_base = buffer;
    } else {
        entry.len = mMsgLen;
        iovec[1].iov_base = mMsg;
    }
    iovec[1].iov_len = entry.len;
    uint64_t retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mSequence;
    if (buffer) {
        free(buffer);
    }
    return retval;
}

这里把数据(mLogId,mPid,mTid,mRealTime.tv_sec,mRealTime.tv_nsec)封装成logger_entry_v3 entry

struct logger_entry_v3 {
    uint16_t    len;       /* length of the payload */
    uint16_t    hdr_size;  /* sizeof(struct logger_entry_v3) */
    int32_t     pid;       /* generating process's pid */
    int32_t     tid;       /* generating process's tid */
    int32_t     sec;       /* seconds since Epoch */
    int32_t     nsec;      /* nanoseconds */
    uint32_t    lid;       /* log id of the payload */
    char        msg[0];    /* the entry's payload */
}

然后把数据统一放到iovec数组中,iovec[0]放入logger_entry_v3
自己的数据msg放入iovec[1]中
然后调用SocketClient *reader的sendDatav(iovec,2)方法

int SocketClient::sendDatav(struct iovec *iov, int iovcnt) {
    pthread_mutex_lock(&mWriteMutex);
    int rc = sendDataLockedv(iov, iovcnt);
    pthread_mutex_unlock(&mWriteMutex);
    return rc;
}
int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {
    ...
    for (;;) {
        ssize_t rc = TEMP_FAILURE_RETRY(writev(mSocket, iov + current, iovcnt - current));
        if (rc > 0) {
            size_t written = rc;
            while ((current < iovcnt) && (written >= iov[current].iov_len)) {
                written -= iov[current].iov_len;
                current++;
            }
            if (current == iovcnt) {
                break;
            }
            iov[current].iov_base = (char *)iov[current].iov_base + written;
            iov[current].iov_len -= written;
            continue;
        }
        break;
    }
    return ret;
}

同样是writev写log

下面我们看一看runSocketCommand方法:

void FlushCommand::runSocketCommand(SocketClient *client) {
    LogTimeEntry *entry = NULL;
    LastLogTimes &times = mReader.logbuf().mTimes;
    LogTimeEntry::lock();
    LastLogTimes::iterator it = times.begin();
    while(it != times.end()) {
        entry = (*it);
        if (entry->mClient == client) {
            entry->triggerReader_Locked();
            if (entry->runningReader_Locked()) {
                LogTimeEntry::unlock();
                return;
            }
            entry->incRef_Locked();
            break;
        }
        it++;
    }
    if (it == times.end()) {
        if (mTail == (unsigned long) -1) {
            LogTimeEntry::unlock();
            return;
        }
        entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid, mStart);
        times.push_front(entry);
    }

    client->incRef();
    entry->startReader_Locked();
    LogTimeEntry::unlock();
}

继续调用startReader_Locked()

void LogTimeEntry::startReader_Locked(void) {
    pthread_attr_t attr;

    threadRunning = true;

    if (!pthread_attr_init(&attr)) {
        if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
            if (!pthread_create(&mThread, &attr,
                                LogTimeEntry::threadStart, this)) {
                pthread_attr_destroy(&attr);
                return;
            }
        }
        pthread_attr_destroy(&attr);
    }
    threadRunning = false;
    if (mClient) {
        mClient->decRef();
    }
    decRef_Locked();
}

创建一个线程调用LogTimeEntry::threadStart,传入LogTimees.cpp这个对象类

void *LogTimeEntry::threadStart(void *obj) {
    prctl(PR_SET_NAME, "logd.reader.per");
    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
    pthread_cleanup_push(threadStop, obj);
    SocketClient *client = me->mClient;
    if (!client) {
        me->error();
        return NULL;
    }
    LogBuffer &logbuf = me->mReader.logbuf();
    bool privileged = FlushCommand::hasReadLogs(client);
    me->leadingDropped = true;
    lock();
    uint64_t start = me->mStart;
    while (me->threadRunning && !me->isError_Locked()) {
        unlock();
        if (me->mTail) {
            logbuf.flushTo(client, start, privileged, FilterFirstPass, me);
            me->leadingDropped = true;
        }
        start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);
        lock();
        if (start == LogBufferElement::FLUSH_ERROR) {
            me->error_Locked();
            break;
        }
        me->mStart = start + 1;
        if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
            break;
        }
        me->cleanSkip_Locked();
        pthread_cond_wait(&me->threadTriggeredCondition, &timesLock);
    }

    unlock();
    pthread_cleanup_pop(true);
    return NULL;
}

此线程会不断循环 logbuf.flushTo(client, start, privileged, FilterFirstPass, me);发送log给客户端

小结上面

LogReader会监听logdr socket然后处理各个socket请求获取log.
LogReader会新建一个LogTimeEntry对象开启一个线程来调用LogBuffer的flushTo函数发送log,并且也会调用回调函数来过滤log,线程调用完挂起,直到下个相同的socket client请求,才会把这个线程恢复继续调用LogBuffer的flushTo发送log

也就是说:这里LogRead相当于服务端,谁发送命令需要什么log就给我发什么命令,然后我再把log发送回去
三个logd.reader.per线程就是在这里创建的

我们在看看第二个监听器:LogListener
他的构造函数如下:

LogListener::LogListener(LogBuffer *buf, LogReader *reader) :
        SocketListener(getLogSocket(), false),
        logbuf(buf),
        reader(reader) {
}
--------------------------------------------------------------------------------------------
其中SocketListener调用,参数传递的是
socketName="logdw"
socketFd=-1
listen=false
useCmdNum=flase
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
    mListen = listen;
    mSocketName = socketName;
    mSock = socketFd;
    mUseCmdNum = useCmdNum;
    pthread_mutex_init(&mClientsLock, NULL);
    mClients = new SocketClientCollection();
}
  • 同样调用getLogSocket方法链接logdw命名的socket
  • 将LogReader *reader赋值给成员变量reader
  • 将LogBuffer *buf赋值给logbuf

同样的继续调用startListener(300)开启监听

同上也是建立管道,创建线程调用pthread_create(&mThread, NULL, SocketListener::threadStart, this)
步骤同上最后调用到回调函数onDataAvailable()


bool LogListener::onDataAvailable(SocketClient *cli) {
    ...
    prctl(PR_SET_NAME, "logd.writer");//logd.writer就是当前线程
    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))];
    //利用hdr存log,其中iov是buffer
    struct msghdr hdr = {
        NULL,
        0,
        &iov,
        1,
        control,
        sizeof(control),
        0,
    };
    int socket = cli->getSocket();
    ssize_t n = recvmsg(socket, &hdr, 0);//接收数据
   ...
    struct ucred *cred = NULL;
    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
    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);
    }
    ...
    android_log_header_t *header = reinterpret_cast<android_log_header_t *>(buffer);//获取header头信息
    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);

    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) {
        reader->notifyNewLog();
    }
    return true;
}

这段代码是获取信息,封装信息,最后调用

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)

我们继续看看logbuf->log()怎么处理log的


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) {
...
    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;
    }
  ...
    // Insert elements in time sorted order if possible
    LogBufferElementCollection::iterator it = mLogElements.end();
    LogBufferElementCollection::iterator last = it;
    while (last != mLogElements.begin()) {
        --it;
        if ((*it)->getRealTime() <= realtime) {
            break;
        }
        last = it;
    }

    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;
                }
            }
            t++;
        }

        if (end_always
                || (end_set && (end >= (*last)->getSequence()))) {
            mLogElements.push_back(elem);
        } else {
            mLogElements.insert(last,elem);
        }

        LogTimeEntry::unlock();
    }

    stats.add(elem);
    maybePrune(log_id);
    pthread_mutex_unlock(&mLogElementsLock);

    return len;
}

其中核心方法是:
- stats.add(elem);//将封装的LogBufferElement进行封装,然后添加到 LogBufferElementCollection mLogElements;中
- maybePrune(log_id);
对于maybePrune(log_id)函数let me see

void LogBuffer::maybePrune(log_id_t id) {
    size_t sizes = stats.sizes(id);//log的个数,因为stats.add(elem);一直在添加
    unsigned long maxSize = log_buffer_size(id);//对应id缓冲区的最大个数,id上文已经给出
    if (sizes > maxSize) {
        size_t sizeOver = sizes - ((maxSize * 9) / 10);
        size_t elements = stats.realElements(id);
        size_t minElements = elements / 100;
        if (minElements < minPrune) {
            minElements = minPrune;
        }
        unsigned long pruneRows = elements * sizeOver / sizes;
        if (pruneRows < minElements) {
            pruneRows = minElements;
        }
        if (pruneRows > maxPrune) {
            pruneRows = maxPrune;
        }
        prune(id, pruneRows);
    }
}

修剪百分之十的log条目,然后选择最小者
prune这个函数是从id中除去那些pruneRows
此垃圾回收任务用于使日志条目过期。 它删除所有日志(清除),所有UID日志(非特权清除)或每个
256或10%的总日志(以较少者为准)修剪日志。

小结LogListener.cpp

  • 接收客户端传入的参数和数据(写入到缓冲区)
  • 将客户端日志写入
  • 通知所有客户端,日志写入,此时已经能够并且读取

大结

  • 在java程序中调用打印log,会通过socket链接dev/socket/logdw
  • 接收是通过在系统启动的时候执行system\core\logd\Main.cpp,然后创建LogListener对象监听socket为logdw,当有数据的时候会先封装,然后通过通知链接的客户端打印数据
  • LogReader监听/dev/socket/logdr上的socket客户端有数据就给客户端
  • CommandListener是一个管理类,里面封装了很多命令,可以操作log的输出,监听的是/dev/socket/logd
  • 对应线程的创建
    LogReader::onDataAvailable():logd.reader
    LogListener::onDataAvailable():logd.writer
    LogTimes::threadStart():logd.reader.per

也就是说:我们Java写一个日志调用,对应的设备文件是dev/socket/logdw,对应的监听器是LogListener,在LogListener中做的操作是将新的日志条目添加到LogBuffer中,并且通知LogReader更新日志条目,然后发送给链接的客户端

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值