[contents] [usage] [execution] [stack] [breakpoints] [watchpoints] [advanced]

7.1 Example Debugging Session: Infinite Loop Example

We are going to use gdb to discover where the infinite loop in the following program is. It may be obvious to you on inspection, but it is instructive to use gdb to find it. The program should print out all the alphanumeric (letter and number) characters in it's input.

1 : #include <stdio.h>
2 : #include <ctype.h>
3 : 
4 : int main(int argc, char **argv)
5 : {
6 :   char c;
7 :
8 :   c = fgetc(stdin);
9 :   while(c != EOF){
10:
11:	    if(isalnum(c))
12:	      printf("%c", c);
13:     else
14:	      c = fgetc(stdin);
15:   }
16:
17:   return 1;
18: }


The first step is to compile the program with debugging flags:

prompt> gcc -g inf.c

Now if we run this program and enter a few characters followed by a newline, we discover something is amiss. Note that you will have to press Ctrl-C to stop this program once the infinite loop starts!

prompt> a.out
bob
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
...

Obviously, we have a problem. Lets load up gdb:

prompt> gdb a.out
GNU gdb 5.0
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...
(gdb) 

To find the problem, we'll set off the infinite loop, and then press Ctrl-C to send the program a SIGINT. Gdb will trap this signal and stop program execution.

(gdb) run
Starting program: /home/dgawd/cpsc/363/a.out 
moo
mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
....
mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
Program received signal SIGINT, Interrupt.
0x400d8dc4 in write () from /lib/libc.so.6
(gdb) 

Now the program is stopped and we can see where we are. We will use the backtrace command to examine the stack. The output of this command depends on your C libraries and exactly where the program was when you sent it the SIGINT. Mine looks like this:

(gdb) backtrace
#0  0x400d8dc4 in write () from /lib/libc.so.6
#1  0x40124bf4 in __check_rhosts_file () from /lib/libc.so.6
#2  0x40086ee8 in _IO_do_write () from /lib/libc.so.6
#3  0x40086e46 in _IO_do_write () from /lib/libc.so.6
#4  0x40087113 in _IO_file_overflow () from /lib/libc.so.6
#5  0x40087de5 in __overflow () from /lib/libc.so.6
#6  0x40069696 in vfprintf () from /lib/libc.so.6
#7  0x40070d76 in printf () from /lib/libc.so.6
#8  0x80484c2 in main (argc=1, argv=0xbffffaf4) at inf.c:12
#9  0x40037f5c in __libc_start_main () from /lib/libc.so.6

From this output, we can see that the program stopped in the write() system call inside the C library. But what we really want to see is where we are in main, so we are going to switch to frame 8:

(gdb) frame 8
#8  0x80484c2 in main (argc=1, argv=0xbffffaf4) at inf.c:12
12                printf("%c", c);

From the output, we know that the value of c is probably 'm', but lets check anyway:

(gdb) print c
$1 = 109 'm'

Now we have to find the loop. We use several iterations of the 'next' command to watch what is happening. Note that we have to work our way up the call stack back to main(). The next command will exit any functions we can't debug (like C library functions). We could also use the finish command here.

(gdb) next
Single stepping until exit from function write, 
which has no line number information.
0x40087778 in _IO_file_write () from /lib/libc.so.6
(gdb) next
Single stepping until exit from function _IO_file_write, 
which has no line number information.
0x40086ee8 in _IO_do_write () from /lib/libc.so.6
.....
.....
(gdb) 
Single stepping until exit from function printf, 
which has no line number information.
main (argc=1, argv=0xbffffaf4) at inf.c:15
15        }

Ok, now we are inside main(). We run the next command several more times to watch the program execute.

(gdb) n
11              if(isalnum(c))
(gdb) 
12                printf("%c", c);
(gdb) 
15        }
(gdb) 
11              if(isalnum(c))
(gdb) 
12                printf("%c", c);
(gdb) n
15        }
(gdb) 
11              if(isalnum(c))
(gdb) 
12                printf("%c", c);

Notice a pattern? The same two lines of code are executing over and over. This means we are looping, inside the while loop on line 9. Now, the value of 'c' is not changed in these lines. Maybe we should look back at our program:

11:	    if(isalnum(c))
12:	      printf("%c", c);
13:     else
14:	      c = fgetc(stdin);

The lines being executed are 11 and 12. The test is always passing because the character is never changing. So the program is only reading characters until it finds an alphanumeric character, after which it never reaches the fgetc. But we always want to read the next character. Removing the 'else' on line 13 will fix the bug.





[contents] [usage] [execution] [stack] [breakpoints] [watchpoints] [advanced]

Questions? Comments? Flames? email rms@unknownroad.com