R 上google 加入了一个fd leak 检测功能,基本实现如下:
SystemServer.java 中在system_server 进程创建一个Native 检测进程
spawnFdLeakCheckThread();
frameworks/base/services/core/jni/com_android_server_SystemServer.cpp
参见dlopen() ,加载该动态库,则会执行注册信号处理函数 __attribute__((constructor)) static void
ctor()
dlopen函数详解_u013531497的专栏-CSDN博客_dlopen函数
检测到文件句柄超支,则产生
raise(BIONIC_SIGNAL_FDTRACK); 信号;
定义信号:
bionic/libc/platform/bionic/reserved_signals.h
#define BIONIC_SIGNAL_FDTRACK (__SIGRTMIN + 7)
注册信号处理函数:
实现打印:
1、当sockFD=open(..,...) 返回<0 且错误码为 “-24” ,则发生了文件句柄超过最大值泄漏。
诸如 ”open,fopen,dlopen,pipe” 等系统调用出现fail,也就是返回值小于0的时候,可以怀疑是句柄泄露,但是我们还需要证实一下。
cat /proc/pid/limits 可以查看进程资源限制
查看确定fd数量 /proc/pid/fd
2、发下native 出现crash 但是没有打印对应的堆栈
通常情况exit(0) _exit(1)才不会打印堆栈,但是一般这样退出会打印一些提示信息
pipe_set_size(...){
if (nr_pages > pipe->buffers && 1129 (too_many_pipe_buffers_hard(user_bufs) || 1130 too_many_pipe_buffers_soft(user_bufs)) && 1131 is_unprivileged_user()) { ... }
}
内核会设置Pipe管道的大小,在这里出现了权限错误。权限异常需要满足如下3个条件:
1. 当前设置的Pipe管道大小超过了上一次设置过的或者默认的管道大小。
2. 当前用户组的所有Pipe管道的buffer总大小超过了软限制16*1024*4K或者硬限制(默认为0)。
3. “ 忽略资源限制 ”以及 “系统管理任务” 这2类权限都不具备(默认值)。
这里是根据用户组来分配pipe_buffer 的,某个进程泄漏pipe ,则会影响整个用户组。
这里可以使用FDTrack 功能,在main中创建thread,执行同com_android_server_SystemServer.cpp 中,fdtrack 在收到信号会打印fd 句柄及对应的堆栈。
进入 “/proc/pid/fd” 目录,执行 “ls -l | grep pipe > /data/leak_before.txt” 保存泄露前句柄信息;
目标进程创建一个线程,该线程间隔每5s运行一次,获取当前进程最大句柄数,当进程持有的最大句柄数超过阈值,打开 “libfdtrack.so” ;
复现句柄泄露问题
进程发送信号,”libfdtrack.so” 会将记录的FD堆栈信息打印出来。
“/proc/pid/fd” 目录,执行 “ls -l | grep pipe > /data/leak_after.txt” 保存泄露后句柄信息;
比较泄露前后句柄信息,找到泄露的句柄号,配合logcat打印出的FD堆栈信息即可顺利找到泄露点。