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();//当前线程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更新日志条目,然后发送给链接的客户端
更多相关文章
- Android(安卓)5.0 API变化
- android 退出全部activity的方法
- [置顶] android 中使用回调函数
- android ListView本行控件操作本行其它控件的重要方法(绝对原创,本
- Android(安卓)登录页面(密码显示隐藏、EditText 图标切换、限制输
- Android(安卓)多线程通信 Handler
- Android中的SharedPreference源码整理总结
- android中打印函数调用栈、内存使用、屏幕分辨率
- android 科大讯飞语音唤醒demo