[Android] 记一次FileDescriptor泄漏造成的Crash

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/y874961524/article/details/82908250

记一次FileDescriptor泄漏造成的Crash

问题描述

最近项目中一直偶现Native Crash,先看下log。

09-29 10:46:47.530 24328-24328/? A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 24328 (com.bw.pk) 
09-29 10:46:47.533 448-448/? I/MIUINDBG_HOOK: hook hook_sigtimedwait 
09-29 10:46:47.696 25962-25962/? A/DEBUG: * * 
Build fingerprint: 'xiaomi/mido/mido:7.0/NRD90M/V9.0.5.0.NCFCNEI:user/release-keys' 
Revision: '0' 
ABI: 'arm' 
pid: 24328, tid: 24328, name: com.bw.pk >>> com.bw.pk <<< 
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- 
09-29 10:46:47.714 25962-25962/? A/DEBUG: Abort message: 'FORTIFY: FD_SET: file descriptor >= FD_SETSIZE' 
r0 00000000 r1 00005f08 r2 00000006 r3 00000008 
r4 edb7c590 r5 00000006 r6 edb7c538 r7 0000010c 
r8 00000002 r9 0003555b sl ffa627e0 fp 00000002 
ip 0000000b sp ffa62520 lr ecef42b7 pc ecef6b38 cpsr 000e0010 
09-29 10:46:47.727 25962-25962/? A/DEBUG: backtrace: 
09-29 10:46:47.729 25962-25962/? A/DEBUG: #00 pc 00049b38 /system/lib/libc.so (tgkill+12) 
#01 pc 000472b3 /system/lib/libc.so (pthread_kill+34) 
#02 pc 0001d555 /system/lib/libc.so (raise+10) 
#03 pc 000190a1 /system/lib/libc.so (__libc_android_abort+34) 
#04 pc 00017104 /system/lib/libc.so (abort+4) 
#05 pc 0001b54f /system/lib/libc.so (__libc_fatal+22) 
#06 pc 0001b52f /system/lib/libc.so (__fortify_chk_fail+26) 
#07 pc 0004f44f /system/lib/libc.so (__FD_SET_chk+70) 
#08 pc 00000bdf /system/vendor/lib/libqti-perfd-client.so (mpctl_send+378) 
#09 pc 00000a25 /system/vendor/lib/libqti-perfd-client.so (perf_lock_acq+32) 
#10 pc 00000c57 /system/vendor/lib/libqti_performance.so 
#11 pc 000045dd /system/framework/oat/arm/QPerformance.odex (offset 0x4000)

分析

大概看了下,是libc.so抛出的,再仔细看,可以发现此问题的关键。

 Abort message: 'FORTIFY: FD_SET: file descriptor >= FD_SETSIZE' 

文件描述符(fd)超过上限,文件描述符上限这个在不同系统中有不同的定义,在Android设备上是1024个。

可以通过这个命令进行查看:
ulimit用于限制启动进程所占用的资源,使用-n可以查看每个进程可以打开的最大fd。

adb shell ulimit -n

这个问题就相当的棘手了,先简单介绍下UNIX系统中的文件描述符。

UNIX系统中,所有文件是通过文件描述符进行引用的,它通常是一个非负整数,标识一个进程正在进行访问文件。

下面我们查看当前进程的fd使用情况。
首先找到pid

ps | grep <包名>

查看进程fd占用

cd /proc/<pid>/fd
# 计算进程的fd占用情况
ls -l | wc -l

ls-l查看所有已占用的fd资源

统计总的占用情况

表示当前进程一共占用了153个fd。

解决

随后逐渐定位问题,发现一些场景,如列表滑动,会造成fd一直增长,没有释放。

经过排查,问题出现在OkHttp的连接没有关闭。应该这样改正。

我们看下OkHttp源码,为什么Response不Close会造成泄漏。

如果ResponseBody不进行close的话最终会导致SteamAllocation中的streamFinished没有调用,导致Socket没有关闭。

总结

file,socket,pipe之类的fd资源需要及时关闭,否则会引起Native层的Crash,并且问题极难排查。

通过/proc/<pid>/fd/目录下的文件,查看fd的占用情况。

参考资料:
https://github.com/square/okhttp/issues/2232
https://gist.github.com/jhansche/322965fa3c3a6179114bc3ac8e4337ec#file-watch-fds-sh
https://stackoverflow.com/questions/30521826/fortify-source-fd-set-file-descriptor-fd-setsize-calling-abort

展开阅读全文

没有更多推荐了,返回首页