• 分析解决logcat报read: Unexpected EOF


    0. 前言

      在做android开发调试过程中,我们经常会用到logcat,通过logcat可以更直接的看出程序执行的顺序以及开发产生的日志信息,但是,今天客户遇到一个问题,logcat提示read: Unexpected EOF!异常,然后日志就不在打印了。今天我们就分析一下为什么logcat会出现read: Unexpected EOF异常,以及出现read: Unexpected EOF异常后日志无法正常输出的原因。

    1. 分析

      查阅先关资料,仔细分析后你会发现,出现这个日志,是因为最终的logcat进程退出,而退出的的原因是log buffer size设置过小导致,而默认size为256KB,如果你的程序长时间运行,并且产生了大量的日志,最终日志缓存的大小肯定是超过了默认的256kb。

    分析大致过程如下:

    1. 在应用或者服务等进程 往logd中写入log量过大时(大于buffer size设置的2倍),logd会调用kickMe函数,这里面会去判断stats size即系统中实际需要占用的大小,当大于2倍我们在init函数中设定的默认buffer size(64KB)时,Logd认为reader读取数据的速度过慢,会主动release_Locked函数尝试断开连接,断开连接后会导致logd.reader.per线程while循环break退出,Logd.cpp -> kickMe函数部分代码:

      void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
          if (stats.sizes(id) > (2 * log_buffer_size(id))) {  // +100%
              // A misbehaving or slow reader has its connection
              // dropped if we hit too much memory pressure.
              me->release_Locked();
      
      • 1
      • 2
      • 3
      • 4
      • 5
    2. logd.reader.per线程线程退出后,会调用SocketListener监听类的SocketListener::release,logd开启的LogReader是继承自SocketListener,会调用到doSocketDelete,SocketClient相关联的decRef函数,mRefCount—减值后会调用到~SocketClient析构函数,析构后会调用close(mSocket) 关闭SocketListener端的socket连接。

    3. 导致最终Logcat端进程的while循环中android_logger_list_read读取到的数据为0,logcat进程主动调用logcat_panic进程,logcat进程退出。

       while (!context->stop &&
                 (!context->maxCount || (context->printCount < context->maxCount))) {
              struct log_msg log_msg;
              int ret = android_logger_list_read(logger_list, &log_msg);
              if (!ret) {
                    fprintf(stderr, "android_logger_list_read error ,ret:%d !\n", ret);
                  logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
                  break;
              }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

      既然找到了原因,是否可以想办法扩充一下这个默认日志缓存区的大小了,当然是可以的。所以我们只需要将buffer size设置为2M/3M/4M…即可,下面我们看一下如何扩充缓存区。

    2. 解决方案

    2.1 logcat命令

      第一种方案,我们要先知道logcat常用的命令,以及常用命令的作用。我们打开系统的终端,或者开发工具idea的终端。然后输入adb shell logcat -help,常看常用的命令如下:

    Usage: logcat [options] [filterspecs]
    options include:
      -s              Set default filter to silent. Equivalent to filterspec '*:S'
      -f <file>, --file=<file>               Log to file. Default is stdout
      -r <kbytes>, --rotate-kbytes=<kbytes>
                      Rotate log every kbytes. Requires -f option
      -n <count>, --rotate-count=<count>
                      Sets max number of rotated logs to <count>, default 4
      --id=<id>       If the signature id for logging to file changes, then clear
                      the fileset and continue
      -v <format>, --format=<format>
                      Sets log print format verb and adverbs, where <format> is:
                        brief help long process raw tag thread threadtime time
                      and individually flagged modifying adverbs can be added:
                        color descriptive epoch monotonic printable uid
                        usec UTC year zone
                      Multiple -v parameters or comma separated list of format and
                      format modifiers are allowed.
      -D, --dividers  Print dividers between each log buffer
      -c, --clear     Clear (flush) the entire log and exit
                      if Log to File specified, clear fileset instead
      -d              Dump the log and then exit (don't block)
      -e , --regex=
                      Only print lines where the log message matches 
                      where  is a Perl-compatible regular expression
      -m , --max-count=
                      Quit after printing  lines. This is meant to be
                      paired with --regex, but will work on its own.
      --print         Paired with --regex and --max-count to let content bypass
                      regex filter but still stop at number of matches.
      -t       Print only the most recent  lines (implies -d)
      -t '<time>'     Print most recent lines since specified time (implies -d)
      -T       Print only the most recent  lines (does not imply -d)
      -T '<time>'     Print most recent lines since specified time (not imply -d)
                      count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'
                      'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format
      -g, --buffer-size                      Get the size of the ring buffer.
      -G , --buffer-size=
                      Set size of log ring buffer, may suffix with K or M.
      -L, --last      Dump logs from prior to last reboot
      -b , --buffer=         Request alternate ring buffer, 'main',
                      'system', 'radio', 'events', 'crash', 'default' or 'all'.
                      Multiple -b parameters or comma separated list of buffers are
                      allowed. Buffers interleaved. Default -b main,system,crash.
      -B, --binary    Output the log in binary.
      -S, --statistics                       Output statistics.
      -p, --prune     Print prune white and ~black list. Service is specified as
                      UID, UID/PID or /PID. Weighed for quicker pruning if prefix
                      with ~, otherwise weighed for longevity if unadorned. All
                      other pruning activity is oldest first. Special case ~!
                      represents an automatic quicker pruning for the noisiest
                      UID as determined by the current statistics.
      -P '<list> ...', --prune='<list> ...'
                      Set prune white and ~black list, using same format as
                      listed above. Must be quoted.
      --pid=     Only prints logs from the given pid.
      --wrap          Sleep for 2 hours or when buffer about to wrap whichever
                      comes first. Improves efficiency of polling by providing
                      an about-to-wrap wakeup.
    
    filterspecs are a series of 
      [:priority]
    
    where  is a log component tag (or * for all) and priority is:
      V    Verbose (default for )
      D    Debug (default for '*')
      I    Info
      W    Warn
      E    Error
      F    Fatal
      S    Silent (suppress all output)
    
    '*' by itself means '*:D' and  by itself means :V.
    If no '*' filterspec or -s on command line, all filter defaults to '*:V'.
    eg: '*:S <tag>' prints only , '<tag>:S' suppresses all <tag> log messages.
    
    If not specified on the command line, filterspec is set from ANDROID_LOG_TAGS.
    
    If not specified with -v on command line, format is set from ANDROID_PRINTF_LOG
    or defaults to "threadtime"
    
    -v <format>, --format=<format> options:
      Sets log print format verb and adverbs, where <format> is:
        brief long process raw tag thread threadtime time
      and individually flagged modifying adverbs can be added:
        color descriptive epoch monotonic printable uid usec UTC year zone
    
    Single format verbs:
      brief      — Display priority/tag and PID of the process issuing the message.
      long       — Display all metadata fields, separate messages with blank lines.
      process    — Display PID only.
      raw        — Display the raw log message, with no other metadata fields.
      tag        — Display the priority/tag only.
      thread     — Display priority, PID and TID of process issuing the message.
      threadtime — Display the date, invocation time, priority, tag, and the PID
                   and TID of the thread issuing the message. (the default format).
      time       — Display the date, invocation time, priority/tag, and PID of the
                 process issuing the message.
    
    Adverb modifiers can be used in combination:
      color       — Display in highlighted color to match priority. i.e. VERBOSE
                    DEBUG INFO WARNING ERROR FATAL
      descriptive — events logs only, descriptions from event-log-tags database.
      epoch       — Display time as seconds since Jan 1 1970.
      monotonic   — Display time as cpu seconds since last boot.
      printable   — Ensure that any binary logging content is escaped.
      uid         — If permitted, display the UID or Android ID of logged process.
      usec        — Display time down the microsecond precision.
      UTC         — Display time as UTC.
      year        — Add the year to the displayed time.
      zone        — Add the local timezone to the displayed time.
      ""    — Print using this public named timezone (experimental).
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

      重点我们要找到与logd buffer size相关的命令:

    1. 查看buffer size 命令 :logcat -g
      -g命令可以查看当前设备日志缓存区 大小

      console:/ $ logcat -g 
      main: ring buffer is 2Mb (0b consumed), max entry is 5120b, max payload is 4068b
      system: ring buffer is 2Mb (0b consumed), max entry is 5120b, max payload is 4068b
      crash: ring buffer is 2Mb (0b consumed), max entry is 5120b, max payload is 4068b
      
      • 1
      • 2
      • 3
      • 4
    2. 修改buffer size 命令:logcat -G < size >
      -G命令修改buffer size ,打开终端输入logcat -G,如下图所示,buffer size 从2M变成了4M
      在这里插入图片描述

    2.2 属性控制

    上述logcat命令仅是临时生效,若要修改默认值需要通过属性控制
    在这里插入图片描述
    属性的控制逻辑,后面会通过代码分析。

  • 相关阅读:
    小学生python游戏编程arcade----可旋转的坦克的发射子弹
    Git多人开发解决冲突案例
    源码解析FlinkKafkaConsumer支持punctuated水位线发送
    华为---企业WLAN组网基本配置示例---AC+AP组网
    vulhub中GitLab 远程命令执行漏洞复现(CVE-2021-22205)
    循环服务器
    vue重新进入页面,js不加载
    jbase实现通用码表
    机器学习笔记:轨迹驻留点 staypoint
    vmware虚拟机(ubuntu)&远程开发&golang、python环境安装
  • 原文地址:https://blog.csdn.net/IT_xiao_bai0516/article/details/126480105