Lab 10: valgrind and gdb

Objectives

  1. Building on command line use that we started previously, Use valgrind to reveal memory issues.

  2. Learn how to use the gdb debugger to find the source of crashes and logic errors in code.

Both valgrind and gdb can be run from the command line in Replit (the black window over on the right).
[Thanks to Samuel Effendy for finding the gcc -O0 compile option within Replit to make running valgrind there possible!
Thanks to Bilal Naseer for identifying
this video showing how Mac users can use the built-in leaks program to find memory leaks.]

Lab Reading / Preparation

We will use the following two Replit projects for this lab. Make sure you have access to both of them:

Because these were not originally set up as group projects, we can't now convert them into group projects. To edit collaboratively, copy/paste your code into the Lab 10: Group Work project.

For the pre-lab work you should do the following:

  1. In the Valgrind project in Replit, edit main.c and fill-in the code according to the instructions within each function there, where each of them is designed to cause a different type of error when you run it later with valgrind. Don't forget to uncomment the functions calls within main(). You will submit your completed version of the Valgrind project code as your outline this week, along with your answer to the next item shown below where you use gdb to find a single error.

  2. Next switch to the gdb Linked List code project within Replit. Play with gdb from the Replit command line so you get a sense of how to use it, and using gdb commands (see below) find one issue with the Lab 10 Linked List code. Include the details on what this found issue/error was in your "outline" submission for this week. During lab you will debug the rest of this Lab 10 Linked List code, again using gdb commands.

  3. For your bibliography research, find helpful tutorials on using the gdb debugger.

Submit your code into the Reading Outline Submission Form that we've been using each week, by copy / pasting it into the form question that asks for 250 words minimum, entitled "What are the main points to this topic?"

Lab Activity

  1. With your partner, use your outline-submitted versions of the Replit Lab 10: Valgrind test code project to document how the distinct error from each function error is identified (or not) in valgrind. Run valgrind using the following steps, similar to what was seen previously in the class notes, except that to run valgrind within Replit we must also compile the program with optimization (-O capital letter 'o') level set to 0 (the number 0).

In the examples below we assume the program we are working on is called test.c and we compile using the default resulting executable file called a.out

        1. From the command line in Replit (the black window over on the right) compile by typing in the following:
          gcc -g -O0 test.c
          Just to be clear, after the -g and the space you then have "dash capital-oh zero". This results in creating a.out

        2. From the command line in Replit run valgrind on the compiled / executable version of your program (probably a.out), including the options to show all memory leaks and option -s to show the error list:
          valgrind --leak-check=full -s ./a.out
          The dot '.' at the front of ./a.out means "current directory".

  1. Next, with your partner, use gdb to find and identify at least two of the ~5 program errors in Replit Lab 10: gdb Linked List code using the gdb commands shown below, again using the Replit command line. The point of this activity is for you to become familiar with gdb and document how it is used. The goal is not for you to find all the errors, but rather your documentation should demonstrate your process in gdb of trying to find errors. Did you set breakpoints? Which ones? Did you examine variables? Which ones? (and so on.) You should find at least 2 errors, but don't need to find all of them.

Basic gdb Commands

(Also listed on the course web site at Resources / gdb Basics ) (adapted 10/20/22 from cs.umd.edu/~srhuang/teaching/cmsc212/gdb-tutorial-handout.pdf )


Startup [ This assumes you are typing the commands below after the prompt (shown in bold) in some UNIX command-line window. ]

  • Make sure you have compiled with the -g option, such as:
    > gcc -g test.c
    Verify that you have created an executable file called a.out
    > ls
    which could list files looking something like:
    a.out test.c

  • Start up gdb:
    > gdb

  • Load the previously created file:
    (gdb) file a.out

  • Run the program:
    (gdb) run
    Note that you could also start gdb and load the file all at once by starting up gdb using:
    > gdb a.out

Breakpoints [The (gdb) prompt is assumed and not shown below]

  • Set a breakpoint in file test.c at line 10. The program will stop when it reaches that line.
    break test.c:10

  • Set a breakpoint at function void display( pHead, pTail)
    break display

  • Go on to the next breakpoint
    continue

  • Single-step to the next line of code
    step
    Pressing enter repeats the previous command, so doing that here repeatedly takes you through successive lines of code, including stepping into functions.

  • To step over a function call, we use
    next

  • Create a conditional breakpoint to stop at a particular line, when a particular criterion is met
    break test.c:10 if total > 0

Display Memory [The (gdb) prompt is assumed and not shown below]

  • print pHead Display the contents of memory using the print command:

  • print pHead->value You can also operate on pointers, such as:

  • pause total Pause a program whenever the value of the selected variable is modified.
    When
    total is changed, the program will pause and display the old and new values. It uses the scope of where you currently are in the program.

More [The (gdb) prompt is assumed and not shown below]

    • list Display lines around the current program location. See more options here.
      Consider having a separate window open with your code, showing line numbers.

    • backtrace Produces a stack trace of function calls leading you to a seg fault

    • where Same as backtrace, while a program is still running (no seg fault)

    • finish Run to the end of the current function

    • delete Delete the specified breakpoint

    • info breakpoints Provide info about all current breakpoints

    • quit Quit out of gdb. You may need to agree to kill the inferior process.


Sample Code (In case you don't have access to Replit)

First here is the valgrind sample program:

Next we have the code to use with gdb to try and find errors, documenting the process: