Having written programs before, the notion of a bug will be familiar. The UNIX system provides debuggers for tackling problems that evade the Ansi C compiler. There are many debuggers available for use, we shall examine gdb.
The most common sort of errors that occur when executing your program are:
Using the debuggers, the line in the program where the error occurred can be identified, as well as the value of the variables at that point.
To debug bug1.c using the available debugger, gdb. Although some of the errors are obvious let's use gdb to find them; don't modify the program directly, follow the instructions below to familiarise yourself with gdb.
Since bug1.c will be modified, it might be wise to copy the original onto a backup. Call it bug1.c.bak. This way you can always refer to the original if you need to.
Before the debugger can be used to debug your program, the program must be compiled with the -g option. The -g option causes the compiler to generate additional information that is used by the debugger. So edit the Makefile to compile bug1.c with the -g option. Run make.
Examine the contents of bug1.c, read the header, and note what the program is supposed to do. Now try executing your program. You should find that it doesn't work according to its specification.
You should get the following error message:
Segmentation fault (core dumped)
To find out what is wrong in the program let's put it through our debugger. To do this type:
$gdb bug1
If this is the first time you have used a debugger it is a good idea to examine the sort of functions the debugger provides. Type:
(gdb) help
The gdb help utility groups its commands into various classes. For example all the commands related to examining the internal stack produced by running your program are listed under the stack command. Spend some time browsing through the types of commands available under each class. Type:
(gdb) help running (gdb) help stack (gdb) help data (gdb) help breakpoints
Go down the list, without worrying too much about what each command does or how it works. For further information on each utility type help followed by the utility name. We shall now use some of gdb's features to debug our simple program.
To start let's list the whole source program. Type:
(gdb) help list (gdb) list bug1.c:1,20
Since we aren't exactly sure where the error occurred, let's execute the program using the debugger then examine the stack to discover precisely where the program crashed. Type:
(gdb) help run (gdb) run
You'll see the following message and prompt:
Starting program: /...pathname.../bug1 Do you wish to read in a word? (y/n)
Now we have reproduced the same error as before while running the program through the debugger. However this time we have a little more information. You should see something like
Program received signal 11, Segmentation fault 0x400fe8 in _doprnt () at ../doprnt.c:327 ../doprnt.c:327: No such file or directory.
This may seem a little strange since we have no function doprnt() in our program. By printing a STACK TRACE we can work out where the problem occurred. Before we do so, examine the stack commands using the help utility. Type:
(gdb) help stack (gdb) help bt (gdb) bt
You should see something like:
#0 0x400fe8 in _doprnt () at ../doprnt.c:327 #1 0x400680 in fprintf () at ../fprintf.c:110 #2 0x400270 in main () at bug1.c:24
This says that main called fprintf, which called _doprnt. Since _doprnt isn't mentioned anywhere in bug1.c, our troubles must be somewhere in fprintf or above. If we look at the fprintf invocation in this defective program, it is wrong. There's no FILE pointer specified so it is treating the string "Finished reading in words\n" as such.
A STACK TRACE can be printed by using the "where" gdb command. The "where" command has been aliased to the "bt" command. Type:
(gdb) help aliases
for a list of aliases available.
Such errors, in which a function is called with the wrong arguments, can be found by using the C verifier lint(). Lint examines C programs for potential errors, portability problems and dubious constructs. Also such errors may be determined using the compiler gcc.
There are two ways to modify your program.
(gdb) help support (gdb) quit
The program is running. Quit anyway (y or n)?
(gdb) help support (gdb) shell vi bug1.c (gdb) kill
Kill the inferior process? (y or n)
(gdb) make bug1
Obviously the program is not doing what it should be. Why is the program not executing the correct statement and what is causing this to happen? This is what we shall try to figure out next.
We shall do this by tracing the program and examining the value of variables along the way. Go back into gdb if you are not already in there. List the source code.
Let's examine if the correct value has been stored in the variable ReadInWord. To do this set a break-point at the line in the program which contains the if-statement. A break point marks the position in the source code where execution should temporarily halt. Then examine the variable's value.
(gdb) help breakpoints (gdb) break 23
(gdb) help status (gdb) help info (gdb) info breakpoints
Num Type Disp Enb Address What 1 breakpoint keep y 0x0040025c in main at bug1.c:23
This tells you that a breakpoint is set in function main at line 23 in the program called bug1.c. Now run the program from within gdb entering 'y' as input.
The program will not run to completion, rather it will stop execution at the line where the breakpoint is. Note that line has not yet been executed.
(gdb) help data (gdb) help print (gdb) print ReadInWord[0]
$1 = 121 'y'
Go back and read the help information provided for the print utility. You should work out at least that the variable ReadInWord so far contains the correct value.
(gdb) help step (gdb) step
What did you notice? Examine the value stored in ReadInWord. What value is stored there now? Why did it change? How can you fix the problem? Edit your program. Compile and run your program, entering 'y' as input.
Under LINUX, you should find another error occurring even before any word is entered:
Segmentation fault (core dumped)
(gdb) print i (gdb) print (char)c
You should find that i is set to 0 and c contains a '\n' character (ascii value 10). Why has the for-loop terminated before a word is entered? How would you discard the '\n' after the response 'n' or 'y' is entered? Fix the program, compile and run it, type in a word.
Under Linux, you should find a segmentation fault occurring.
The program is trying to store a character in an area of memory that it's not allowed to. Examine the variable word. It is a pointer to a character. Has space been allocated to store the word? Obviously not. Allocate space to hold the word by defining word to be an array of 20 characters or using malloc() function.
Edit and compile your program. Run it, and notice that this time no errors are produced but the program isn't printing out the word entered by the user.
Set a break point at the start of the for-loop. Rerun your program.
Step through the for-loop skipping, proceeding through the call to getchar().
(gdb) help next (gdb) next
What is the value of i? What is stored in c? Now execute the next line. Type
(gdb) step
What is stored in the array word? What does this indicate?
Modify your program, so that the correct block is included in the body of the for-loop. Recompile your program and run it again.
make clean