CSC 101 Lab Notes Week 9 and 10

CSC 101 Lab Notes Weeks 9 and 10
Using a Symbolic Debugger


Overview

This lab focuses on tools for program debugging. The major debugging tool we will use is named "gdb" -- the Gnu debugger. We will also see how gdb can be used inside the Emacs editor to provide a convenient debugging environment.

Symbolic Debugging with Gdb

Gdb allows the programmer to perform the following general functions:

  1. run a program in a controlled environment that traps errors and allows the errors to be investigated
  2. set program breakpoints at lines within a program where program execution will pause.
  3. print out the values of program variables at breakpoints
  4. continue execution after a breakpoint
  5. execute a program one line at a time by "single stepping"

The exercises that follow illustrate the gdb commands that perform these functions. You should read through the exercises and then perform them by running gdb and executing the commands that are illustrated.

NOTE: In order for gdb to run a program properly, the program must be compiled with the -g argument, in addition to any other arguments that the compiler requires. E.g., the program in the first exercise below was compiled as follows:

CC -g lab9-ex1.cpp -o lab9-ex1

Exercise 1: Using Gdb to Debug an Infinite Loop

Consider the following program, from the file lab9-ex1.cpp:



////
//
// This is the example used in Exercise 1 of Lab Notes Week 9.
//
// Author: Gene Fisher (gfisher@calpoly.edu)
// Created: 24may99
// Modified: 24may99
//
////

#include "Boolean.h"

const int LOOP_LIMIT = 5;               // Upper limit of loop iterations


int main() {
    Boolean continue_loop = TRUE;       // Loop sentinel variable
    int i;                              // Loop counter

    //
    // Execute loop while continue_loop is true.
    //
    for (i = 0; continue_loop = TRUE; i++) {
        if (i > LOOP_LIMIT) {
            continue_loop = FALSE;
        }
    }
}

The problem with the program is that it has an infinite loop. Here is how gdb is used to run the program and inspect program variables to determine the cause of the problem.


Command/Message                              Description
--------------------------------------------+--------------------------------------------
polylog1> gdb lab9-ex1                      |At  the  UNIX  prompt,  run the executable
                                            |program lab9-ex1 under gdb control.
                                            |
GDB is free software ...                    |Gdb starts with an opening  message,  then
                                            |outputs its command prompt "(gdb) ".
                                            |
(gdb) run                                   |Enter  the  gdb  run  command to start the
                                            |program.  The program will execute exactly
                                            |as  it  would  if  run directly from UNIX,
                                            |except now gdb will be in control  of  the
                                            |program.
                                            |
Starting program: ...                       |Gdb   responds   to  the  run  command  by
                                            |outputting a message with the name of  the
                                            |program file that it's running.
                                            |
^C^C                                        |Since  the program is in an infinite loop,
                                            |you must type control-C a couple times  to
                                            |stop  it.  This is the same way to stop an
                                            |infinite loop program at the UNIX  command
                                            |level.   When  the program is stopped from
                                            |inside gdb, we will be able to see  better
                                            |what is going on.
                                            |
Program received ... Interrupt.             |Gdb responds to the control-C interrupt by
0x10810 in main () at lab9-ex1.cpp:24       |outputting an informational message.   The
24                if (i > LOOP_LIMIT) {     |first  line  of  the message says that the
                                            |program stopped because you interrupted it
                                            |with  control-C.   The  second line of the
                                            |message says where the program was stopped
                                            |(you  can  ignore  the "0x10810" number --
                                            |it's the actual  computer  memory  address
                                            |where  the  program  stopped).   The third
                                            |line of the message is a copy  of  program
                                            |source line where the program stopped.  In
                                            |this  case,  it's  in  the  body  of   the
                                            |infinite loop.
                                            |
(gdb) print i                               |Use the print command to examine the value
                                            |of   program   variables.     Any    legal
                                            |expression can given to the print command.
                                            |Here we print the value of variable i.
                                            |
$1 = 24850033                               |Gdb  responds  to  the  print  command  by
                                            |outputting  the  value  of  i.  It's clear
                                            |from this value that  i  is  way  to  big,
                                            |since  according  to  the intended program
                                            |logic, the loop should  have  quit  at  5.
                                            |Using  this  information,  we  inspect the
                                            |program code, and deduce that the  bug  is
                                            |due to the mistyped loop condition
                                            |    continue_loop = TRUE
                                            |which should be
                                            |    continue_loop == TRUE
                                            |
                                            |(Note  that  the "$1 =" part of the output
                                            |is a numbering scheme used by gdb to  list
                                            |all of its outputs.  You can safely ignore
                                            |the "$" numbers.)
                                            |
(gdb) quit                                  |To exit gdb, type the quit command.
The program is running.                     |
Quit anyway (and kill it)? (y or n) y       |
polylog1>                                   |

Exercise 2: Using Gdb to Debug an Out-of-Bounds Array Reference

Consider the following program



////
//
// This is the example used in Exercise 2 of Lab Notes Week 9.
//
// Author: Gene Fisher (gfisher@calpoly.edu)
// Created: 24may99
// Modified: 24may99
//
////

#include "Boolean.h"

const int LOOP_LIMIT = 5;               // Upper limit of loop iterations
const int ARRAY_SIZE = 100;             // Size of array.


int main() {
    int a[ARRAY_SIZE];                  // Array variable
    Boolean continue_loop = TRUE;       // Loop sentinel variable
    int i;                              // Loop counter

    //
    // Execute loop while continue_loop is true.  For each loop iteration,
    // store 10 times the value of the loop counter i in the ith array element.
    //
    for (i = 0; continue_loop = TRUE; i++) {

        //
        // Signal loop exit when i exceeds limit.
        //
        if (i > LOOP_LIMIT) {
            continue_loop = FALSE;
        }

        //
        // Store i*10 in the next array element.
        //
        a[i] = i*10;
    }
}

This program has the same logic bug as the program in Exercise 1.  Here, since
an array is involved, the infinite loop is terminated when the program tries to
write too far past the end of the array declared to have 100 elements.  Here is
how gdb is used to debug the program:



Command/Message                              Description
--------------------------------------------+--------------------------------------------
polylog1> gdb lab9-ex2                      |At the UNIX  prompt,  run  the  executable
                                            |program lab9-ex2 under gdb control.
                                            |
GDB is free software ...                    |Gdb  starts  with an opening message, then
                                            |outputs its command prompt "(gdb) ".
                                            |
(gdb) run                                   |Enter the gdb run  command  to  start  the
                                            |program.  The program will execute exactly
                                            |as it would if  run  directly  from  UNIX,
                                            |except  now  gdb will be in control of the
                                            |program.
                                            |
Starting program: ...                       |Gdb  responds  to  the  run   command   by
Program received ... Segmentation fault.    |outputting  a message with the name of the
0x10844 in main () at lab9-ex2.cpp:38       |program file that it's  running.   Shortly
38                a[i] = i*10;              |after  execution  begins,  gdb  prints the
                                            |"Segmentation fault" message, because  the
                                            |program  has  attempted  to write into the
                                            |array  beyond  the  segment  of   computer
                                            |memory  the program has been allotted.  As
                                            |when  the  program  was  interrupted  with
                                            |control-C,  gdb prints out the source line
                                            |of  the  program  where  the  segmentation
                                            |fault interrupt occurred.
                                            |
(gdb) print i                               |Again,  use  the  print command to inspect
                                            |the value of i.
                                            |
$1 = 646                                    |In this program,  i  has  only  reached  a
                                            |value of 646 before the Segmentation fault
                                            |occurred.  The fix  to  the  bug  in  this
                                            |program  is  the same as it is in Exercise
                                            |1.

Exercise 3: Setting Breakpoints

Suppose when debugging the program in exercise 2 we want to stop program execution before the Segmentation fault occurs. Gdb allows a program breakpoint to be set for such purposes. Here is an example that continues the debugging session begun in Exercise 2:


Command/Message                              Description
--------------------------------------------+--------------------------------------------
(gdb)  break 38                             |The gdb break command is  used  to  set  a
                                            |breakpoint  at  a  selected  line  of  the
                                            |program.  Any number of breakpoints can be
                                            |set  at  different  points in the program.
                                            |When the program reaches a line at which a
                                            |breakpoint  has  been  set, gdb interrupts
                                            |the program temporarily  so  that  program
                                            |values can be investigated.
                                            |
Breakpoint 1 at ...                         |Gdb  responds  to  the  break  command  by
                                            |outputting a  message  indicating  exactly
                                            |where the breakpoint has been set.
                                            |
(gdb) run                                   |Execute  the  run  command  to  start  the
                                            |program over again.
                                            |
The program ... started already.            |Gdb responds to this second run command by
Start it from the beginning? (y or n) y     |indicating  that  the  program has already
                                            |been  started  and  is  technically  still
                                            |running (even though it had a Segmentation
                                            |fault).  Type y to proceed with execution.
                                            |
Breakpoint 1, main () at lab9-ex2.cpp:38    |When  the  program  reaches  line  38, gdb
38                a[i] = i*10;              |takes  control  and   prints   a   message
                                            |announcing  that  a  breakpoint  has  been
                                            |reached, including the line on  which  the
                                            |breakpoint  occurred.   At this point, any
                                            |legal gdb command can be executed.
                                            |
(gdb) print i                               |Use the print command to examine the value
                                            |of i.
                                            |
$2 = 0                                      |Gdb prints the value of i as 0.
                                            |
(gdb) cont                                  |The  gdb  cont command is used to continue
                                            |execution after a breakpoint.
                                            |
Continuing.                                 |Gdb responds to cont with a brief message.
Breakpoint 1, main () at lab9-ex2.cpp:38    |Then,  since  the breakpoint is in a loop,
38                a[i] = i*10;              |the program comes  back  to  line  38  and
                                            |stops again.
                                            |
(gdb) print i                               |Issue  a  series  of  commands to print i,
$3 = 1                                      |a[0] and a[1].  Note that  since  we  have
(gdb) print a[0]                            |stopped  on line 38, this line has not yet
$4 = 0                                      |been executed, so the value  of  a[1]  has
(gdb) print a[1]                            |not yet been set.  We can use the gdb next
$5 = 0                                      |command to execute just one line.
                                            |
(gdb) next                                  |The next command tells gdb  to  execute  a
                                            |single  line and stop again.  In this case
                                            |line 38 is executed.
                                            |
39        }                                 |Gdb  responds  to  the  next  command   by
                                            |printing  the  source  line of the program
                                            |where it stops.  Note that line 39 has not
                                            |yet been executed.
                                            |
(gdb) print a[i]                            |Use  print  to  inspect the value of a[i],
$6 = 10                                     |which is now 10 as expected.  Note how the
                                            |value  of  i is used in the print command.
                                            |Any legal C++ expression can be printed.
                                            |
(gdb) delete 1                              |Use  the  delete  command  to   remove   a
                                            |breakpoint.   Note  that  breakpoints  are
                                            |deleted  by  number.   You  can  list  all
                                            |currently  set  breakpoints using the info
                                            |break gdb command.
                                            |
(gdb) cont                                  |When  execution  is  continued,  the  same
Continuing.                                 |segmentation fault as before occurs, since
... Segmentation fault.                     |the program still has the bug in it.
...                                         |
                                            |
(gdb) quit                                  |Use the quit command to leave gdb.

Exercise 4: Using Gdb Inside Emacs

Gdb can be run inside Emacs using a few basic commands. The commands are:


Command     Description
-----------------------------------------------------
M-x gdb     Run  gdb  inside  emacs.   The   display
            screen  is  split into two windows, with
            gdb running in one window.   All  normal
            gdb  commands  can  be  typed in the gdb
            window, just as they would typed if  gdb
            were running directly under UNIX.

C-x o       Move  the  emacs  cursor  to  the  other
            window.   This  allows  you  to   switch
            between  the  gdb  window  and  whatever
            other window you have.

C-x space   Here "space" means type the  space  key.
            This  command  is  used  by  moving  the
            cursor to the line of C++ source program
            in  non-gdb  display  window.   When C-x
            space  is  typed   at   that   line,   a
            breakpoint  is  automatically set in the
            gdb window.

The main advantage of running gdb inside emacs is that whenever an program interrupt or breakpoint occurs, emacs automatically opens the source file where the interrupt or break happened, moves the cursor to the affected line of the program, and points to the line with an indicator "=>".

To experiment with gdb in Emacs, you can rerun Exercises 1 through 3 from inside Emacs.



index | lectures | labs | handouts | assignments | solutions | grades | help