由MATLAB MEX函数中的“优化输出”值引起的GCC段故障

问题描述 投票:4回答:1

我正在尝试编写一个相当简单的递归泛洪算法(以MATLAB mex函数的形式运行),但是在GCC中打开优化标志(如果重要,请参见v 7.5.0)时遇到了问题。该代码可以在没有打开任何优化标志的情况下正常运行,但是当我使用-O2或-O3标志时会出现段错误。我将罪魁祸首缩小到了由GCC优化的索引变量-如果我将其指定为volatile变量,则即使在更高的优化级别上也不会发生段错误。我认为我必须使用未定义的行为,但是我看不到可能在哪里发生。

令人反感的代码段:

#include "mex.h"
#include <string.h>

//     Removing this causes the program to segfault -----v
void fill(double *image, signed int x, signed int n, volatile signed int i, double k)
{
    image[i] = k;
    if ((i-1) >= 0 && ((i-1) % x) < (i % x) && image[i-1]==1)
        fill(image,x,n,i-1,k);
    if ((i-x) >= 0 && image[i-x]==1)
        fill(image,x,n,i-x,k);
    if ((i+1) < n && ((i+1) % x) > (i % x) && image[i+1]==1)
        fill(image,x,n,i+1,k);
    if ((i+x) < n && image[i+x]==1)
        fill(image,x,n,i+x,k);
}

// image is a 1D array holding a 2D image of size <x,y>
void flood(double *image, signed int x, signed int y)
{
    signed int n = x*y;
    signed int i = 0;
    double k = 2;

    while (i < n)
    {
        while(i<n && image[i] != 1) ++i;
        if(i>=n) return;
        fill(image,y,n,i,k);
        ++k;
        ++i;
    }
}

void mexFunction(int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[])
{
    int n;
    double *image;
    size_t x, y;

    if(nrhs!=1)
    {
        mexErrMsgIdAndTxt("floodfill:nrhs","One input required.");
    }

    if(nlhs!=1)
    {
        mexErrMsgIdAndTxt("floodfill:nlhs","One output required.");
    }

    if( !mxIsDouble(prhs[0]) || 
        mxIsComplex(prhs[0]))
    {
        mexErrMsgIdAndTxt("floodfill:doubleMatrix","Input 1 must be real double matrix.");
    }

    x = mxGetM(prhs[0]);
    y = mxGetN(prhs[0]);
    plhs[0] = mxCreateDoubleMatrix( (mwSize)x, (mwSize)y, mxREAL);
    image = mxGetPr(plhs[0]);

    memcpy(image,mxGetPr(prhs[0]),sizeof(double)*x*y);

    flood(image,y,x);
}

最后的样板是允许从MATLAB编译和传递数据(这是针对MATLAB MEX函数的)。 GDB和Valgrind都说segfault发生在fill函数中,但是没有指定确切的位置-我必须从MATLAB调用它,因此输出有些混乱。 Valgrind指出发生段错误的原因是“地址0x27E33F70的映射区域的权限错误”。

据我所知,代码不应该 segfault-在访问数组image之前,我总是进行边界检查,并且创建的数组的大小为x*y==n。最让我困惑的是,如果我将i指定为volatile,则代码可以正常工作,这表明GCC可能在优化我的边界检查之一。我意识到我可以保持原样,但我担心这可能表明存在更大的问题,以后可能会再次引起我的注意。

作为附录,我尝试剥离MATLAB代码并在MATLAB之外运行它,但这导致不再发生此问题。我不知道添加的代码是否会使GCC进行不同的编译。但是,这不是解决方案,因为它需要在MATLAB内部运行。

c matlab gcc segmentation-fault mex
1个回答
0
投票

根据我的经验,与通过调试器运行程序相比,打开AddressSanitizer进行编译是查找问题提示的更好的方法。只需将-fsanitize=address添加到GCC命令行即可。启动MATLAB时,您可能需要预加载ASan库:LD_PRELOAD=/path/to/asan/runtime/lib matlab

我的直觉,因为似乎没有一种方法可以超出范围,所以问题是堆栈溢出。尽管很难理解,但comment by OP确认了这一点。添加volatile参数或printf语句会极大地影响优化器如何更改生成的程序集。

除非用尽所有其他解释,否则永远不要怪编译器。但是,这种行为肯定是由编译器中的错误引起的。如果代码有问题,我看不到。

另一方面,在非递归函数中使用自己的堆栈编写泛洪填充算法通常要好得多。我发现它使代码更高效且更易于阅读。

© www.soinside.com 2019 - 2024. All rights reserved.