有没有办法比较两个不同的C / C ++程序运行?

时间:2013-06-21 16:35:25

标签: c++ c debugging gdb valgrind

所以我正在调试这个程序,这个程序是我从即将毕业的博士生中继承的,或者是在学生完成论文后发生的任何事情。无论如何,现在我有责任进行调试。该程序基本上接受几个文本文件并处理它们。我遇到的问题(段错误)是因为程序试图访问尚未初始化的数组。我想知道是否有任何调试工具可以让你运行程序,并比较程序关闭的两个不同路径。我想我可以手动完成程序,但我宁愿不这样做,因为它相当大,我仍然没有掌握它。我一直在使用GDB和Valgrind(以及使用g ++ -wall来显示警告),这就是我如何做到这一点。但是,是否有任何软件让你按照我上面所描述的那样做,甚至只是让你完成你的程序。

3 个答案:

答案 0 :(得分:4)

我认为您正在寻找正确的方向,选择GDB和valgrind等工具。

使用GDB,您可以在两种条件下编写程序的执行脚本,并在发生segfault时查看调用堆栈。然后,您可以在该位置放置一个断点,并使用不会使程序崩溃的参数再次运行,并调查两者的差异。

使用valgrind,它实际上是一套工具(http://valgrind.org/info/tools.html),使用callgrind和kcachegrind可以取得一些成功。 Callgrind为您提供调用图表,kcachegrind(http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindIndex)允许您对其进行可视化。我将它们用于大型C代码库的性能分析。

可以帮助您的另一个工具是Fenris(http://lcamtuf.coredump.cx/fenris/whatis.shtml),它还可以打印出代码的调用图。在阅读您的要求时,我认为Fenris最接近,因为它还允许您“可视化”所采用的代码路径。

答案 1 :(得分:4)

这些建议特定于GCC。您可以使用gcov覆盖率工具详细说明程序的哪些部分已执行以及执行频率。您必须将一些特殊选项传递给GCC,以生成gcov要处理的正确检测和输出。

  

--coverage   此选项用于编译和链接为覆盖率分析检测的代码。该选项是-fprofile-arcs -ftest-coverage(编译时)和-lgcov(链接时)的同义词。有关更多详细信息,请参阅这些选项的文档。

然后,当您执行程序时,会生成一些分析和覆盖数据。然后,您可以调用gcov来分析该输出。以下是从上面的链接中获取的输出示例:

         -:    0:Source:tmp.c
         -:    0:Graph:tmp.gcno
         -:    0:Data:tmp.gcda
         -:    0:Runs:1
         -:    0:Programs:1
         -:    1:#include <stdio.h>
         -:    2:
         -:    3:int main (void)
         1:    4:{
         1:    5:  int i, total;
         -:    6:
         1:    7:  total = 0;
         -:    8:
        11:    9:  for (i = 0; i < 10; i++)
        10:   10:    total += i;
         -:   11:
         1:   12:  if (total != 45)
     #####:   13:    printf ("Failure\n");
         -:   14:  else
         1:   15:    printf ("Success\n");
         1:   16:  return 0;
         -:   17:}

如果您想要实现自己的工具来记录程序的通话记录,可以在GCC上使用-finstrument-functions及其相关选项。

  

-finstrument-functions
  生成用于进入和退出函数的检测调用。在函数输入之后和函数退出之前,使用当前函数的地址及其调用站点调用以下分析函数。 (在某些平台上,__builtin_return_address在当前函数之外不起作用,因此调用站点信息可能无法用于分析函数。)

      void __cyg_profile_func_enter (void *this_fn,
                                     void *call_site);
      void __cyg_profile_func_exit  (void *this_fn,
                                     void *call_site);
  

第一个参数是当前函数的起始地址,可以在符号表中查找。

在C ++中,您对这些挂钩的实现应声明为extern "C"。每次调用函数时都可以实现钩子记录。您没有获得函数名称,但您可以使用objdumpaddr2line对该指针进行后期处理。

答案 2 :(得分:3)

GDB可以让您逐行“逐步”完成您的程序。一些提示:

  1. 只需在main(类型b main)处中断,然后按n + Enter键执行当前行并继续执行下一行。
  2. 如果要进入该功能,请按s + Enter键(即进入被调用的功能并从那里继续)。
  3. 输入p +变量名称以打印出值(真的很好判断该变量是否已初始化,提示提示......)
  4. 如果您从命令行运行GDB并想要一个GUI包装器,请使用Emacs。只需输入emacs program.c,然后输入Alt + x,然后输入gdb即可。输入可执行文件的名称,然后按Enter键。现在您可以看到更多代码,并仍然使用gdb命令进行调试。