一类fd leak的通用解法

本文档适用的一类问题特征如下:
1.出现Too many open files的crash
2.Too many files 里一直新增的文件指的是anon_inode:dmabuf.

现象: 有很多种操作手法,总体上是操作相机的时间够久都会出现,例如一直拍照等等.
致命的log:有很多种:
E mm-still: DoMmap:81] Ion map failed Too many open files
E mm-still: DoCacheOps:322] Ion fd failed to create Too many open files
Failed to clone native_handle in hidl_handle: Too many open files'
mm-still: OpenClient:37] Ion open failed Too many open files
共同的问题点是 "Too many open files"

1.查看pid
ps -A | grep "provider@"
查询到如下
cameraserver 22066 1 765604 266904 binder_thread_read f3d6c0a4 S  android.hardware.camera.provider@2.4-service
可知pid=22066

2.查看进程当前的open files 总数
Tiffany:/ # ls -al /proc/22066/fd/ | wc -l
47

3.观察到当一直拍照的时候,open files的总数会一直增加,到1024个的临界点时,crash了.

4.确认一直在打开的文件是什么文件.
Tiffany:/ # ls -al /proc/22066/fd/
可以查看到当前pid打开的文件句柄分别是哪些.
对比初始状态,会发现
进程一直在打开anon_inode:dmabuf这种文件.

5.经过做实验,发现anon_inode:dmabuf跟CameraDevice.cpp文件里的sGetMemory跟sPutMemory有关.
分配到的内存会以一个打开的fd来体现出来.
既然fd的个数一直在增加,那么肯定是get的次数大于put的次数导致的.

6.增加log定位问题
只需要在CameraDevice.cpp文件里新增log,就可以定位到是哪个pid导致的.

a. 先定义一个变量.
static int cnt = 0;

b. 在sGetMemory函数里对cnt++,打印cnt的值.
在sPutMemory函数里对cnt–.打印cnt的值.

c. 此时cnt的值代表的是Get跟Put的剪刀差.
差值如果一直是稳定在一个数值上,说明内存泄漏并不是因为Get跟Put导致的,需要另行解决.
差值如果一直在缓慢增长,说明Get的次数大于Put的次数,说明有内存泄漏.

d. 定位为什么Get的次数会大于Put的次数
在打印cnt值的同时,打印线程号tid,用getpid方法.再由tid的值,通过
sprintf(path, "/proc/%u/task/%u/comm", getpid(),gettid());
读取上面的文件内容,代表的是tid对应的线程名字.

e. 通过抓log,保存下来.
统计Get跟Put的差值,
再看看哪些线程再Get,哪些在Put.
正常情况下,CAM_img会不断Get,CAM_cbNotify会不断Put.
会大概找到是哪个线程引起的.本文档是因为JPEG_Enc引起的.

f. 佐证上述判断
打印Get到的内存的地址.
打印Put到的内存的地址.
会发现JPEG_Enc Get到的内存地址没有Put.而其他的内存地址有Get就会有Put.

g. 分析JPEG_Enc 为什么没有释放内存
打印出JPEG_Enc Get的调用栈,发现是在OMXJpegEncoderPipeline::CompleteMainImage里调用到的.查看代码,
发现在CompleteMainImage函数里会调用reserveJpegOutMem申请内存,接下来会调用startSwMemcpy函数,这个函数里调用ReleaseMainImage.
说明内存申请之后是有释放的.但是现象明显是没有释放的.
只能说明ReleaseMainImage函数里面没有释放的流程,或者有流程,但却没能跑到.
一直跟代码,会发现
1.OMXJpegEncoderPipeline::ReleaseMainImage
2.QOMXImageCodec::postMessage(OMX_MESSAGE_FTB_DONE);
3.OMXImageEncoder::processMessage
4.QOMXImageCodec::fillBufferDone 会调用指针
m_callbacks->FillBufferDone(m_compHandle, m_appData, m_currentOutBuffHdr);
5. mm_jpeg_fbd(mm_jpeg.c)
6.mm_jpeg_put_mem
跟踪到这个地方,发现代码流程出现问题.
观察这个变量: p_encode_job->ref_count,发现:
mm_jpeg_get_mem的时候,ref_count会由初始的0,++变为1
然后会调用mm_jpeg_put_mem,--变为0.
会再调用一次的mm_jpeg_put_mem,此时代码里对ref_count为0的处理内容是
LOGW("Buffer already released %d", p_encode_job->ref_count);
rc = -1;
警告内存已经被释放,但实际上,没有释放内存的操作.
把mm_jpeg_put_mem里真正释放内存的操作,增加到ref_count为0的处理中.
通过测试,发现问题得到解决.

总结如下:
1.增加log,查哪个线程导致的Get跟Put的剪刀差
2.增加log,跟流程,这个线程为什么有Get,却没Put
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值