静态代码排查
1、v4l2 hal 绑定dma fd
如上时序图:
在app open camera 的时候,就会调用到camera的openCamera(),最后会调用到v4l2_wrapper的CreateInputSurface(), 在这个方法里面会根据当前为这个camera 分配对应的MAX_BUFFER_NUM
int V4L2Wrapper::Init() {
......
// Format changed, request new buffers.
if (isSRVMCamera()) {
res = RequestBuffers(MAX_SRVM_BUFFER_NUM);
}else {
res = RequestBuffers(MAX_BUFFER_NUM);
}
......
}
int V4L2Wrapper::RequestBuffers(uint32_t num_requested) {
......
int result = CreateInputSurfaces(num_requested);
......
}
int32_t V4L2Wrapper::CreateInputSurface(int32_t index) {
.......
v4l2_buffer device_buffer;
memset(&device_buffer, 0, sizeof(device_buffer));
device_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
device_buffer.memory = V4L2_MEMORY_MMAP;
device_buffer.index = index;
// Use QUERYBUF to ensure our buffer/device is in good shape,
// and fill out remaining fields.
if (IoctlLocked(VIDIOC_QUERYBUF, &device_buffer) < 0) {
HAL_LOGE("QUERYBUF fails: %s", strerror(errno));
return -ENODEV;
}
if (input_buffer_infos[index].isUsed) {
HAL_LOGE("ERROR: index:%d, isUsed:%d", index,
input_buffer_infos[index].isUsed);
// 这个input_buffer_infos后面会做c2d的时候用
input_buffer_infos[index].gpuAddr = NULL;
input_buffer_infos[index].virtPtr = NULL;
input_buffer_infos[index].surfaceId = 0;
input_buffer_infos[index].isUsed = false;
input_buffer_infos[index].id = 0;
if (input_buffer_infos[index].fd >= 0) {
close(input_buffer_infos[index].fd);
}
memset(&input_buffer_infos[index], 0, sizeof(buffer_info_t));
}
struct v4l2_exportbuffer expbuf = { 0 };
expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
expbuf.index = index;
expbuf.plane = 1;
expbuf.flags = O_CLOEXEC | O_RDWR;
// 这里就会调用到v4l2loopback里面,然后就会调用到ais_v4l2_proxy的processAllocBufs里面将最终用做数据传输的buffer fd返回给
input_buffer_infos
if (IoctlLocked(VIDIOC_EXPBUF, &expbuf) < 0)
{
HAL_LOGE("EXPBUF failed err= %s index %x", strerror(-errno), index);
return 1;
}
input_buffer_infos[index].width = mInputWidth;
input_buffer_infos[index].height = mInputHeight;
input_buffer_infos[index].cropWidth = mInputCropWidth;
input_buffer_infos[index].cropHeight = mInputCropHeight;
input_buffer_infos[index].cropLeft = mInputCropLeft;
input_buffer_infos[index].cropTop = mInputCropTop;
input_buffer_infos[index].halFormat = mInputHalFormat;
// 这里fd, 会关联到dma buffer -> GraphicBuffer -> window -> fd
input_buffer_infos[index].fd = expbuf.fd; //dup(device_fd_.get());
input_buffer_infos[index].length = device_buffer.length;
input_buffer_infos[index].offset = device_buffer.m.offset;
input_buffer_infos[index].id = index;
........
}
2、v4l2 hal 销毁dma fd
以上是销毁dma buffer 的流程,在调用close camera的时候就会去销毁dma buffer既销毁dma fd。
分析历程一:
在帧率正常的情况下,去切换流媒体(open)和sideview(close), 都会看到dma fd的减少和增加, 符合预期和流程
在帧率异常的情况下,sideview 在不断的被打开成功,但是没有帧率过来。随后buffer等待超时,返回camera failed给app, app 下发close 关闭 camera,按理说也会随着close camera ,去销毁dma fd,但是从检测的报告来看dma fd好像没有随着close camera减少,open camera的时候还增加了。
两者差异就在这个等待的buffer,但是代码静态分析,等待的请求buffer在close camera的时候也会随着清除掉。
分析历程二
发现只要是帧率异常,有超时上报就会有dma fd堆积,rvc camera也会出现此问题,通过lsof 查看该fd的占用情况,只有ais_v4l2_proxy 占用
在创建和销毁fd的地方都加上了log
最后发现这些未销毁的fd值,不在上面创建和销毁的log里面
分析历程三
在帧率正常的情况下,来回切换流媒体/sideview 发现dmabuf fd也会增加,那就推反了上面分析一、二,怀疑点就在open/close的情况会增加了,和生产者buffer堆积没有关系了。
再继续做静态代码分析,发现如下
Dmabuf fd间接来自GraphicBuffer转换而来,但是在销毁的时候虽然close了fd,但是只对calloc的地址进行了free,而没有对里面的对象清除
再销毁具体的GraphicBuffer对象后,发现dmabuf就没有再增加了。