Using the Debugger: A Simple Example

Probably the best way to see how Perl's debugger works is to show you an example of a typical use of the debugger. For this example, we'll step through the execution of a simple script that contains subroutines—the names script from Day 6, which reads in a list of names, prompts you for something to search for, and returns the names that match that search key.

When you run a script with the Perl debugger turned on, you'll end up inside the debugger itself, which looks something like this:

% perl
						-d  statssubbed.pl statsdata.txt

Loading DB routines from perl5db.pl version 1.01
Emacs support available.

Enter h or `h h' for help.

main::(statssubbed.pl:3):       &initvars();
  DB<1>

Note

Don't worry about starting the debugger yet; you'll learn how to do that after we do a little walkthrough.


The debugger prints out two lines of output at each step. That DB<1> on the second line is the debugger prompt; you'll type in various commands at this prompt. The number 1 means this is the first command; you can repeat commands by referring to the command number.

The first line provides information about where the debugger is in the script. The number in the information lineshows the line number of the line of code that will be executed next. The part on the left refers to the current package, name of the file, and the line number. The part on the right is the actual line of code, including any comments for that line.

To execute a line of code, use either the s or n commands. The s command is more exhaustive; it will step into subroutines. The n command only steps through lines of code at the top level of your script; it'll execute subroutines silently (in other words, it executes them without allowing you to step through them). When you've used either s or n, you can repeat that command by just pressing Return (or Enter) at each prompt:

main::(statssubbed.pl:3):       &initvars();
  DB<1> s
main::initvars(statssubbed.pl:8):           $input = "";  # temporary input
  DB<1>
main::initvars(statssubbed.pl:9):           @nums = ();   # array of numbers;
  DB<1>
main::initvars(statssubbed.pl:10):          %freq = ();   # hash of number frequ
encies
  DB<1>

Note in this example how when the script execution moves to the &initvars() subroutine (immediately, in the case of this script) that the information on the left side of the output shows the name of that subroutine. This way, you always know where within your script you are. But in case you forget, or can't place the position based on the one line, you can use the l command to list some lines:

DB<1> l
10==>       %freq = ();   # hash of number frequencies
11:         $maxfreq = 0; # maximum frequency
12:         $count = 0;   # count of numbers
13:         $sum = 0;     # sum of numbers
14:         $avg = 0;     # average
15:         $med = 0;     # median
16:         @keys = ();   # temp keys
17:         $totalspace = 0; # total space across histogram
18      }
19

The l command will list ten lines after the current line; you can also use - to print lines before the current line. Multiple uses of l and - will move back and forth in the script's source code. Keep in mind that this just prints the lines so you can get some context for where you are in the script—1 and - don't actually execute anything.

As you step through the code, you can print out the value of any variable—any scalar, array, hash, and so on, using the x command. In this next example, the &getinput() subroutine has just read a line from the data input file, stored it in the $input variable, and chomped the newline off the end of it. At DB<5> we print the value of $index, and print the current values of @nums (the array indexes are on the left, and the actual elements on the right). Keep in mind that the line of code displayed (line 23 in the source) is displayed before it is run, so the value of $input will not yet be in @nums.

main::getinput(statssubbed.pl:21):          while (defined ($input = <>)) {
  DB<5> s
main::getinput(statssubbed.pl:22):              chomp ($input);
  DB<5>
main::getinput(statssubbed.pl:23):              $nums[$count] = $input;
  DB<5> x
						$input
						0  5
DB<6> x
						@nums
0  1
1  4
2  3
3  4
DB<7>

You can also use the x command to execute bits of Perl code; in the following case, to find the number of elements in @raw, or to show the first element:

DB<3> x
						scalar(@nums)
0  4
  DB<4> x
						$raw[0]
0  1

Stepping through the script using n or s enables you to see each line in excruciating detail, but sometimes it's more detail than you want. If you're inside a subroutine, you can use the r command to stop stepping through that subroutine, execute the rest of it, and return to the place where that subroutine was called (which, in the case of nested subroutines, may be still another subroutine).

To stop stepping through the script at any time, use the c command (c for continue). Perl then runs the rest of the script with no intervening stops (unless you've put them into your script itself, for example, to read some input).

In addition to stepping through the execution of a script line by line, you can also control execution of your script with breakpoints. A breakpoint is a mark at a line of code or the beginning of a subroutine. Using c will run the script until the breakpoint, and then stop. At the breakpoint, you can use n or s to step through lines of code, use x to print variables, or use c to continue to the next breakpoint.

For example, take the statsmenu.pl script we created yesterday, which organized the stats script into several subroutines. The &countsum() subroutine prints out the count and the sum of the data, calling the &sumnums() subroutine to get the latter value. To set a breakpoint at the &sumnums() subroutine, use the b command with the name of that subroutine, then c to execute the script up to that breakpoint:

# perl
						-d statsmenu.pl statsdata.txt
Loading DB routines from perl5db.pl version 1.01
Emacs support available.

Enter h or `h h' for help.

main::(statsmenu.pl:3): @nums = (); # array of numbers;
  DB<1> b
						sumnums
  DB<2> c
Please choose one (or Q to quit):
1. Print data set
2. Print count and sum of numbers
3. Print maximum and minimum numbers
4. Print mean and median numbers
5. Print a histogram of the frequencies.

Your choice --> 2
Number of elements: 70
main::sumnums(statsmenu.pl:72):     my $sum = 0;
  DB<2>

Can't remember the name of a subroutine? You can use the S command to print all the available subroutines. S main in particular will show you the subroutines you've defined (although there may be some extra Perl subroutines listed in there as well). Take a look at the following example.

DB<7> S
						main
main::BEGIN
main::countsum
main::getinput
main::maxmin
main::meanmed
main::printdata
main::printhist
main::printmenu
main::sumnums
  DB<8>

Note

The main part is in reference to the main package, which is where all your variables and subroutines live by default. We'll explore more about packages tomorrow on Day 13.


If you want to set a breakpoint at a particular line, you can find the line using l to list the script, and then just use that line number with the b command:

DB<3> l
38:         print "5. Print a histogram of the frequencies.
";
39:         while () {
40:             print "
Your choice --> ";
41:             chomp($in = <STDIN>);
42:             if ($in =~ /^d$/ || $in =~ /^q$/i) {
43:                 return $in;
44              } else {
45:                 print "Not a choice.  1-5 or Q, please,
";
46              }
47          }
DB<3> b 43

One other neat feature of the debugger is the ability to trace the execution of the script as it's running. The s and n commands step through each statement one at a time, but sometimes it's useful to see a printout of each statement as it executes even if you aren't stepping through each one. The t command will turn tracing on or off, toggling between the two. Here's a trace from the &printdata() subroutine, with a breakpoint set at the top of the foreach loop:

  DB<1> b 54
  DB<2> t
    Trace = on
  DB<2> c
Please choose one (or Q to quit):
1. Print data set
2. Print count and sum of numbers
3. Print maximum and minimum numbers
4. Print mean and median numbers
5. Print a histogram of the frequencies.

Your choice --> 1
Data Set:
1 main::printdata(statsmenu.pl:54):         foreach $num (@nums) {
DB<2> c
main::printdata(statsmenu.pl:55):               print "$num ";
1 main::printdata(statsmenu.pl:56):             if ($i == 10) {
main::printdata(statsmenu.pl:59):               } else { $i++; }
main::printdata(statsmenu.pl:54):           foreach $num (@nums) {
  DB<2>

Note here that tracing prints not only the script's output (note the 1 at the start of the line in the middle of the output after the c command), but it also prints each script line as it's being executed. If we typed c here again, we'd get another loop of the foreach, and that same output all over again.

To quit the debugger, use the q command (not quit, just q).

DB<18> q
%

With that walkthrough under your belt, you should now have a basic idea of how the debugger works. For the rest of this section, I'll show you more detail on the specific commands.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset