This chapter introduces you to the internal Perl debugger. Perl has modules with debugging facilities to help you catch bugs in your Perl scripts. The use of the debugger will prove to be invaluable when you're debugging complicated Perl scripts, especially when writing or working with your own Perl modules. Admittedly, the debugging facilities in Perl are not as fancy as those found in packages for C, C++, or BASIC; however, they can be very powerful tools in catching nasty bugs that are not caught by the Perl compiler or by the warning facility in Perl.
There are two switches to Perl you should be aware of: the -d switch for turning on the debugger for a script and the -w switch for printing out warning messages when ambiguous code is encountered. We will be covering the -d switch through most of this chapter. Warning messages are printed by the Perl interpreter when suspect code is encountered. To turn on the ability to print warnings in header code, add the -w to the header comment as shown below:
#!/usr/bin/perl -w
You will find, though, that simply getting warning messages is not enough to debug your code. What if you need to monitor the values of variables in a program? For such a feature, you will have to use a debugger.
To invoke the debugger you simply invoke your script with the -d option. It's also possible to place the -d option in the first line of script. Here's an example:
#!/usr/bin/perl -d
The debugger is invoked when the script is run. You are presented a message about the version of the debugger and a prompt to type your commands. You issue the cryptic one-character commands at the prompt in order to get the debugger to execute the program, set breakpoints, view or edit variable contents, and so on.
The debugger is a Perl module called DB and comes with the Perl 5.002 distribution files. All required source code for the DB module is in the file perl5db.pl in your /usr/lib/perl5 directory. You must have the perl5db.pl file in your PERLLIBDIR path for the debugger to work. The perl5db.pl file contains the entire list of commands available to you. (Look in the middle of the source file around line 128 to see what commands are available.)
When the debugger is invoked, you have a set of one- or two-character commands that you can use. Typing the h command gives you a list of all commands available to you. Unfortunately, unless you have a very long screen, the output scrolls by very quickly. Therefore, if you are not familiar with the Perl debugger, I strongly recommend at least going through the debugger commands detailed in this chapter once before you attempt a debug session on an important Perl program on your own.
Use the sample script shown in Listing 30.1 to run a sample debug session. You have seen this script before in Chapter 3, "References."
Listing 30.1. A sample script to be debugged.
1 #!/usr/bin/perl -d
2
3 sub xyc {
4 my $a = $_[0];
5 print $a;
6 }
7 # --------------------------------------------------------------
8 # Define each state as subroutine. Then create a
9 # reference to each subroutine. We have four states here.
10 # --------------------------------------------------------------
11 $s0 = sub {
12 my $a = $_[0];
13 print "State 0 processing $a \n";
14 if ($a eq '0') { return(0); }
15 if ($a eq '1') { return(1); }
16 if ($a eq '2') { return(2); }
17 if ($a eq '3') { xyc("hello"); return(3); }
18 return 0;
19 };
20 # --------------------------------------------------------------
21 $s1 = sub {
22 local $a = shift @_;
23 print "State 1 processing $a \n";
24 if ($a eq '0') { return(0); }
25 if ($a eq '1') { return(1); }
26 if ($a eq '2') { return(2); }
27 if ($a eq '3') { return(3); }
28 return 1;
29 };
30 # --------------------------------------------------------------
31 $s2 = sub {
32 local $a = $_[0];
33 print "State 2 processing $a \n";
34 if ($a eq '0') { return(0); }
35 if ($a eq '1') { return(1); }
36 if ($a eq '2') { return(2); }
37 if ($a eq '3') { return(3); }
38 return 2;
39 };
40 # --------------------------------------------------------------
41 $s3 = sub {
42 local $a = shift @_;
43 print "State 3 processing $a \n";
44 if ($a eq '0') { return(0); }
45 if ($a eq '1') { return(1); }
46 if ($a eq '2') { return(2); }
47 if ($a eq '3') { return(3); }
48 return 3;
49 };
50
51 # --------------------------------------------------------------
52 # Create an array of pointers to subroutines. The index
53 # into this array is the current state.
54 # --------------------------------------------------------------
55 @stateTable = ($s0, $s1, $s2, $s3);
56
57 # --------------------------------------------------------------
58 # Intialize the state to 0.
59 # --------------------------------------------------------------
60 $this = 0;
61
62 # --------------------------------------------------------------
63 # Implement the state machine.
64 # set current state to 0
65 # forever
66 # get response
67 # set current state to next state based on response.
68 # --------------------------------------------------------------
69 while (1)
70 {
71 print "\n This state is : $this -> what next? ";
72 $reply = <STDIN>;
73 chop($reply);
74
75 #
76 # Stop the machine here
77 #
78 if ($reply eq 'q') { exit(0); }
79 print " Reply = $reply \n";
80
81 #
82 # Get the present state function.
83 #
84 $state = $stateTable[$this];
85 #
86 # Get the next state from this state.
87 #
88 $next = &$state($reply);
89 printf "Next state = $next from this state $this\n";
90 #
91 # Now advance present state to next state
92 #
93 $this = $next;
94 }
When you first run this program, you are presented the screen shown in Figure 30.1. The filename, line number, and line of code that will be executed next are shown before the prompt.
Figure 30.1 : Using the Perl debugger.
The prompt has a number between the less-than and greater-than signs to signify the calling level. As the program execution continues, the number will indicate the number of debugger commands in the history of commands in the debugger. For example, if you have three commands in the debugger history list, your prompt will look like this:
DB<3>
Let's first see how we can list the contents of the program we are debugging. To list the contents of the file, you have to use the l command. By default, 10 lines of the source code are listed. Every time you type an l command, the next 10 lines of the file are listed, as shown in Figure 30.2.
Figure 30.2 : Listing the source with the 1 command.
You can look at just one specific line by specifying the line number as a parameter to the l command. For example, the command to print the code on line 23 would be l 23.
The l command can also take one set of line numbers to specify a range of parameters. The ranges have to be specified in the following form. Lines numbers in a range are inclusive.
startLine-endLine.
Don't specify the endLine number as less than the startLine number. To list the source file lines from 20 through 41, try this command:
l 20-41
To look at the 10 lines preceding the current command, use the - command. For example, if you are at line 23, typing in the - command will show lines 13 to 22.
The w command will print a window of lines of code around the current line by printing three lines before the current line (or the line number you specify after it as an option to the w command), followed by the code at the current line, and up to the next six lines of code. For example, the command
w 5
will print line numbers 2 through 11.
You can step through the source code with the s command. Pressing the s key will successively walk you through each line of the code as it executes, as shown in Figure 30.3.
Figure 30.3 : Using the step function.
The s command steps to the next line of code. If the line of code is in a module whose code is accessible, you'll go to it and stop. To step over a function, use the n command, which goes to the next instruction at the same scope.
The c command executes the debugged program in continuous mode. That is, the program will run until either a breakpoint is hit or the program terminates. To run in continuous mode, simply type c at the debug prompt with no parameters. The c command can also be used to execute from the current line to another known line number:
c newLineNumber
For example, if you are at line 7, to execute from the current line number (7) to line number 12, you would issue the command c 12.
To see the values of certain variables in the program, use the V command. Used by itself, V lists all the variables in scope at this time. Here's the syntax:
V [package [variable]]
To look at values in your program, you'll want to look at the main package. For example, to print the value of $reply, use this command:
V main reply
$reply = '1'
Note that the dollar sign before the variable specified to V is not supplied. Therefore, if you specify the command V main $reply, you are actually asking for the value of $$reply and not $reply.
The trace option is available with the t toggle command. Issuing trace once turns it on, and issuing it again turns it off. See Figure 30.4 for a sample use of the trace command on Listing 30.2. In this example, trace is turned on, and then the c command is issued to run the debugger continuously. In trace mode, the debugger prints out each line of code that executes.
Figure 30.4 : Using the trace command with breakpoints.
The X command is helpful when displaying values of variables in the current package. Remember that the main package is the default package for a Perl script. Issued by itself with no options, the X command displays all the variables in the current package. Avoid issuing the X command by itself because it can generate a very long listing of all the variables in the main package.
To see the value of a particular variable instead of all the variables, type the name of the variable after the X command. For example, the following command
X fileNumber
will print the value of the fileNumber variable in the current package. If you have array variables and scalar variables with the same name in the same package, the X command will display the values of both these variables. For example, if you have a scalar variable called names and an array called names, the X command will show the values of both variables:
DB<3> X names
$names = "kamran"
@names = (
"kamran"
"joe"
"donald"
)
You can place breakpoints at suspect locations in your code and run the program until one of the specified breakpoints is hit. Breakpoints can be specified to be hit as soon as the line of code is about to be executed.
The c command is used to step forward until either the program stops or a specified breakpoint is hit. To specify a breakpoint at the current line, use the b command without any parameters. To specify a specific line, use the command of the form:
b linenumber
Usually, you use trace statements to see statements between the current execution point and a breakpoint (refer to Figure 30.4). The program is run in continuous mode with the c command until it hits a breakpoint. There is a breakpoint in Listing 30.1 that causes the debugger to stop. The L command is issued in the example to list the breakpoints in the system.
Breakpoints can also be specified to occur at the first executable line of code within a subroutine. Simply use the b command with the name of the subroutine as the first parameter. For example, to break at the first line of code in the xyc subroutine, try this command:
b xyc
You can also ask the debugger to look at a condition when a line is hit with a breakpoint tag on it. If the breakpoint is specified at a line and the condition is true, the debugger stops; otherwise, it keeps on going. For example, if you want the debugger to stop in xyc only when the global $reply is 1, use this command:
b xyc ($reply == '1')
To list all breakpoints defined during a debug session, use the L command. If you issue unconditional breakpoints, you'll see breakpoints listed as this:
break if (1)
The L command will also list up to the last five executed lines of the program.
To remove a breakpoint, use the d command and specify the line number to delete. To remove all breakpoints, use the D command. For example, to delete a breakpoint at line 12, you would issue the command d 12.
The DB package uses the following sequence to hit breakpoints and evaluate code on each line of executable code:
You can specify actions to take when a certain line of code is executed. This step is very important when you want to print out values as the program executes (see Figure 30.5). Notice how the value of reply is printed out when line 73 is reached. The action is defined with this statement:
Figure 30.5 : Using actions in the debugger.
a 73 print "I am on line 73 and reply is $reply"
Notice that you did not have to terminate the action command with a semicolon. You need to use semicolons only if you have more than one statement for an action. If you forget to supply the terminating semicolon, the debugger will supply it for you. In any event, try to keep actions simple and short. Don't write lengthy actions unless absolutely necessary; otherwise, you'll slow down the debugger and clutter up the output on your terminal.
Actions are not limited to displaying values. For instance, you can use an action to reset a variable to a known value while in a loop, using a statement like this:
a 73 $reply = 1; print "forced reply to 1\n";
To execute statements within the debugged program's space, simply type the command at the prompt. For example, to explicitly create and set the value of $kw to 2 in the code, use the following commands at the DB<> prompt:
DB<1> $kw = 2
... nothing is printed here ...
DB<1> print $kw
2
DB<1> V main kw
$kw = '2'
In this example, the variable $kw is created and defined in the program environment. You cannot modify the source code in the original program, but you can add items to the name space.
In some cases, your program may have redirected its output to STDOUT and therefore whatever it is printing will not be shown on the console. To evaluate an expression and print its value out to the console regardless of how STDOUT is redirected, you can use the p command. The p command evaluates an expression in the current program's environment and prints it out to the debugger console. Basically, the print command prints the output to wherever STDOUT is redirected, whereas the p command is equivalent to the following print command:
print DB::OUT
The command above forces output from a print command to where the DB:: package prints its output.
To look for certain strings in the source code, you can use the forward slash command followed by the string to look for. Note that there are no spaces between the / and the string you are looking for. The string can be specified between two slashes, but the second slash is optional. Actually, you can search for regular expressions, just as in Perl.
To search forward in the file, use the / operator. To search backward, use the question mark operator (?).
The history of the commands you have executed is tracked in the debugger. Only commands greater than one character long are listed in this directory. To execute commands from the history list, use the bang operator (!) followed by the index of the command. To execute a command from the history, type ! and the index of the command to redo. This should be familiar to Bash and C shell programmers.
To see the current history of commands in the buffer of commands in the debugger, type the H command. For example, in the middle of a debug session, if you type in the H command at the DB<3> prompt, you should expect to see three items listed in reverse order of execution:
DB<3> H
3: b 79
2: w 2
1: w 9
To list all the subroutines currently in the system, use the S command. The output from the S command lists all subroutines in any package that your code uses. For example, if you run the program in Listing 30.2 with the debugger, you will see output as shown in Figure 30.6.
Figure 30.6 : Listing subroutine names.
Listing 30.2. A sample listing.
1 #!/usr/bin/perl -d
2
3 use VRML;
4 use VRML::Cube;
5
6 my $header = VRML::new();
7 $header->VRML::startHeader;
8
9 $header->VRML::startSeparator;
10
11 my $cubeb = $header->VRML::putCube(
12 'width' => 0.5, 'height' => 0.5 , 'depth' => 0.5 ,
13 'translation' => [1,0,0]
14 );
15 my $cubed = $header->VRML::putCube(
16 'width' => 1, 'height' => 1 , 'depth' => 1 ,
17 'translation' => [1,1,0],
18 );
19 $header->VRML::stopSeparator;
At any time in a debug session, you can do a "stack trace," which is a listing of the calling order of the functions called so far. Be aware that if you are modifying the argument stack in any way, the values of the passed arguments might not be correct. The T command will do a stack trace for you.
First of all, there is no way to restart the debugger if there is a problem. If you overstep something, you have to start all over. This means getting out of the program and restarting the debugger.
Second, the debugger itself is not completely debugged yet. If you notice certain problems, such as your commands not being recognized, it's probably because you typed too many characters at the prompt.
Table 30.1 lists the information about the available debugger
commands. All information in this table is gleaned from the perl5db.pl
source file. Keep this table handy so that you don't have to go
to the file to see what options are available.
Command | Description |
a [ln] command | Sets an action to take before the line is executed. |
b | Sets an unconditional breakpoint at the current line. |
b [ln] [cond] | Sets a breakpoint if the condition is true at the specified line number. |
b sname [cond] | Sets a breakpoint at the first line inside the subroutine sname(). |
c | Continues until the next breakpoint or until the end of the program. |
c line | Continues and stops at the specified line. |
d [line] | Deletes the breakpoint at a given line. |
D | Deletes all breakpoints. |
f filename | Switches to the filename as the default. |
H -number | Displays history of all commands longer than one character. |
L | Lists all breakpoints and actions. |
l min+incr | Lists incr+1 lines starting at line #min. |
l min-max | Lists lines from min to max, inclusively. |
l line | Lists one line of code at a specified line. |
l | Lists the next 10 lines of code from the last location. |
l name | Lists a subroutine by name. |
n | Next code at the same level. Steps over subroutine calls. |
p expr | Same as print DB::OUT expr in current package. |
q or ^D | Quits. You cannot use quit. |
r | Returns from current subroutine. |
s | Single-step over code. Steps into subroutines. |
S | Lists all known subroutine names in the current scope. |
t | Toggles trace mode on and off. |
T | Performs a stack trace. |
V | Lists all variables in all used packages. |
V pkg | List all variables in a given package. |
V pkg var | Lists all variables in a package that have var in them. |
w line | Lists five lines before and five lines after current line. |
<CR> | Repeats last n or s. |
- | Lists the previous window. |
/regexp/ | Searches forward for a pattern using a regular expression. |
?regexp? | Searches backward for a pattern using a regular expression. |
< command | Defines the command before the prompt. |
> command | Defines the command after the prompt. |
! number | Redoes a command (the default is the previous command). |
! -number | Redoes number\'th to the last command. |
= [alias value] | Starts a command alias. |
= | Lists all the current aliases. |
command | Executes as a Perl statement in the current package. |
There are ways to customize your debugger environment. If you do not like the one-character commands that come with the debugger, you can use different aliases. There is a hash in the DB:: package called %alias() that contains the command strings. You can substitute your own commands in place of the existing ones using the = command. Since most of the time you'll want to keep your changes consistent between debug sessions, you can edit a file called .perldb in the current working directory and place the assignments there. Here's a sample .perldb file:
$DB::alias{'ln'} = 's/ln/p $1/';
$DB::alias{'z'} = 's/z/l/';
These two lines will substitute the value of p for every command ln you type, and the value of l for every z command. Of course, you'll probably want to alias long commands into short one-character sequences to save yourself some time.
Using the debugger should not be your only method for getting bugs out of the system. The -w switch is important if you want Perl to do checking and warn you of error conditions while executing. The types of messages generated vary from warnings to notifications of fatal errors that can cause the program to abort.
Reading the source file perl5db.pl gives you a few clues about how the debugger works and the commands that are available during a debug session. Consult the perldebug.html page at www.metronet.com. This file contains the full list of all the options in the debug environment. Review the perldiag.html page for a list of possible diagnostic values you get from using the w switch.
Nothing really beats the use of well-placed print statements to do debugging. However, Perl does offer a simple yet powerful debugging tool with the -d option. The interactive debugger lets you step through code, into or over subroutines, set breakpoints, execute commands, and look at variables in a Perl program.