如何调试令人困惑的大代码?

时间:2015-02-05 14:13:22

标签: c++ debugging gdb sigabrt

我不是c ++程序员,但尝试调试一些复杂的代码。不是最好的先决条件,我知道......

所以我有一个openfoam求解器,它使用(包括)大量代码,我很难找到错误。我用

编译
  

SOURCE = mySolver.C; g ++ -m64 -Dlinux64 -DWM_DP -Wall -Wextra -Wno-unused-parameter -Wold-style-cast -O3 -DNoRepository -ftemplate-depth-100 -I / opt / software / openfoam / OpenFOAM-2.0.5 / src / dynamicMesh / lnInclude {more linking} -I。 -fPIC -c $ SOURCE -o Make / linux64Gcc46DPOpt / mySolver.o

在使用适当的选项运行求解器之后,它在我的return语句之后(或同时)结束时崩溃:

BEFORE return 0

*** glibc detected *** /opt/software/openfoam/myLibs/applications/bin/linux64Gcc46DPOpt/mySolver: double free or corruption (!prev): 0x000000000d3b7c30 ***
======= Backtrace: =========
/lib64/libc.so.6[0x31c307230f]
/lib64/libc.so.6(cfree+0x4b)[0x31c307276b]
/opt/software/openfoam/ThirdParty-2.0.5/platforms/linux64/gcc-4.5.3/lib64/libstdc++.so.6(_ZNSsD1Ev+0x39)[0x2b34781ffff9]
/opt/software/openfoam/myLibs/applications/bin/linux64Gcc46DPOpt/mySolver(_ZN4Foam6stringD1Ev+0x18)[0x441e2e]
/opt/software/openfoam/myLibs/applications/bin/linux64Gcc46DPOpt/mySolver(_ZN4Foam4wordD2Ev+0x18)[0x442216]
/lib64/libc.so.6(__cxa_finalize+0x8e)[0x31c303368e]
/opt/software/openfoam/myLibs/lib/linux64Gcc46DPOpt/libTMP.so[0x2b347a17f866]
======= Memory map: ========
...

我的求解器看起来像(抱歉,我无法发布所有部分):

#include "stuff1.H"
#include "stuff2.H"

int main(int argc, char *argv[])
{
#include "stuff3.H"
#include "stuffn.H"

    while (runTime.run())
    {

        ...

    }

Info<< "BEFORE return 0\n" << endl;

return(0);
}

使用带有setting set environment MALLOC_CHECK_ 2的gdb运行求解器,得到:

BEFORE return 0

Program received signal SIGABRT, Aborted.
0x00000031c3030265 in raise () from /lib64/libc.so.6
(gdb) bt
#0  0x00000031c3030265 in raise () from /lib64/libc.so.6
#1  0x00000031c3031d10 in abort () from /lib64/libc.so.6
#2  0x00000031c3075ebc in free_check () from /lib64/libc.so.6
#3  0x00000031c30727f1 in free () from /lib64/libc.so.6
#4  0x00002aaab0496ff9 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() ()
   from /opt/software/openfoam/ThirdParty-2.0.5/platforms/linux64/gcc-4.5.3/lib64/libstdc++.so.6
#5  0x0000000000441e2e in Foam::string::~string (this=0x2aaaac0bd3c8, __in_chrg=<value optimized out>) at /opt/software/openfoam/OpenFOAM-2.0.5/src/OpenFOAM/lnInclude/string.H:78
#6  0x0000000000442216 in Foam::word::~word (this=0x2aaaac0bd3c8, __in_chrg=<value optimized out>) at /opt/software/openfoam/OpenFOAM-2.0.5/src/OpenFOAM/lnInclude/word.H:63
#7  0x00000031c303368e in __cxa_finalize () from /lib64/libc.so.6
#8  0x00002aaab2416866 in __do_global_dtors_aux () from /opt/software/openfoam/myLibs/lib/linux64Gcc46DPOpt/libTMP.so
#9  0x0000000000000000 in ?? ()
(gdb) 

我应该如何找到错误的真正来源?

顺便说一下。我看到thisthis类似,但没有解决我的问题。 valgrind对我来说也不正常。我知道它与一些错误的(de-)分配有关,但我不知道如何真正找到问题。

/修改

我无法找到我的问题......

我认为上面发布的回溯(位置#8)显示问题出现在编译为libTMP.so的代码中。在 Make / options 文件中,我添加了选项 -DFULLDEBUG -g -O0 。我认为有可能跟踪这个错误,但我不知道如何。

非常感谢任何帮助!

5 个答案:

答案 0 :(得分:5)

如果您已经处理了所有编译器警告和valgrind错误,但问题仍然存在,那么分而治之

删除一半代码(使用#if指令,从Makefile中删除文件,或删除行,然后使用源代码控制恢复。)

如果问题消失,那很可能是因为你刚刚删除的东西造成的。或者如果问题仍然存在,那么它仍然存在于代码中。

递归重复此过程,直到您磨练问题位置。

这并不总是有效,因为未定义的行为可以在比导致它的行更晚的时间表现出来。

但是,您可以努力制作minimal program that still has the problem。最终,你必须要么产生一个你不能进一步减少的实际最小例子,要么揭示真正的原因。

答案 1 :(得分:3)

如果您在使用gdbvalgrind后发现任何具体内容我认为您可以尝试使用objdump反汇编您的所以图书馆,正如你在backtrace中看到的,它已经给你了错误的地址,我在调试问题的同时在我的项目中尝试了这种方法。反汇编后,您将错误的地址与库中语句的地址相匹配,它可能会让您了解错误位置。 用于反汇编objdump -dR <library.so>

的命令

您可以找到有关objdump here

的更多信息

答案 2 :(得分:2)

<强>的valgrind

好吧,我冒着因单词答案被击落的风险,但请耐心等待。试试valgrind。构建您仍然遇到问题的最大调试版本,然后发出:

valgrind path / to / program

可能是,第一个报道的问题将是您的问题来源。您甚至可以获得valgrind来启动gdb服务器,并允许您附加以调试导致第一个内存问题的代码。参见:

http://tromey.com/blog/?s=valgrind

答案 3 :(得分:2)

尚未列出的其他一些选项包括:

您可以尝试gdb execution flow recording capability

$ gdb target_executable
(gdb) b main
(gdb) run
(gdb) target record-full
(gdb) set record full insn-number-max unlimited

然后,当程序崩溃时,您将能够使用reverse-nextreverse-step命令向后执行流程。请注意,在此模式下,程序运行速度非常慢。

另一种可能的方法是在代码上尝试使用clang static analyzerclang-check工具。有时,分析器可以提供代码中可能存在问题的良好提示。

此外,您可以将代码与jemalloc相关联并使用它的调试功能。选项&#34; opt.junk&#34;,&#34; opt.quarantine&#34;,&#34; opt.valgrind&#34;和&#34; opt.redzone&#34;可以是有用的。通常,它使malloc分配一些额外的内存,用于监视缓冲区结束后的写入和读取,读取释放的内存等等。见man page。可以使用mallctl功能启用此选项。

查找错误的另一种方法是使用启用了gcc或clang的清理程序来构建代码。您可以使用-fsanitize =&#34; sanitizer&#34;打开它们,其中&#34; sanitizer&#34;可以是以下之一:addressthreadleakundefined。编译器将使用一些额外的代码来检测应用程序,这些代码将执行其他检查并打印报告。例如:

#include <vector>
#include <iostream>

int main() {
  std::vector<int> vect;
  vect.resize(5);
  std::cout << vect[10] << std::endl; // access the element after the end of vector internal buffer
}

使用开启的清洁剂进行编译并运行:

$ clang++ -fsanitize=address test.cpp
$ ./a.out

给出输出:

==29920==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60400000dff8 at pc 0x0000004bad10 bp 0x7fff16d63e10 sp 0x7fff16d63e08
READ of size 4 at 0x60400000dff8 thread T0
#0 0x4bad0f in main (/home/pablo/a.out+0x4bad0f)
#1 0x7f0b6ce43fdf in __libc_start_main (/lib64/libc.so.6+0x1ffdf)
#2 0x4baaac in _start (/home/pablo/a.out+0x4baaac)

0x60400000dff8 is located 0 bytes to the right of 40-byte region [0x60400000dfd0,0x60400000dff8)
allocated by thread T0 here:
#0 0x435b9b in operator new(unsigned long) (/home/pablo/a.out+0x435b9b)
#1 0x4c1f49 in __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (/home/pablo/a.out+0x4c1f49)
#2 0x4c1d05 in __gnu_cxx::__alloc_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (/home/pablo/a.out+0x4c1d05)
#3 0x4bfd51 in std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) (/home/pablo/a.out+0x4bfd51)
#4 0x4bdb2a in std::vector<int, std::allocator<int> >::_M_fill_insert(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, unsigned long, int const&) (/home/pablo/a.out+0x4bdb2a)
#5 0x4bbe49 in std::vector<int, std::allocator<int> >::insert(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, unsigned long, int const&) (/home/pablo/a.out+0x4bbe49)
#6 0x4bb358 in std::vector<int, std::allocator<int> >::resize(unsigned long, int) (/home/pablo/a.out+0x4bb358)
#7 0x4bacaa in main (/home/pablo/a.out+0x4bacaa)
#8 0x7f0b6ce43fdf in __libc_start_main (/lib64/libc.so.6+0x1ffdf)

答案 4 :(得分:1)

我部分同意马特:除此之外是分裂和阻碍。但我部分同意,因为我部分不同意:修改你试图调试的代码可能会导致你在错误的轨道上搜索,如果你试图用一种语言来调试一个巨大而复杂的代码,那就更多了。你不会掌握。

相反,遵循除了et impera方法并结合从上到下的策略:首先在更高级别的代码中添加几个断点,让我们在主要部分说,然后启动程序并查看哪些断点在崩溃之前被击中。现在你大致了解了bug的位置;删除所有断点并在刚刚找到的区域中添加更新的断点,然后重复直到达到导致崩溃的例程。

我知道这可能很乏味,但是它有效,而且,这样做可以让你更好地理解整个系统是如何工作的。我用这种方式修复了由成千上万行代码组成的未知应用程序中的错误,它始终有效;也许它可能需要一整天,但它确实有效。