CS 3210 - Lab2 - Debugging

Or: "Debugging for fun and profit"

Pre-Requisite: Make a lab2 Directory

After logging into the lab server, change into the unix directory by typing cd unix and then make a directory called 'lab2' by typing mkdir lab2. Then change into the lab2 directory by typing cd lab2.

Finding A Run-Time Bug

For this first exersize, we'll fix a program that compiles just fine, but pukes when you try to run it.

Step #1: Get / Run the sample buggy program

Copy 'buggy.c' from /tmp into the lab2 directory by typing cp /tmp/lab2/buggy.c .

Now compile the buggy program with gcc buggy.c -o buggy. Then try running it by typing ./buggy. Watch it fail horribly.

Step #2: Compile with debugging symbols

To include debugging symbols in the executable, pass the -g flag to gcc like so:

	gcc -Wall -g buggy.c -o buggy

The -Wall means "Turn on all warnings". I want you to get used to including that.

You might want to write a Makefile that looks something like this:

debug:
	gcc -Wall -g buggy.c -o buggy

strip: debug
	strip buggy

Now when you want to compile the program with debugging symbols, you can just type 'make'. When you want to build a non-debug version, type 'make strip'. The 'strip' program removes (strips) debugging symbols from an executable file. Note also that the "strip" target depends on the "debug" target (so indicated by putting the target name after the colon) so it will build debug first before running its own commands.

Step #3: Run the program under gdb

GDB is short for the "GNU Debugger". You can get information from the following sources:

You start GDB by typing gdb progname, where progname is the name of some executable program that you want to debug. If you didn't compile the program with the -g flag you'll get an error that says "No debugging symbols found".

You should find yourself at the (gdb) prompt. You can start the program running by typing run. (If you didn't specify a progname when you started gdb, you can type file progname at the (gdb) prompt.) Now when the program crashes, you can look around at what went wrong.

To display the contents of a variable, type print i. If you try to display the value of the array with print ary[i], you will get an address. This is because the C language treats arrays as pointers. If you want to get the value of the array, you can type print *ary[i] or x/d ary[i].

Step #4: Actually Fix The Bug

Having successfully found the cause of the bug, modify the program so that the bug not longer occurs. Left as an exersize for the student to find a good way to do that.

Alternative Interfaces to GDB

There is a text-window interface built into GDB that you can use. This section of the GDB manual talks about that.

You can also use Emacs as a front-end for gdb. You do this by starting Emacs and then typing M-x gdb progname. You can then type C-xas to step, C-xan for 'next', C-xar for continue, etc. This section of the GDB manual talks about using GDB under Emacs.

If we were able to run an X Window session, we could all be using DDD: The Data Display Debugger. Screenshots. DDD is nice.

Fixing A Leaky Program

Now let's look at some memory-allocation problems.

Step #5: Get / Run the sample leaky program

Copy 'leaky.c' into your own lab2 directory by typing cp /tmp/lab2/leaky.c . This is very similar to the code in the book on pgs. 53-54.

Compile it with:

	gcc -g leaky.c -o leaky

You could even modify the Makefile to make it look like this:

debug: buggy leaky

buggy: buggy.c
    gcc -Wall -g buggy.c -o buggy

leaky: leaky.c
	gcc -Wall -g leaky.c -o leaky

strip: debug
	strip buggy
	strip leaky

Adding 'buggy.c' and 'leaky.c' as a dependency after the respective targets forces make to re-compile those programs if the .c source file is modified (which is what you want it to do).

Now you can just type 'make' to build both.

Step #6: Find the dynamic memory abuses with Electric Fence

Note: From here on out, we're following the steps outlined in the book beginning on page 55. Note that I have changed the order of some of the lines in the sample code.

Add -lefence to the 'leaky' target's command so that it reads:

	gcc -Wall -g leaky.c -o leaky -lefence

This means "link with the libefence library". Note that you do not type "-llibefence", but just "-lefence"; the lib- prefix is assumed (and standard).

Now if you re-build and re-run the program, it will catch most of the dynamic leaks. You may need to set some environment variables. If you like, you can run the program through gdb to help you get a look at the lines where the memory abuses occur.

Step #7: Find memory leaks with checkergcc

An alternative to using Electric Fence is to use 'checkergcc'. You can replace each instance of 'gcc' with 'checkergcc' in your Makefile. This is a good occasion to use makefile variables so you only have to change it in one place. Modify your Makefile to look like this:

CC=checkergcc

debug: buggy leaky

buggy: buggy.c
    $(CC) -Wall -g buggy.c -o buggy

leaky: leaky.c Makefile
    $(CC) -Wall -g leaky.c -o leaky
    #$(CC) -Wall -g leaky.c -o leaky -lefence

strip: debug
    strip buggy
    strip leaky

Now, to switch back and forth, you can just change the value of the CC variable. Note that checkergcc and libefence do not play nicely together, so you also have to comment out the line that links to libefence. (Online copy of the final Makefile.)

Now you can compile it and run it and it will print lots of helpful information about your memory leaks at run-time, rather than making you use a debugger. Due to the sheer amount of information that checkergcc produces, you will probably want to run the commmand like this:

	./leaky 2>&1 | less

The "2>&1" means: "send stdout to the same place as stderr". The "| less" means: "pipe output through the less program", which allows you to scroll up and down, search for things, etc.

Other Stuff

You might find it instructive to use the strace program. This program shows system calls made by a program and are occasionally helpful in tracking down where a problem is occurring. The previously mentioned approaches are usually superior. People usually use strace when they haven't got any source code available.

Files

Here is a complete listing of all the files for this lab. You should put them all in a lib2 directory with these names: