这篇博客,分析的是logd接收到logcat传来的命令,logd如何把log传给logcat。

一、logd LogReader监听logdr socket

在logd的main函数中会有一个监听logdr socket的LogReader类

我们来看下main函数的源码

[java] view plaincopy
  1. LogReader *reader = new LogReader(logBuf);
  2. if (reader->startListener()) {
  3. exit(1);
  4. }

再来看看LogReader的构造函数

[java] view plaincopy
  1. LogReader::LogReader(LogBuffer *logbuf) :
  2. SocketListener(getLogSocket(), true),
  3. mLogbuf(*logbuf) {
  4. }

getLogSocket来获取logdr的socket

[java] view plaincopy
  1. int LogReader::getLogSocket() {
  2. static const char socketName[] = "logdr";
  3. int sock = android_get_control_socket(socketName);
  4. if (sock < 0) {
  5. sock = socket_local_server(socketName,
  6. ANDROID_SOCKET_NAMESPACE_RESERVED,
  7. SOCK_SEQPACKET);
  8. }
  9. return sock;
  10. }

每次socket有请求数据都会调用onDataAvailable函数

[java] view plaincopy
  1. bool LogReader::onDataAvailable(SocketClient *cli) {
  2. ......
  3. uint64_t sequence = 1;
  4. // Convert realtime to sequence number
  5. if (start != log_time::EPOCH) {
  6. class LogFindStart {
  7. const pid_t mPid;
  8. const unsigned mLogMask;
  9. bool startTimeSet;
  10. log_time &start;
  11. uint64_t &sequence;
  12. uint64_t last;
  13. public:
  14. LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) :
  15. mPid(pid),
  16. mLogMask(logMask),
  17. startTimeSet(false),
  18. start(start),
  19. sequence(sequence),
  20. last(sequence) {
  21. }
  22. static int callback(const LogBufferElement *element, void *obj) {//回调
  23. LogFindStart *me = reinterpret_cast<LogFindStart *>(obj);
  24. if ((!me->mPid || (me->mPid == element->getPid()))
  25. && (me->mLogMask & (1 << element->getLogId()))) {
  26. if (me->start == element->getRealTime()) {
  27. me->sequence = element->getSequence();
  28. me->startTimeSet = true;
  29. return -1;
  30. } else {
  31. if (me->start < element->getRealTime()) {
  32. me->sequence = me->last;
  33. me->startTimeSet = true;
  34. return -1;
  35. }
  36. me->last = element->getSequence();
  37. }
  38. }
  39. return false;
  40. }
  41. bool found() { return startTimeSet; }
  42. } logFindStart(logMask, pid, start, sequence);
  43. logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
  44. logFindStart.callback, &logFindStart);//
  45. if (!logFindStart.found()) {
  46. if (nonBlock) {
  47. doSocketDelete(cli);
  48. return false;
  49. }
  50. sequence = LogBufferElement::getCurrentSequence();
  51. }
  52. }
  53. FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence);
  54. command.runSocketCommand(cli);
  55. return true;
  56. }

我们先看下LogBuffer的flushTo函数

[java] view plaincopy
  1. uint64_t LogBuffer::flushTo(
  2. SocketClient *reader, const uint64_t start, bool privileged,
  3. int (*filter)(const LogBufferElement *element, void *arg), void *arg) {
  4. LogBufferElementCollection::iterator it;
  5. uint64_t max = start;
  6. uid_t uid = reader->getUid();
  7. pthread_mutex_lock(&mLogElementsLock);
  8. if (start <= 1) {//初始打印的值
  9. // client wants to start from the beginning
  10. it = mLogElements.begin();
  11. } else {
  12. // Client wants to start from some specified time. Chances are
  13. // we are better off starting from the end of the time sorted list.
  14. for (it = mLogElements.end(); it != mLogElements.begin(); /* do nothing */) {
  15. --it;
  16. LogBufferElement *element = *it;
  17. if (element->getSequence() <= start) {
  18. it++;
  19. break;
  20. }
  21. }
  22. }
  23. for (; it != mLogElements.end(); ++it) {
  24. LogBufferElement *element = *it;
  25. if (!privileged && (element->getUid() != uid)) {
  26. continue;
  27. }
  28. if (element->getSequence() <= start) {
  29. continue;
  30. }
  31. // NB: calling out to another object with mLogElementsLock held (safe)
  32. if (filter) {//传入的回调用来过滤
  33. int ret = (*filter)(element, arg);
  34. if (ret == false) {
  35. continue;
  36. }
  37. if (ret != true) {
  38. break;
  39. }
  40. }
  41. pthread_mutex_unlock(&mLogElementsLock);
  42. // range locking in LastLogTimes looks after us
  43. max = element->flushTo(reader, this);//调用LogBufferElement的flushTo函数
  44. if (max == element->FLUSH_ERROR) {
  45. return max;
  46. }
  47. pthread_mutex_lock(&mLogElementsLock);
  48. }
  49. pthread_mutex_unlock(&mLogElementsLock);
  50. return max;
  51. }

而LogBufferElement的flushTo函数就是往logcat的socket写log了。

[java] view plaincopy
  1. uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent) {
  2. struct logger_entry_v3 entry;
  3. memset(&entry, 0, sizeof(struct logger_entry_v3));
  4. entry.hdr_size = sizeof(struct logger_entry_v3);
  5. entry.lid = mLogId;
  6. entry.pid = mPid;
  7. entry.tid = mTid;
  8. entry.sec = mRealTime.tv_sec;
  9. entry.nsec = mRealTime.tv_nsec;
  10. struct iovec iovec[2];
  11. iovec[0].iov_base = &entry;
  12. iovec[0].iov_len = sizeof(struct logger_entry_v3);
  13. char *buffer = NULL;
  14. if (!mMsg) {
  15. entry.len = populateDroppedMessage(buffer, parent);
  16. if (!entry.len) {
  17. return mSequence;
  18. }
  19. iovec[1].iov_base = buffer;
  20. } else {
  21. entry.len = mMsgLen;
  22. iovec[1].iov_base = mMsg;
  23. }
  24. iovec[1].iov_len = entry.len;
  25. uint64_t retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mSequence;
  26. if (buffer) {
  27. free(buffer);
  28. }
  29. return retval;
  30. }

我们来看看这逻辑,mMsg是当初java,c++写log文件后,通过socket到logd写log。每个log都有一个msg,保存在mMsg。正常情况下mMsg不为空。但是当为空的时候,我们需要把这个log填充,就调用populateDroppedMessage函数,最后这样的log会打印出类似这样的log。

[html] view plaincopy
  1. chatty  : uid=1000(system) RenderThread expire 3 lines

我们再回过头看LogReader中的回调:

[java] view plaincopy
  1. static int callback(const LogBufferElement *element, void *obj) {//回调
  2. LogFindStart *me = reinterpret_cast<LogFindStart *>(obj);
  3. if ((!me->mPid || (me->mPid == element->getPid()))
  4. && (me->mLogMask & (1 << element->getLogId()))) {
  5. if (me->start == element->getRealTime()) {
  6. me->sequence = element->getSequence();
  7. me->startTimeSet = true;
  8. return -1;
  9. } else {
  10. if (me->start < element->getRealTime()) {
  11. me->sequence = me->last;
  12. me->startTimeSet = true;
  13. return -1;
  14. }
  15. me->last = element->getSequence();
  16. }
  17. }
  18. return false;
  19. }

这个回调中没有返回true,说明在LogBuffer的flushTo函数中,执行到filter就执行不下去了。

[java] view plaincopy
  1. for (; it != mLogElements.end(); ++it) {
  2. LogBufferElement *element = *it;
  3. if (!privileged && (element->getUid() != uid)) {
  4. continue;
  5. }
  6. if (element->getSequence() <= start) {
  7. continue;
  8. }
  9. // NB: calling out to another object with mLogElementsLock held (safe)
  10. if (filter) {
  11. int ret = (*filter)(element, arg);
  12. if (ret == false) {
  13. continue;
  14. }
  15. if (ret != true) {
  16. break;
  17. }
  18. }

所以我们继续分析LogReader的onDataAvailable函数:

[java] view plaincopy
  1. FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence);
  2. command.runSocketCommand(cli);

调用了runSocketCommand函数:

[java] view plaincopy
  1. void FlushCommand::runSocketCommand(SocketClient *client) {
  2. LogTimeEntry *entry = NULL;
  3. LastLogTimes × = mReader.logbuf().mTimes;
  4. LogTimeEntry::lock();
  5. LastLogTimes::iterator it = times.begin();
  6. while(it != times.end()) {
  7. entry = (*it);
  8. if (entry->mClient == client) {//看传进来的client是否是同一个。
  9. entry->triggerReader_Locked();//唤醒正在传log的线程
  10. if (entry->runningReader_Locked()) {
  11. LogTimeEntry::unlock();
  12. return;
  13. }
  14. entry->incRef_Locked();
  15. break;
  16. }
  17. it++;
  18. }
  19. if (it == times.end()) {
  20. // Create LogTimeEntry in notifyNewLog() ?
  21. if (mTail == (unsigned long) -1) {
  22. LogTimeEntry::unlock();
  23. return;
  24. }
  25. entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid, mStart);
  26. times.push_front(entry);
  27. }
  28. client->incRef();
  29. // release client and entry reference counts once done
  30. entry->startReader_Locked();
  31. LogTimeEntry::unlock();
  32. }

我们再来看LogTimeEntry的startReader_Locked函数

[java] view plaincopy
  1. void LogTimeEntry::startReader_Locked(void) {
  2. pthread_attr_t attr;
  3. threadRunning = true;
  4. if (!pthread_attr_init(&attr)) {
  5. if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
  6. if (!pthread_create(&mThread, &attr,
  7. LogTimeEntry::threadStart, this)) {//开启线程
  8. pthread_attr_destroy(&attr);
  9. return;
  10. }
  11. }
  12. pthread_attr_destroy(&attr);
  13. }
  14. threadRunning = false;
  15. if (mClient) {
  16. mClient->decRef();
  17. }
  18. decRef_Locked();
  19. }

threadStart函数代码如下:

[java] view plaincopy
  1. void *LogTimeEntry::threadStart(void *obj) {
  2. prctl(PR_SET_NAME, "logd.reader.per");
  3. LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
  4. pthread_cleanup_push(threadStop, obj);
  5. SocketClient *client = me->mClient;
  6. if (!client) {
  7. me->error();
  8. return NULL;
  9. }
  10. LogBuffer &logbuf = me->mReader.logbuf();
  11. bool privileged = FlushCommand::hasReadLogs(client);
  12. me->leadingDropped = true;
  13. lock();
  14. while (me->threadRunning && !me->isError_Locked()) {
  15. uint64_t start = me->mStart;
  16. unlock();
  17. if (me->mTail) {
  18. logbuf.flushTo(client, start, privileged, FilterFirstPass, me);//第一次调用只是获取有多少条log
  19. me->leadingDropped = true;
  20. }
  21. start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);//调用LogBuffer的flushTo函数,发送要选择的log
  22. lock();
  23. if (start == LogBufferElement::FLUSH_ERROR) {
  24. me->error_Locked();
  25. }
  26. if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
  27. break;
  28. }
  29. me->cleanSkip_Locked();
  30. pthread_cond_wait(&me->threadTriggeredCondition, ×Lock);//挂起线程
  31. }
  32. unlock();
  33. pthread_cleanup_pop(true);
  34. return NULL;
  35. }

主要循环调用LogBuffer的flushTo函数,然后挂起线程,直到下个同样的socket client请求来到,然后会唤醒这个线程,就会继续调用LogBuffer的flushTo。

二、总结

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

原文地址: http://blog.csdn.net/kc58236582/article/details/51077497

android 6.0 logcat机制(三)logd处理请求log相关推荐

  1. android 6.0 logcat机制(一)java层写log,logd接受log

    第一篇博客,讲的主要是c++,java中打印log,然后通过socket传给logd,然后logd是如何处理接受log的. 一.logcat常用命令 logcat -c 清除已有log信息 logca ...

  2. android 6.0 log,android 6.0 logcat机制(二)logcat从logd中获取log保存到文件中

    一.设置保存log文件的路径 在手机刚开机的时候,会有类似如下命令执行 /system/bin/logcat -r 5120 -v threadtime -v usec -v printable -n ...

  3. android 6.0 logcat机制(二)logcat从logd中获取log保存到文件中

    这篇博客分析的是logcat是如何获取logd中的log,然后写入文件. 一.设置保存log文件的路径 在手机刚开机的时候,会有类似如下命令执行 /system/bin/logcat -r 5120 ...

  4. android 6.0 log,android 6.0 logcat机制(三)logd处理请求log

    一.logd LogReader监听logdr socket 在logd的main函数中会有一个监听logdr socket的LogReader类 我们来看下main函数的源码 LogReader * ...

  5. Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析-[Android取经之路]

    摘要:本节主要来讲解Android10.0 logd.logcat读写日志源码内容 阅读本文大约需要花费20分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Andro ...

  6. Android 10.0 PackageManagerService(三)APK扫描-[Android取经之路]

    摘要:上一节讲解了PKMS的 权限扫描,扫描/system/etc/permissions中的xml,存入相应的结构体中,供之后权限管理使用. 这一节主要来讲讲APK的扫描. 阅读本文大约需要花费15 ...

  7. Android 6.0 PM机制系列(四) APK安装需要空间分析

    前言 在Android 9.0 PM机制系列(四) APK安装需要空间分析文章中,我们重点分析了Android9.0需要的最小APK安装存储空间大小.结论就是:只要系统空间小于Math.min(get ...

  8. 深入理解 Android 9.0 Crash 机制(二)

    极力推荐Android 开发大总结文章:欢迎收藏 Android 开发技术文章大总结 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容: 九. AppError ...

  9. Android 8.0 VDEX机制简介

    背景 Android 8.0在odex的基础上又引入了vdex机制,目的是为了降低dex2oat时间. 因为当系统ota后,用户自己安装的应用是不会发生任何变化的,但framework代码已经发生了变 ...

最新文章

  1. Bootstrap学习记录-2.container和table
  2. JZOJ 5419. 【NOIP2017提高A组集训10.24】筹备计划
  3. 【模板】 全排列 有重复元素的全排列
  4. Selenium入门11 滚动条控制(通过js)
  5. 8. Dropout and Strides For Larger Models
  6. SQL敲了mySQL变了_MySQL-Front肿么导出SQL文件
  7. Charting for WinForms控件发布v3.5版本
  8. python面对对象编程写一个程序有一个汽车_汽车类面向对象编程Python
  9. C++ Qt学习笔记(3)QT中的文本处理
  10. hidefocus小技巧
  11. Leetcode 266.回文排列
  12. JAVA RSA加密解密代码范例(Base64版)
  13. 路由器k2固件改系统时间
  14. 利用Python查询IP地址
  15. TCP SYN洪水 (SYN Flood) 攻击原理与实现
  16. 无线网络有信号显示未连接网络连接服务器,路由器无线网络受限制或无连接怎么办...
  17. 在标准IO库中,rewind函数作用?
  18. 被误解的 Node.js
  19. Xunsearch与Sphinx的预比较
  20. a全球及中国电子手环行业销售现状及竞争趋势预测报告2022-2027年

热门文章

  1. Android 自定义组件随着手指自动画圆
  2. Jquery获取了元素
  3. ASP.NET 3.5 Extensions预览版即将发布
  4. Go 语言框架 Gin 练习2
  5. Git学习笔记:分支管理3
  6. 二十万字C/C++、嵌入式软开面试题全集宝典十一
  7. matlab cell类型数组存至txt文件
  8. 科大星云诗社动态20210815
  9. [我的1024开源程序]350元写的HTML5程序
  10. 十三、“词短情长书不尽,桃花潭水是我心。”(2021.2.12)