在Valgrind中出现段错误,但在实际运行中未出现

3

当我在Valgrind中运行代码时,出现了分段错误,但在正常运行时没有。这是怎么可能的?

valgrind指出的罪魁祸首代码片段:

static inline void * Vec_PtrEntry( Vec_Ptr_t * p, int i )
{
    assert( i >= 0 && i < p->nSize );
    return p->pArray[i];
}

And the message by valgrind:

Process terminating with default action of signal 11 (SIGSEGV)
==3290==  Access not within mapped region at address 0x0

如果通常代码可以正常运行,为什么会出现这种情况?我该如何解决?我需要对代码进行一些内存分析。


不看 p 是如何创建的以及 Vec_Ptr_t 的定义,很难说出问题出在哪里。 - R Sahu
1
Valgrind内置了GDB服务器。你尝试过附加GDB并调查程序在那一点的状态吗? - Dark Falcon
4
你很幸运,我曾看到它反过来发生。 - Beta
4
表面上看,你在某个地方访问了一个空指针。很可能是p为空或p->pArray为空。(您可以添加关于此的新断言或扩展现有断言。)软件没有义务在调用未定义行为时崩溃;它似乎可以工作,并且通常会一直工作,直到你的老板向公司最重要的客户展示你的工作为止。至少你早早地找到了问题。 - Jonathan Leffler
除了(非常合适的)“未定义行为可以做任何它想做的事情,包括不崩溃”的理由外,还要注意运行在valgrind下的一个副作用是它会极大地改变程序的性能特性(主要是使其运行更慢)。因此,如果您有一个bug,其症状只在程序运行比正常速度慢时才显现出来,那么valgrind可能会暴露这个bug。例如,如果您正在运行一个有缺陷的客户端,并且它正在与仍以正常速度运行的服务器通信,则可能会发生这种情况。 - Jeremy Friesner
1个回答

3

如评论中所提到的,未定义行为并不一定会导致崩溃。它可以完美地运行。然而,在这种情况下似乎并非如此。

我们可以从消息中看到

Process terminating with default action of signal 11 (SIGSEGV)
==3290==  Access not within mapped region at address 0x0

程序尝试访问地址0x0。通常意味着我们已经取消引用了一个空指针。

查看您的代码:

static inline void * Vec_PtrEntry( Vec_Ptr_t * p, int i )
{
    assert( i >= 0 && i < p->nSize );
    return p->pArray[i];
}

我们可以看到您已经试图通过断言 i>=0i<p->nSize 来防止无效参数的出现,但是没有检查 p 本身是否有效。

在现有的断言之前,您可以使用 assert(p) 来确保它不是 NULL。

至于为什么只有在 valgrind 下运行时才会发生这种情况,一个重要的考虑因素是程序在 valgrind 下运行 MUCH 更慢,所以您可能已经暴露了仅在重载或非常不同的动态行为下才会发生的问题。

您该如何解决这个问题并继续进行内存分析?您需要修复这个错误。

  1. 使用调试器。gdb 与 valgrind 集成得很好。
  2. 使用断言来验证 p 是非空的。

这两者中的任何一项都应该允许您查看回溯,并找出为什么 p 是 NULL。


1
同意可能的原因(我自己也是这样回答的,更容易点赞你的回答)。OP应该查看操作流程,其中生成并使用p。在正常的代码中,这两个操作将非常接近,例如循环将没有时间执行或仅执行一次;我怀疑有一些由外部事件(套接字)驱动或运行线程的代码,并且p被卡在竞争条件中。我认为这实际上是Valgrind的优势,因为它捕获了否则会长时间不被注意的错误(或直到大秀日,以先到者为准)。 - LSerni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接