0


使用开源库libyuv中替换开源汇编接口,解决汇编接口中的崩溃问题

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;
}
标签: C++ Windbg 汇编代码

本文转载自: https://blog.csdn.net/chenlycly/article/details/126210270
版权归原作者 dvlinker 所有, 如有侵权,请联系我们删除。

“使用开源库libyuv中替换开源汇编接口,解决汇编接口中的崩溃问题”的评论:

还没有评论