在做android开发过程中,我们经常会用到logcat,通过logcat可以更直接的看出程序执行的顺序以及开发产生的日志信息,但是,我们在开发过程中也经常看到logcat输出read: Unexpected EOF!异常,然后日志就不在打印了。今天我们就分析一下为什么logcat会出现read: Unexpected EOF!异常,以及出现read: Unexpected EOF!异常后日志无法正常输出的原因。
分析
查阅先关资料,仔细分析后你会发现,出现这个日志,是因为最终的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();
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;
}
既然找到了原因,是否可以想办法扩充一下这个默认日志缓存区的大小了,当然是可以的。所以我们只需要将buffer size设置为2M/3M/4M......即可,下面我们看一下如何扩充缓存区。
如何扩充日志缓存区大小
首先,我们要先知道logcat常用的命令,以及常用命令的作用。我们打开系统的终端,或者开发工具idea的终端。然后输入adb shell logcat -help,常看常用的命令如下:
C:\Users\Administrator>adb shell logcat -help
Unrecognized Option h
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
-v <format>, --format=<format>
Sets the log print format, where <format> is:
brief color epoch long monotonic printable process raw
tag thread threadtime time uid usec UTC year zone
-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 <expr>, --regex=<expr>
Only print lines where the log message matches <expr>
where <expr> is a regular expression
-m <count>, --max-count=<count>
Quit after printing <count> 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 <count> Print only the most recent <count> lines (implies -d)
-t '<time>' Print most recent lines since specified time (implies -d)
-T <count> Print only the most recent <count> 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 <size>, --buffer-size=<size>
Set size of log ring buffer, may suffix with K or M.
-L, -last Dump logs from prior to last reboot
-b <buffer>, --buffer=<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=<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
<tag>[:priority]
where <tag> is a log component tag (or * for all) and priority is:
V Verbose (default for <tag>)
D Debug (default for '*')
I Info
W Warn
E Error
F Fatal
S Silent (suppress all output)
'*' by itself means '*:D' and <tag> by itself means <tag>:V.
If no '*' filterspec or -s on command line, all filter defaults to '*:V'.
eg: '*:S <tag>' prints only <tag>, '<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"
下面我就简单介绍几个常用命令,只有熟悉了这些命令,你才能更快更直接定位和解决问题。
- 查看buffer size 命令 :logcat -g -g命令可以查看当前设备日志缓存区 大小,打开终端输入logcat -g:
C:\Users\Administrator>adb logcat -g
main: ring buffer is 2Mb (1Mb consumed), max entry is 5120b, max payload is 4068b
system: ring buffer is 2Mb (634b consumed), max entry is 5120b, max payload is 4068b
crash: ring buffer is 2Mb (0b consumed), max entry is 5120b, max payload is 4068b
2.修改buffer size 命令:logcat -G<size>
-G<size>命令修改buffer size ,打开终端输入logcat -G<size>,如下图所示,buffer size 从2M变成了4M
注意:
修改和查看buffer日志缓存区大小都是用大英语字母“g”的,但是区别在于,修改是大写字母"G",而查看是小写字母“g”.
更多命令这里就不在一一介绍说明,言归正传继续解决read: Unexpected EOF。
解决read: Unexpected EOF方式:
通过上面的分析和对应logcat命令介绍,大概知道了如何解决read: Unexpected EOF这个问题,下面就介绍一下俩种常用2种方式:
1.logcat -G《size》命令解决
adb logcat -G 4m
2.手动修改系统设置
打开自己手中的调试手机或者调试设备,以此点击:开发者选项===》日志记录器缓冲区大小, 然后修改一下系统的日志缓冲区大小即可,如下图所示:

可以发现,系统默认的就是256kb,我们可以手动修改日志大小。至于使用哪种方式,可以根据具体实际的使用情况进行设置。