难以跟踪大型程序中的SIGSEGV分段故障

Difficult to track SIGSEGV Segmentation fault in large program

本文关键字:SIGSEGV 分段 故障 程序 跟踪 大型      更新时间:2023-10-16

我很抱歉发布了一个被问了很多次的问题(我刚刚读了 10 页),但我找不到解决方案。

我正在分别使用OpenGL和Portaudio开发多线程图形/音频程序。音频线程使用我为音频处理对象制作的库。SIGSEGV 可能发生 20% 的时间(调试时要少得多),并且在使用新的流信息(采样率、矢量大小等)重置音频对象的负载时发生。代码::块 调试器在每次发生故障时将错误声明为来自不同的位置。

这是音频处理循环:

while(true){
    stream->tick();
    menuAudio.tick();
    {
        boost::mutex::scoped_lock lock(*mutex);
        if(channel->AuSwitch.resetAudio){
            uStreamInfo newStream(channel->AuSwitch.newSrate, 
                 channel->AuSwitch.newVSize, channel->AuSwitch.newChans);
            menuAudio.resetStream(&newStream);
            (*stream) = newStream;
            menuAudio.resetStream(stream);
            channel->AuSwitch.resetAudio = false;
        }
    }
}

检查来自图形线程的信息,告诉它重置音频并运行补丁对象的 resetStream 函数,该对象基本上是音频对象的向量并运行每个对象:

void uPatch::resetStream(uStreamInfo* newStream)
{
    for(unsigned i = 0; i < numObjects; ++i){
/*This is where it reports this error: Program received signal SIGSEGV,  
Segmentation fault. Variables: i = 38, numObjects = 43 */
        objects[i]->resetStream(newStream); 
    }
}  

有时它会将 SIGSEGV 声明为来自不同的位置,但由于在使用调试器运行时很少出错,这是我唯一能发生的错误。

由于对象太多,我不会发布它们的所有重置代码,但作为示例:

void uSamplerBuffer::resetStream(uStreamInfo* newStream)
{
    audio.set(newStream, false);
    control.set(newStream, true);
    stream = newStream;
    incr = (double)buffer->sampleRate / (double)stream->sampleRate;
    index = 0;
}

其中 audio.set 代码是:

void uVector::set(uStreamInfo* newStream, bool controlVector)
{
    if(vector != NULL){
        for(unsigned i = 0; i < stream->channels; ++i)
            delete[] vector[i];
        delete vector;
    }
    if(controlVector)
        channels = 1;
    else
        channels = newStream->channels;
    vector = new float*[channels];
    for(unsigned i = 0; i < channels; ++i)
        vector[i] = new float[newStream->vectorSize];
    stream = newStream;
    this->flush();
}

我最好的猜测是这是一个堆栈溢出问题,因为它只真正发生在大量对象上,并且它们各自运行良好。也就是说,音频流本身运行良好,并以类似的方式运行。此外,objects[i]->resetStream(newStream);循环应该在每个成员函数之后弹出堆栈,所以我不明白为什么它会 SIGSEGV。

有什么意见/建议吗?

编辑:

这是一个错误删除的内存问题。应用程序验证程序在错误点使其出现故障,而不是标识为源自其他位置的偶尔故障。问题出在uVector流设置功能上,因为该类的目的是使用stream->channels的多维数组的音频向量,并可以选择使用一维数组作为控制信号。删除以重新分配内存时,我不小心将所有uVectors(无论类型如何)设置为使用流>通道删除。

if(vector != NULL){
    for(unsigned i = 0; i < stream->channels; ++i)
        delete[] vector[i];
    delete vector;
}

它应该在哪里:

if(vector != NULL){
    for(unsigned i = 0; i < this->channels; ++i)
        delete[] vector[i];
    delete vector;
}
因此,它

删除了它不应该访问的内存,这损坏了堆。我很惊讶段错误没有更频繁地发生,因为这似乎是一个严重的问题。

我可以节省内存,您可以尝试像 Electric Fence(或 DUMA,它的子级)这样的工具,看看它是否是您执行的越界写入。通常,这些类型的段错误(非永久性的,仅在偶尔发生)是先前缓冲区溢出某处的遗物。

您也可以尝试 Valgrind,它将具有与上述 2 种工具相同的效果,但代价是执行速度较慢。

另外,当发生这种情况时,请尝试检查您正在访问的错误地址的值是多少:它看起来有效吗?有时,值可以提供有关您遇到的错误的信息(通常:尝试访问0x12内存,其中0X12是循环:)中的计数器)。

对于堆栈溢出...我建议尝试增加有罪线程的堆栈大小,看看是否重现了该错误。如果没有经过大量的尝试,你已经发现了问题。

至于窗户:

  • 如何调试堆损坏错误?
  • Win32 下的堆损坏;如何定位?
  • https://stackoverflow.com/search?q=windows+memory+corruption&submit=search

我想你只是把它变成了堆栈溢出问题。 :)

严肃地说,像这样的错误通常是访问内存位置的对象的结果,而这些对象不再存在。在您的第一个代码块中,我看到您在堆栈上创建newStream,其范围仅限于它所属的 if 语句。然后将其复制到取消引用的指针 ( *stream )。是否为 uStreamInfo 类定义了安全正确的赋值?如果没有显式定义,编译器将悄悄地为对象赋值提供成员复制,这对于简单的基元(如 intdouble)是可以的,但不一定适用于动态分配的对象。 *stream可能会留下一个指向newStream分配的内存的指针,但当newStream超出范围时,它已经被释放了。现在该 RAM 上的数据仍然存在,并且暂时看起来是正确的,但是被释放的内存,它随时可能损坏,就像崩溃之前一样。:)

我建议密切关注何时分配和解除分配对象,以及哪些对象拥有哪些其他对象。您还可以采取除以征服的方法,注释掉大部分代码并逐渐启用更多代码,直到您看到崩溃再次开始发生。该错误可能存在于最近重新启用的代码中。