Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. But is there any software that let's you do what I've described above, or even just steps you through your program.

like image 789
higgs241 Avatar asked Jun 21 '13 16:06

higgs241


3 Answers

These suggestions are specific to 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. You have to pass some special options to GCC to generate the proper instrumentation and output for gcov to process.

--coverage This option is used to compile and link code instrumented for coverage analysis. The option is a synonym for -fprofile-arcs -ftest-coverage (when compiling) and -lgcov (when linking). 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. 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.

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

      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". 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.

like image 54
jxh Avatar answered Nov 15 '22 16:11

jxh


I think you are looking in the right direction with your choice of tools like GDB and valgrind.

With GDB, you can script the execution of the program under both conditions and look at the call stack when the segfault occurs. 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. Callgrind gives you call graphs and kcachegrind (http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindIndex) allows you to visualize them. I used them both for performance profiling of large C code bases.

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. When reading your requirements, I think Fenris comes closest as it also allows you to 'visualise' the code path taken.

like image 33
Johan Avatar answered Nov 15 '22 15:11

Johan


GDB can allow you to 'step' through your program line by line. 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.
  2. Press s + Enter key if you want to step into the function (i.e. go into the function that is called and go on from there).
  3. Type p + variable name to print out the value (really good to tell if that variable is initialized or not, hint hint...)
  4. If you are running GDB from command line and want a GUI wrapper for it, use Emacs. Just type emacs program.c, and type Alt + x, then type gdb. Type in the name of your executable, and press Enter. Now you can see more of your code, and still use the gdb commands to debug.
like image 29
jh314 Avatar answered Nov 15 '22 16:11

jh314