【问题标题】:How to debug confusingly big code?如何调试令人困惑的大代码?
【发布时间】:2015-02-05 14:13:22
【问题描述】:

我不是 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 {更多链接} -I。 -fPIC -c $SOURCE -o Make/linux64Gcc46DPOpt/mySolver.o

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

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 对我来说也不能正常工作。我知道这与一些错误的(取消)分配有关,但我不知道如何真正找到问题。

/编辑

我还没有找到我的问题...

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

非常感谢任何帮助!

【问题讨论】:

  • 如果你正在使用 new/new[] 你是在调用 delete/delete[] 吗?
  • 跟踪表明损坏发生在 Foam::word 类型的全局对象的 d'tor 中。我会寻找那些被声明、定义或(错误)使用的地方。
  • 有人正在占用std::string 的内部缓冲区(Foam::stringFoam::word 也使用它,并通过char* 在其上调用deletedelete[] .std::string管理自己的buffer,所以delete[]是不正确的,当std::string被销毁时,它也尝试清理它的buffer,但是发现是别人先做的,问题早在清理之前就出现了std::string,它会通知您出现问题。这很好,因为如果运气不好,可能会发生更糟糕的事情它不会注意到它们发生
  • 使用g++ -Wall -Wextra -g 编译,然后使用valgrindgdb 调试器
  • @ForceBru:你可以return一个带括号的表达式(即使括号没用)。

标签: c++ debugging gdb sigabrt


【解决方案1】:

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

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

如果问题消失了,那么它很可能是由您刚刚删除的某些东西引起的。或者如果问题仍然存在,那么它肯定在仍然存在的代码中。

递归地重复过程,直到找到问题所在。

这并不总是有效,因为未定义的行为可能会在导致它的行之后出现。

但是,您可以努力生成minimal program that still has the problem。最终,您必须要么生成一个无法进一步简化的实际最小示例,要么找出真正的原因。

【讨论】:

    【解决方案2】:

    如果您在使用 gdbvalgrind 后没有得到任何具体的东西,我认为您可以尝试使用 objdump 反汇编您的 so 库,正如您在回溯中看到的那样已经给了你错误的地址,我很久以前在我的项目中调试问题时尝试过这种方法。反汇编后,您将错误地址与库中的语句地址匹配,它可能会让您了解错误位置。 反汇编命令objdump -dR &lt;library.so&gt;

    你可以找到更多关于objdumphere的信息

    【讨论】:

    • 我喜欢这个想法,并查看了 objdump 的输出。 #8 0x00002aaab2416866 in __do_global_dtors_aux () from /opt/software/openfoam/myLibs/lib/linux64Gcc46DPOpt/libTMP.so 中的地址不会在 objdump 中输出。我还搜索了 do_global_dtors 这给了我 something: 00000000001463c0 &lt;__do_global_dtors_aux&gt;: 1463c0: 55 push %rbp 等等...如何继续?
    【解决方案3】:

    valgrind

    好吧,我冒着被一句话回答的风险,但请耐心等待。试试 valgrind。构建仍然存在问题的最调试版本并简单地发出:

    valgrind 路径/到/程序

    很有可能,第一个报告的问题将是您的问题来源。您甚至可以让 valgrind 启动 gdb 服务器并让您附加以调试导致第一个内存问题的代码。见:

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

    【讨论】:

      【解决方案4】:

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

      你可以试试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 链接并使用它的调试功能。选项“opt.junk”、“opt.quarantine”、“opt.valgrind”和“opt.redzone”可能很有用。一般来说,它使 malloc 分配一些额外的内存,用于监视缓冲区结束后的写入和读取、释放内存的读取等。见man page。可以通过mallctl 函数启用此选项。

      另一种查找错误的方法是在构建代码时启用 gcc 或 clang 的清理程序。您可以使用 -fsanitize="sanitizer" 打开它们,其中“sanitizer”可以是以下之一: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
      }
      

      在 sanitizer 开启的情况下编译并运行:

      $ 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)
      

      【讨论】:

      • 使用GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-23.el5_5.2)时没有target record-full。我尝试了target record,然后:set record insn-number-max unlimited 出现以下错误:No symbol "unlimited" in current context.
      • @EverythingRightPlace 你可以只做record full 然后target record 而不是target record-full。尝试将insn-number-max设置为0,必须和unlimited一样。
      • @EverythingRightPlace 如果可以的话尝试更新gdb,我的是GNU gdb (GDB) Fedora 7.8.2-38.fc21
      【解决方案5】:

      我部分同意马特的观点:divide et impera 是一种方式。但我部分同意,因为我部分不同意:修改您尝试调试的代码可能会导致您在错误的轨道上寻找,如果您尝试使用您不使用的语言调试不是您的庞大而复杂的代码,则更是如此大师。

      相反,请遵循与从上到下策略相结合的除法和禁锢方法:首先在更高级别的代码中添加一些断点,比如说在主代码中,然后启动程序并查看哪些断点被击中,哪些断点被击中不是在崩溃之前。现在您对错误在哪里有了一个大致的了解;删除所有断点并在您刚刚找到的区域更深地添加新断点,然后重复直到遇到导致崩溃的例程。

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

      【讨论】:

      • 但我的问题在于我无法断点进入的 .so。或者您将如何处理我的具体问题(请参阅 gdb 输出)?
      • 如果可以的话,我会否决我的答案;抱歉,我确实误解了,我认为这是来自您自己的项目的 .so,而不是来自外部资源。现在如何进行……这有点超出我的能力,我担心。可能因为 OpenFoam 是开源的,所以我会下载源代码并进行调试。
      • 我有代码,但它非常复杂,我不习惯 C++。所以 调试它 正如我所注意到的,说起来容易做起来难 :(
      • @EverythingRightPlace 问题在 .so 中被 检测到 为双重释放或损坏,但原因是第一次释放,或“流浪写入内存”发生得更早。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      相关资源
      最近更新 更多