8

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.

higgs241
  • 589
  • 6
  • 20
  • 1
    I'm not aware of anything like that. Adding code to "trace" where you are in the code (e.g. at the beginning/end of every function, or at least some of the more important functions) is one way to help debug this sort of problem. – Mats Petersson Jun 21 '13 at 16:38
  • 4
    I'm interested to know why you're asking - if Valgrind has led you to the uninitialised data, why do you need to go any further? What more do you need to know to fix the problem now? – RichieHindle Jun 21 '13 at 16:40
  • 2
    Gdb will step through the program. `break main`, `next`, then just keep hitting enter. – luser droog Jun 21 '13 at 16:41
  • Richie, it just seemed as though something like that might exist, and I felt as though it might be helpful. The reason that this array isn't initisialized is a whole lot more complicated than I first thought. – higgs241 Jun 21 '13 at 16:43
  • @luserdroog: Can be quite tedious to step through a program that reads every word that Shakespeare ever wrote, only to crash on the last one... ;) – Mats Petersson Jun 21 '13 at 16:43
  • True, but then you know exactly where it happens. Knowing where often leads (me) to a specific variable that has the wrong value. Then I can start earlier in the execution and trace again with `display variable`, that often finds the bug (for me). – luser droog Jun 21 '13 at 16:48
  • @higgs241 there are static analysis tools (e.g. coverity, klockwork) that are quite useful in finding problems that valgrind might miss. They attempt to analyse all branches of a program. – juanchopanza Jun 21 '13 at 16:48
  • For auto-stepping through GDB, you can consider using [this solution](http://stackoverflow.com/a/5813439/315052). – jxh Jun 21 '13 at 17:18
  • 2
    Coverage analysis tools can tell you what lines of your code your execution ran on. I suppose the output from one coverage analysis and another might tell you what different branches where taken. – Yakk - Adam Nevraumont Jun 21 '13 at 18:22

3 Answers3

5

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.

jxh
  • 69,070
  • 8
  • 110
  • 193
4

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.

Johan
  • 3,072
  • 3
  • 27
  • 27
3

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.
jh314
  • 27,144
  • 16
  • 62
  • 82
  • I'm going to try this out, currently writing a python script that will allow me to model each program in gdb, and compare output, then I'm going to look and see where the output diverges – higgs241 Jun 21 '13 at 18:49
  • Cool, feel free to follow up! – jh314 Jun 21 '13 at 19:11
  • @higgs241 you might check out this gdb/python script which has been useful for me in these situations https://gitorious.org/misc-gdb-stuff/misc-gdb-stuff/trees/master/misc_gdb/lockstep – matt Jun 22 '13 at 17:00