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();//当前线程idheader.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;//0buffer.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)

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

[email protected]:/ $ ps |grep logd                                               logd      220   1     33996  16500 sys_rt_sig 00000000 S /system/bin/logd--------------------------------------------------------------------------------[email protected]:/ $ ps -t|grep 220 system    221   220   33996  16296 futex_wait 00000000 S logd.daemonlogd      222   220   33996  16296 poll_sched 00000000 S logd.readerlogd      223   220   33996  16296 poll_sched 00000000 S logd.writerlogd      224   220   33996  16296 poll_sched 00000000 S logd.controllogd      226   220   33996  16296 poll_sched 00000000 S logd.auditdlogd      5193  220   33996  16296 futex_wait 00000000 S logd.reader.perlogd      14611 220   33996  16296 futex_wait 00000000 S logd.reader.perlogd      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(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(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 × = 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(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, ×Lock);    }    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=-1listen=falseuseCmdNum=flasevoid 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(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. Android(安卓)5.0 API变化
  2. android 退出全部activity的方法
  3. [置顶] android 中使用回调函数
  4. android ListView本行控件操作本行其它控件的重要方法(绝对原创,本
  5. Android(安卓)登录页面(密码显示隐藏、EditText 图标切换、限制输
  6. Android(安卓)多线程通信 Handler
  7. Android中的SharedPreference源码整理总结
  8. android中打印函数调用栈、内存使用、屏幕分辨率
  9. android 科大讯飞语音唤醒demo

随机推荐

  1. AndroidUI组件之RoomButton
  2. Android折线图
  3. Android(安卓)自定义控件 按钮滚动选择
  4. what is already installed?
  5. Android(安卓)App应用底部导航栏实现的一
  6. Android之Button样式
  7. How to Install Ubuntu on Android!
  8. android TabHost 对象报错
  9. android判断一个Service是否存在
  10. Android(安卓)MediaMuxer混合音频和视频