使用开源库libyuv中替换开源汇编接口,解决汇编接口中的崩溃问题
Posted dvlinker
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用开源库libyuv中替换开源汇编接口,解决汇编接口中的崩溃问题相关的知识,希望对你有一定的参考价值。
目录
4、在windbg设置pdb文件路径,查看详细的函数调用堆栈,进一步分析
C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...) https://blog.csdn.net/chenlycly/article/details/125529931 有次软件在某台PC上运行发生崩溃,发现崩溃在纯汇编代码实现的函数中,该接口中的汇编代码是开源的。后来使用开源的libyuv库中的接口替代了该接口的调用,解决了问题。本文详细地讲解一下这个崩溃的排查过程及解决办法。
1、概述
有客户反馈在其PC机上使用我们的软件,会有频繁的崩溃问题,但其他同事的电脑上则没有这样的问题,都能正常使用。于是和客户联系,取来了软件中内置的异常捕获模块在崩溃时捕捉到的dump文件,然后使用windbg对dump文件进行分析。下面来详细讲述一下这个问题的分析过程。
2、初步分析
用windbg工具打开dump文件之后,输入.ecxr切换到异常的上下文,然后输入kn命令查看函数调用堆栈,发现崩溃发生在音视频编解码库的yv12_to_yuyv_xmm接口中,如下所示:
函数调用堆栈中居然只有一行函数调用记录,有点奇怪。
3、查找相关库的pdb文件
我们想看看具体是崩溃在yv12_to_yuyv_xmm函数的哪一行代码上,于是要取来对应dll库的pdb文件。先使用lm命令查看dll库的时间戳,如下:
这个库的时间戳为2020年4月11日15点47分45秒。这个库是底层的音视频编解码组发布过来的,于是到对应软件版本的项目代码流中查看该dll库的发布记录,找到2020年4月11日的发布记录,发布记录中会标出dll等文件在服务器上的路径:
通过这个路径中:
找到dll对应的pdb文件。
4、在windbg设置pdb文件路径,查看详细的函数调用堆栈,进一步分析
在windbg设置pdb文件目录之后,需要再执行.excr切换到异常上下文中,即切换到发生异常的那个线程中,然后输入kn命令就可以查看发生异常时的线程的函数调用堆栈了。
在设置pdb文件目录时即使勾选了reload选项:
这样windbg就会到路径中去自动加载pdb文件了。不过,可能相关模块的pdb没有加载起来,可以使用lm命令去查看目标模块的pdb有没有加载起来,如果已经加载,则会将加载的pdb路径显示出来,如下所示:
如果没加载起来,则不会显示路径,此时就需要使用.reload命令去强制加载了,比如:.reload /f directui.dll,其中/f参数表示强制加载,后面的模块名称必须是完整的带后缀的模块名称。
最开始,没加载pdb文件时,函数调用堆栈中就显示了一行记录。加载pdb后就显示了多行记录:
但最后一帧中的yv12_to_yuyv_xmm函数中还是没有显示行号,这个有点奇怪。
于是到维护该dll库的同事那边,去查看该dll库的源代码,发现yv12_to_yuyv_xmm函数不是C++实现的,是直接使用汇编实现的,应该是为了提升代码的执行效率。
一般我们会在一些对执行效率要求比较高的代码中嵌入汇编代码,提高代码的执行效率,汇编代码的执行效率是最高的。比如我们在处理音视频编解码的算法代码中,时常会嵌入一些汇编代码,以提高代码的运行速度。
从源码中看到,yv12_to_yuyv_xmm函数的实现放置在colorspace_yuyv_mmx.asm汇编文件中,查看该文件内容:
得知该汇编文件是开源的,并且asm文件的实现代码是2004年出的,距今已经有十几年了,可能在处理部分设备出来的图像数据时会出异常,本案例中该函数中就出现了崩溃。
为什么其他机器上没有崩溃,就这台PC上在运行到yv12_to_yuyv_xmm函数会出现崩溃呢?
这个yv12_to_yuyv_xmm函数的代码是2004年实现的,距今已经有十几年了,可能对某写USB摄像头采集到的图像数据不兼容,肯能出问题的机器上的摄像头采集出来的图像数据有些特别之处,所以导致了yv12_to_yuyv_xmm函数内部发生了崩溃!
5、解决办法
开源代码出现bug,并且是汇编代码,汇编代码上下文很难看懂,冒然去修改可能会产生更大的问题。
yv12_to_yuyv_xmm函数是实现色彩空间转换的,后来想到可以使用开源的libyuv库中的相关接口来替代,使用开源libyuv的最新版本,该库的稳定性是有保证的,应该比较保险的,应该不会引入其他问题的,当然这个也需要测试验证的。
libyuv是Google开源的实现各种YUV与RGB之间相互转换、旋转、缩放的库。它是跨平台的,可在Windows、Linux、Mac、android等操作系统。x86、x64、arm架构上进行编译执行,支持SSE、AVX、NEON等SIMD指令加速。
于是下载了libyuv最新版本的开源库,引入到我们的工程,使用libyuv库中的I420ToYUY2替换yv12_to_yuyv_xmm函数。为了方便使用,我们可以对libyuv库的I420ToYUY2接口进行简单的封装,如下所示:
int libyuv_I420ToYUY2(const u8* src_y, int src_stride_y,
const unsigned char* src_u, int src_stride_u,
const unsigned char* src_v, int src_stride_v,
unsigned char* dst_frame, int dst_stride_frame,
int width, int height)
if (src_y == NULL || src_u == NULL || src_v == NULL || dst_frame == NULL)
LogError("Input param is invalid. src_y=0x%p,src_u=0x%p,src_v=0x%p,dst_frame=0x%p\\n", src_y, src_u, src_v, dst_frame);
return -1;
if (src_stride_y <= 0 || src_stride_u <= 0 || src_stride_v <= 0 || dst_stride_frame <= 0 || width <= 0 || height <= 0)
LogError("Input param is invalid. src_stride_y=%d, src_stride_u=%d, src_stride_v=%d, dst_stride_frame=%d, width=%d, height=%d\\n",
src_stride_y, src_stride_u, src_stride_v, dst_stride_frame, width, height);
return -2;
int nRet = I420ToYUY2(src_y, src_stride_y,
src_u, src_stride_u,
src_v, src_stride_v,
dst_frame, dst_stride_frame,
width, height);
return nRet;
以上是关于使用开源库libyuv中替换开源汇编接口,解决汇编接口中的崩溃问题的主要内容,如果未能解决你的问题,请参考以下文章