简体   繁体   English

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

[英]Is there a way to compare two different runs of a C/C++ program?

So I'm working on debugging this program that I inherited from this PhD student who's about to graduate, or whatever happens after a student finishes their thesis. 所以我正在调试这个程序,这个程序是我从即将毕业的博士生中继承的,或者是在学生完成论文后发生的任何事情。 Anyway, it's now my responsibility to debug it. 无论如何,现在我有责任进行调试。 The program basically takes in a couple of text files and processes them. 该程序基本上接受几个文本文件并处理它们。 The problem I've been experiencing (a segfault) occurs because the program tries to access an array that has not been initisialized yet. 我遇到的问题(段错误)是因为程序试图访问尚未初始化的数组。 I was wondering if there's any debugging tool that lets your run through the program, and compare the two different paths the program goes down. 我想知道是否有任何调试工具可以让你运行程序,并比较程序关闭的两个不同路径。 I suppose I could go through the program manually, but I would rather not do that as it is rather large, and I still haven't mastered it. 我想我可以手动完成程序,但我宁愿不这样做,因为它相当大,我仍然没有掌握它。 I've been using GDB and Valgrind (as well as using g++ -wall to show warnings), which is how I've gotten this far. 我一直在使用GDB和Valgrind(以及使用g ++ -wall来显示警告),这就是我如何做到这一点。 But is there any software that let's you do what I've described above, or even just steps you through your program. 但是,是否有任何软件让你按照我上面所描述的那样做,甚至只是让你完成你的程序。

I think you are looking in the right direction with your choice of tools like GDB and valgrind. 我认为您正在寻找正确的方向,您可以选择GDB和valgrind等工具。

With GDB, you can script the execution of the program under both conditions and look at the call stack when the segfault occurs. 使用GDB,您可以在两种条件下编写程序的执行脚本,并在发生段错误时查看调用堆栈。 You can then put a breakpoint at that location and run again with the parameters that do not crash the program and investigate the difference in both. 然后,您可以在该位置放置一个断点,并使用不会使程序崩溃的参数再次运行,并调查两者的差异。

Using valgrind, which is actually a suite of tools ( http://valgrind.org/info/tools.html ), you can have some success using callgrind and kcachegrind. 使用valgrind,它实际上是一套工具( http://valgrind.org/info/tools.html ),使用callgrind和kcachegrind可以取得一些成功。 Callgrind gives you call graphs and kcachegrind ( http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindIndex ) allows you to visualize them. Callgrind为您提供调用图,kcachegrind( http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindIndex )允许您对它们进行可视化。 I used them both for performance profiling of large C code bases. 我将它们用于大型C代码库的性能分析。

Another tool that can help you is Fenris ( http://lcamtuf.coredump.cx/fenris/whatis.shtml ), which also can print out a call graph of your code. 另一个可以帮助你的工具是Fenris( http://lcamt​​uf.coredump.cx/fenris/whatis.shtml ),它也可以打印出代码的调用图。 When reading your requirements, I think Fenris comes closest as it also allows you to 'visualise' the code path taken. 在阅读您的要求时,我认为Fenris最接近,因为它还允许您“可视化”所采用的代码路径。

These suggestions are specific to GCC. 这些建议特定于GCC。 You can use the gcov coverage tool to get a detailed account of which parts of a program have been executed and how often. 您可以使用gcov coverage工具详细说明程序的哪些部分已执行以及执行频率。 You have to pass some special options to GCC to generate the proper instrumentation and output for gcov to process. 您必须将一些特殊选项传递给GCC,以便为gcov生成适当的检测和输出。

--coverage This option is used to compile and link code instrumented for coverage analysis. --coverage此选项用于编译和链接用于覆盖率分析的代码。 The option is a synonym for -fprofile-arcs -ftest-coverage (when compiling) and -lgcov (when linking). 该选项是-fprofile-arcs -ftest-coverage (编译时)和-lgcov (链接时)的-lgcov See the documentation for those options for more details. 有关更多详细信息,请参阅这些选项的文档。

Then, when you execute your program, some profiling and coverage data is generated. 然后,当您执行程序时,会生成一些分析和覆盖数据。 You can then invoke gcov to analyze that output. 然后,您可以调用gcov来分析该输出。 Below is a example of output taken from the link above: 以下是从上面的链接获取的输出示例:

         -:    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:}

If you want to implement your own instrumentation to log the call history of the program, you can use the -finstrument-functions and its related options on GCC. 如果要实现自己的检测以记录程序的调用历史记录,可以在GCC上使用-finstrument-functions及其相关选项。

-finstrument-functions
Generate instrumentation calls for entry and exit to functions. 生成用于进入和退出函数的检测调用。 Just after function entry and just before function exit, the following profiling functions are called with the address of the current function and its call site. 在函数输入之后和函数退出之前,使用当前函数的地址及其调用站点调用以下分析函数。 (On some platforms, __builtin_return_address does not work beyond the current function, so the call site information may not be available to the profiling functions otherwise.) (在某些平台上, __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);

The first argument is the address of the start of the current function, which may be looked up exactly in the symbol table. 第一个参数是当前函数的开始地址,可以在符号表中准确查找。

In C++, your implementation of those hooks should be declared as extern "C" . 在C ++中,您对这些钩子的实现应该声明为extern "C" You can implement the hooks to log each time a function is called. 每次调用函数时都可以实现钩子记录。 You don't get the function names, but you can post process the pointers afterward with objdump or addr2line . 您没有获取函数名称,但您可以使用objdumpaddr2line在后面处理指针。

GDB can allow you to 'step' through your program line by line. GDB可以让您逐行“逐步”完成您的程序。 Some tips: 一些技巧:

  1. Just break at main (type b main ), and press n + Enter key to execute current line and move on to the next one. 只需在main处键入(键入b main ),然后按n + Enter键执行当前行并继续执行下一行。
  2. Press s + Enter key if you want to step into the function (ie go into the function that is called and go on from there). 如果要进入该功能,请按s + Enter键(即进入被调用的功能并从那里继续)。
  3. Type p + variable name to print out the value (really good to tell if that variable is initialized or not, hint hint...) 输入p +变量名来打印出来的值(非常好判断该变量是否已初始化,提示提示......)
  4. If you are running GDB from command line and want a GUI wrapper for it, use Emacs. 如果从命令行运行GDB并想要一个GUI包装器,请使用Emacs。 Just type emacs program.c , and type Alt + x , then type gdb . 只需键入emacs program.c ,然后键入Alt + x ,然后键入gdb Type in the name of your executable, and press Enter. 输入可执行文件的名称,然后按Enter键。 Now you can see more of your code, and still use the gdb commands to debug. 现在您可以看到更多代码,并仍然使用gdb命令进行调试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM