1. 背景
这个软件涉及多个组织、多个人的开发成果,去年在项目A现场,炎热又湿润的海边气候,和J伙伴抢救过一次,这回又开始了。
她支持在1块Nvidia显卡上做视频解码、图像处理和D3D9绘制,去年v1.0版本在项目A的Windows7 + 2块显卡环境下,每个进程对应1个显卡时工作正常。
去年下半年,伙伴为项目B加入了几个新功能发布v1.1,通过了项目B的Windows 10单显卡环境测试和现场验证。现在给项目A升级后发现有1个进程失败,日志中显示CUDA错误:
1
CUDA_ERROR_INVALID_DEVICE: invalid device ordinal
软件各模块的大概关系:
- APP调用foo。
- foo库通过ffmpeg收流解复用,cuvid(基于CUDA)解码。
- foo库通过CUDA Driver分配显存。
- foo库通过bar处理解码后图像。
- foo库把最终图像渲染到对应显示器。
如下图:
2. 过程
第1天,2022/08/19,周五
- 中午接到编写其他文档的任务,因为要处理CUDA问题,跟关联方协调到下周三给出。
- 既然每次都发生,不难解决,只需不断缩小范围,直到定位到具体位置,攻克拿下。
- Google搜索结果集中在给CUDA Runtime或Driver的API,收到了错误的显卡索引。比对成功和失败进程的不同参数,发现使用0显卡成功,1显卡失败。通过在APP、FOO的各个API调用前后加入日志,排除参数传错。同时借此重新熟悉该软件的组成、代码关系,以及编码、测试的各个环节。
第2天,2022/08/20,周六
- 去除编译时数千个字符集警告,降为9个。
- 由于是FFMPEG提示
CUDA_ERROR_INVALID_DEVICE: invalid device ordinal
,比对v4.1.4和v4.4中cuviddec.c和hwcontext_cuda.c,区别主要是支持AV1和AV_CUDA_USE_PRIMARY_CONTEXT参数,在FOO中调整参数试验如旧。 - 物我两忘,D伙伴提醒水壶渗水跳闸了,中午听王德峰聊《六祖坛经》,风和幡还有你,都动了,心动说的乖巧是把人和物之间的信息传递割开,给予一个洞察自我存在的机缘。
- FOO库的自测软件经过多次加入临时代码,已经不好使用,所以一直在APP中验证。到下午还没有进展时,为缩小范围,修改自测软件跑起来。
- 伙伴给自测软件留下的逐个API测试功能非常赞,借此发现不调用BAR库API就不会导致FFMPEG提示错误。
- 自测时发现BAR库输出
cuda id is zero
,而期望是1,看上去有戏。
第3天,2022/08/22,周一
- BAR库作者W伙伴告知各个使用CUDA的地方有个CUDA Context概念,同时其某个关键API中要求传入指定显卡的索引,经过比对,发现果然是调用时没有如实传入正确参数。修改后即正常。
3. 答案
BAR库的新版API发生变更,支持指定显卡索引和其他参数,但FOO集成时没有相应修改,使得BAR内部认为工作在显卡0,所以出现显卡0上进程正常,显卡1上进程失败。
也就是说问题发生在BAR库,“损坏”了CUDA环境,导致FFMPEG在BAR损坏之后访问CUDA Driver时错误,FFMPEG是受害者,正因为第一故障点没有留下记录,导致许多精力放在了分析FFMPEG中cuda解码,偏离了方向。
4. 重新出发
应该早些想到该软件频发问题的故障点,是和关键的BAR库有关,着重分析对其调用的时序、参数,以及其内部的机理。
5. 改善点
- BAR库头文件中的API支持参数默认值。由于C语言不支持参数默认值,故作为DLL或SO这个层面的库,应避免使用,更显著地要求调用者验证API原型。
- 赶进度,没有明确要求v1.1的必要测试用例。这是我的问题,流程上的问题对品质的损害往往大于编码,因为编码着眼于局部,流程缺失的影响范围更大,挽救成本也更高。