分段错误 'gsl_spmatrix_add'

segmentation fault 'gsl_spmatrix_add'

编辑: 我已将问题更改为产生相同错误的新代码,并且这样做更可靠。

一段时间以来,我一直在努力寻找代码中的分段错误,并将其归结为以下代码:

#include <gsl/gsl_spmatrix.h>

#include <iostream>

using namespace std;

void test_gsl() {
    size_t size = 5;
    size_t nzmax = 5 * 5;
    constexpr size_t threads = 5;

    // allocate
    gsl_spmatrix* thread_matrices[threads];
    for (size_t thread = 0; thread < threads; thread++) {
        thread_matrices[thread] = gsl_spmatrix_alloc_nzmax(size, size, nzmax, GSL_SPMATRIX_TRIPLET);
    }

    // set
    for (size_t i = 0; i < threads; i++) {
        gsl_spmatrix_set(thread_matrices[i], 0, 0, 1.0);
    }

    // crs
    for (size_t i = 0; i < threads; i++) {
        gsl_spmatrix* temp = thread_matrices[i];
        thread_matrices[i] = gsl_spmatrix_crs(thread_matrices[i]);
        gsl_spmatrix_free(temp);
    }

    // add to total
    gsl_spmatrix* total_matrix = gsl_spmatrix_alloc_nzmax(size, size, nzmax, GSL_SPMATRIX_CRS);
    gsl_spmatrix* total_copy = gsl_spmatrix_alloc_nzmax(size, size, nzmax, GSL_SPMATRIX_CRS);
    for (size_t i = 0; i < threads; i++) {
        gsl_spmatrix_memcpy(total_copy, total_matrix);  // this is required to avoid another segfault
        gsl_spmatrix_add(total_matrix, total_copy, thread_matrices[i]); // unknown segfault!
    }

    gsl_spmatrix_free(total_matrix);
    gsl_spmatrix_free(total_copy);
}

int main(int argc, char* argv[]) {
    
    test_gsl();
    printf("end\n");

    return 0;
}

当我 运行 时,我始终得到以下输出:

Segmentation fault (core dumped)

分段错误在 gsl_spmatrix_add(total_matrix, total_copy, thread_matrices[i]); 线上。

我正在使用 cmake 编译此代码:

cmake_minimum_required(VERSION 3.22.1)

project(diskmodel)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED YES)

add_subdirectory("src")
project(galaxy)

find_package(GSL REQUIRED)

add_executable(${PROJECT_NAME} main.cpp)

set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "${PROJECT_NAME}" SUFFIX ".exe")

target_link_libraries(${PROJECT_NAME} GSL::gsl GSL::gslcblas )

是什么导致了这个段错误?

编辑:

编译后: g++ 'gsl-config --libs' main.cpp -fsanitize=undefined -g 我得到了和以前一样的输出。使用 address 编译时,我得到:

=================================================================
==31330==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 400 byte(s) in 5 object(s) allocated from:
    #0 0x7efd44b64a06 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:153
    #1 0x7efd449d393e in gsl_spmatrix_alloc_nzmax (/lib/x86_64-linux-gnu/libgsl.so.23+0x1f893e)

Indirect leak of 240 byte(s) in 5 object(s) allocated from:
    #0 0x7efd44b64808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7efd449d3b6c in gsl_spmatrix_alloc_nzmax (/lib/x86_64-linux-gnu/libgsl.so.23+0x1f8b6c)

Indirect leak of 200 byte(s) in 5 object(s) allocated from:
    #0 0x7efd44b64808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7efd449d3b88 in gsl_spmatrix_alloc_nzmax (/lib/x86_64-linux-gnu/libgsl.so.23+0x1f8b88)

Indirect leak of 40 byte(s) in 5 object(s) allocated from:
    #0 0x7efd44b64808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7efd449d39ac in gsl_spmatrix_alloc_nzmax (/lib/x86_64-linux-gnu/libgsl.so.23+0x1f89ac)

Indirect leak of 40 byte(s) in 5 object(s) allocated from:
    #0 0x7efd44b64808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7efd449d397d in gsl_spmatrix_alloc_nzmax (/lib/x86_64-linux-gnu/libgsl.so.23+0x1f897d)

当使用我的 cmake 文件和 运行ning gdb galaxy.exe 编译时,我得到以下回溯:

#0  0x00007ffff7f2c185 in gsl_spblas_scatter () from /lib/x86_64-linux-gnu/libgsl.so.23
#1  0x00007ffff7f2b364 in gsl_spmatrix_add () from /lib/x86_64-linux-gnu/libgsl.so.23
#2  0x00005555555553d2 in test_gsl () at .../src/main.cpp:35
#3  0x0000555555555420 in main (argc=1, argv=0x7fffffffdaf8) at .../src/main.cpp:44

并且在使用 -p 时没有历史记录。

当使用 ulimit -c unlimited 然后 运行ning 时,不会生成核心文件。我试着调查了一下,但我似乎无法在任何地方找到它生成,我也不知道为什么。

看起来像是 GSL 中的错误。请报告:-)

gsl_spmatrix *total_matrix = gsl_spmatrix_alloc_nzmax(size, size, nzmax, GSL_SPMATRIX_CRS);

是 GSL 稀疏矩阵的有效分配器。然而,它的初始化是“智能”的,因为它的一些内存缓冲区是 malloced,但没有初始化。这是指成员pinit_source.c 的第 130 行(来自 GSL 源,子模块(目录)spmatrix):

m->p = malloc((n1 + 1) * sizeof(int));

您的代码接下来要做的是

gsl_spmatrix_memcpy(total_copy, total_matrix); // this is required to avoid another segfault

嗯,评论有点耐人寻味,但让我们看一下代码(copy_source.c 的第 93-96 行):

          for (n = 0; n < src->size1 + 1; ++n)
            {
              dest->p[n] = src->p[n];
            }

这里,size1好像是矩阵的行数,声明为5。所以,代码用垃圾替换(通过复制)垃圾。这告诉我们,如果声明为具有 5 行的矩阵具有少于 5 个非零行,则 GSL 似乎无法正常工作。我相信这是您问题的解决方案。您声明了一些矩阵,例如total_matrixtotal_copy 有 5 行,但它们实际上有 none。然而,到目前为止代码没有错误,因为将垃圾复制到垃圾上是没有错误的。

代码中的下一步:

gsl_spmatrix_add(total_matrix, total_copy, thread_matrices[i]);

调用与成员相关的代码p:

      for (j = 0; j < outer_size; ++j)
        {
          Cp[j] = nz;

这将打开一个循环,在您的例子中将执行 5 次。这里 CpC->p 的 shorthand。到目前为止,p 成员中唯一被初始化的元素是 C = A + BC 的 j-th 之一。接下来,在这个循环中我们可以看到:

          /* CSC: x += A(:,j); CSR: x += A(j,:) */
          nz = FUNCTION (spmatrix, scatter) (a, j, w, x, (int) (j + 1), c, nz);

注意 j 作为第二个参数传递,并且未完全初始化 a 作为第一个参数。这会通过宏调用第 538 行中定义的 spmatrix_scatter

static size_t
FUNCTION (spmatrix, scatter) (const TYPE (gsl_spmatrix) * A, const size_t j, int * w,
                              ATOMIC * x, const int mark, TYPE (gsl_spmatrix) * C, size_t nz)
{
  int p;
  int * Ai = A->i;
  int * Ap = A->p;
  ATOMIC * Ad = A->data;
  int * Ci = C->i;

  for (p = Ap[j]; p < Ap[j + 1]; ++p)
    {

现在,可以看出,GSL 访问了 Ap[j]Ap[j + 1] 的未初始化值。这会在几条指令后立即导致段错误。

现在,如何避免这种情况?

让我们看看创建 CSR 矩阵的“犹太洁食”方法(第 152-156 行,compress_source.c):

      Cp = dest->p;

      /* initialize row pointers to 0 */
      for (n = 0; n < dest->size1 + 1; ++n)
        Cp[n] = 0;

万岁!这是 p 成员的正确初始化。顺带一提,后面几行说明了CRS表示中的p成员是用来存储每一行​​元素的个数的。这似乎是 gsl_spmatrix_alloc_nzmax 中缺少的代码。

结论:不要依赖 gsl_spmatrix_alloc_nzmax 返回的矩阵。它们应该可以用作“目标矩阵”,例如作为 C = A + B 中的 C,但不是 zero-filled 来源。

希望对您有所帮助。

PS。 您可以删除完全不必要的 gsl_spmatrix_memcpy(total_copy, total_matrix);

调用