7.2. Starting

The -d command line option makes your script run under the debugger. You can either add it to the options in the #! line at the beginning of your script, or you can override the options by explicitly running the script through perl. So for example, if wombat.pl currently has the -w option set, you can either change its first line to

#!/usr/bin/perl -wd

or you can type

% perl -wd wombat.pl
				

to debug it without having to change the script. Unlike some debuggers, with this one you supply arguments to the program on the command line, not as part of a debugger command; for example:

% perl -wd wombat.pl kangaroo platypus wallaby
				

The debugger will announce itself and provide a prompt:

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

Enter h or `h h' for help.

main::(wombat.pl:1):   my $marsupial = shift;
  DB<1>

From now on we will elide everything before the first prompt (and the code on which it is stopped) when reproducing debugger sessions.

To begin, let's look at some simple commands. The very first of interest, of course, is h for help. The output from this is several screens long, which gives us an opportunity to mention an option we can apply to all commands: put a vertical bar (|) before any command and it will run the output through your pager (the program that prints things one screen at a time, waiting for you to tell it when to continue—more or less more or less).

7.2.1. Watch the Code Execute: s, n, r

Enter this simple program into debug.pl:

#!/usr/local/bin/perl -w
use strict;

my @parole = qw(Salutations Hello Hey);

print_line(@parole);
print "Done
";

# Our subroutine accepts an array, then prints the
# value of each element appended to "Perl World."
sub print_line
   {
   my @parole = @_;
   foreach (@parole)
      {
      print "$_ Perl World
";
      }
   }

Now run it under the debugger and step through the program one statement at a time using the n (next) command:

% perl -dw debug.pl
main::(debug.pl:4):     my @parole = qw(Salutations Hello Hey);
  DB<1> n
main::(debug.pl:6):     &print_line(@parole);
  DB<1> n
Salutations Perl World
Hello Perl World
Hey Perl World
main::(debug.pl:7):     print "Done
";
  DB<1> n
Done
Debugged program terminated.  Use q to quit or R to restart, use
O inhibit_exit to avoid stopping after program termination, h
q, h R or h O to get additional info.
  DB<1> q
					

Before the prompt, the debugger prints the source line(s) containing the statement to be executed in the next step. (If you have more than one executable statement in a line, it prints the line each time you type n until it's done executing all the statements on the line.) Notice the output of our program going to the terminal is intermingled with the debugger text. Notice also when we called print_line(@parole), we executed all the statements in the subroutine before we got another prompt.

(From now on, we won't reproduce the optimistic Debugged program terminated blurb printed by the debugger.)

Suppose we wanted to step through the code inside subroutines like print_line. That's the reason for s (single step). Let's see how it's used, along with another handy stepping command, r (return):

% perl -d debug.pl
main::(debug.pl:4):     my @parole = qw(Salutations Hello Hey);
  DB<1> n
main::(debug.pl:6):     print_line(@parole);
  DB<1> s
main::print_line(debug.pl:13):     my @parole = @_;
  DB<1> n
main::print_line(debug.pl:14):     foreach (@parole)
main::print_line(debug.pl:15):        {
  DB<1>
main::print_line(debug.pl:16):        print "$_ Perl World
";
  DB<1> r
Salutations Perl World
Hello Perl World
Hey Perl World
void context return from main::print_line
main::(debug.pl:7):     print "Done
";
  DB<1> s
Done
Debugged program terminated.

The effect of r is to execute all the code up to the end of the current subroutine. (All these command letters are copied from existing popular Unix command line debuggers and are mnemonic—next, step, return). In addition, note that just hitting carriage return as a command repeats the last n or s command (and if there hasn't been one yet, it does nothing).

7.2.2. Examining Variables: p, x, V

Stepping through code is dandy, but how do we check our variables' values? Use either p expression to print the result of the expression (which is equivalent to printing to the filehandle $DB::OUT, so expression is put in list context) or x variable , which prints a variable in a pleasantly formatted form, following references. Once again, with the simple program:

% perl -wd debug.pl
main::(debug.pl:4):     my @parole = qw(Salutations Hello Hey);
  DB<1> p
						@parole
  DB<2> n
main::(debug.pl:6):     print_line(@parole);
  DB<2> p
						@parole
SalutationsHelloHey
  DB<3> x
						@parole
0  'Salutations'
1  'Hello'
2  'Hey'

In the first command, we instruct the debugger to print the value of @parole. However, the @parole assignment has yet to execute, so nothing comes out. Step past the assignment and then print the value with p; we see the current state of the array in a list format. Print the array value with x, and we see the individual elements formatted with array indices (a pretty print).

This might look familiar if you've been playing with the Data::Dumper module we referenced in Chapter 5. In fact, the output of x is intentionally very similar.

You can see all of the dynamic variables in a given package (default: main::) with the V command. This isn't as useful as it sounds because, unlike the x or p commands, it won't show you any lexical variables (which you declared with my). Yet you want to make as many of your variables as possible lexical ones (see Perl of Wisdom #8). Unfortunately there is no (easy) way to dump out all the lexical variables in a package, so you're reduced to printing the ones you know about.

A common problem is running off the end of the program and getting the Debugged program terminated message. At that point, all your variables have been destroyed. If you want to inspect the state of variables after the last line of your program has executed, add a dummy line (a 1 by itself will work) so that you can set a breakpoint on it.

Tip

when examining a hash, examine a reference to it instead. This lets the x command see the datatype you're inspecting instead of being handed the list that it evaluates to, and it can format it more appealingly:


  DB<1> %h = (Craig   => 'Stirling', 
						Sharron => 'Macready', Richard => 'Barrett'),
  DB<2> x
						%h
0  'Sharron'
1  'Macready'
2  'Craig'
3  'Stirling'
4  'Richard'
5  'Barrett'
  DB<3> x
						\%h
0  HASH(0x8330d5c)
   'Craig' => 'Stirling'
   'Richard' => 'Barrett'
   'Sharron' => 'Macready'

Examine references to hashes instead of the hashes themselves in the debugger to get well-formatted output.


7.2.3. Examining Source: l, -, w, .

Sometimes you want more of the context of your program than just the current line. The following commands show you parts of your source code:

l List successive windows of source code starting from the current line about to be executed.
l x + y List y + 1 lines of source starting from line x.
l x - y List source lines x through y.
- List successive windows of source code before the current line.
w List a window of lines around the current line.
w line List a window of lines around line.
. Reset pointer for window listings to current line.

Source lines that are breakable (i.e., can have a breakpoint inserted before them—see the following section) have a colon after the line number.

7.2.4. Playing in the Sandbox

Since the debugger is a full-fledged Perl environment, you can type in Perl code on the fly to examine its effects under the debugger;[1] some people do this as a way of testing code quickly without having to enter it in a script or type in everything perfectly before hitting end-of-file. (You just saw us do this at the end of section 7.2.2.)

[1] So, you might wonder, how would you enter Perl code which happened to look like a debugger command (because you'd defined a subroutine l, perhaps)? In versions of Perl prior to 5.6.0, if you enter leading white space before text, the debugger assumes it must be Perl code and not a debugger command. So be careful not to hit the space bar by accident before typing a debugger command. This was no longer true as of version 5.6.0.

Type perl -de 0 to enter this environment.[2] Let's use this as a sandbox for testing Perl constructs:

[2] There are many expressions other than 0 that would work equally well, of course. Perl just needs something innocuous to run.

% perl -de 0
DB<1> $_ = 'What_do*parens-in=split+do%again?';
  DB<2> @a = split /(W+)/;
  DB<3> x
						@a
0  'What_do'
1  '*'
2  'parens'
3  '-'
4  'in'
5  '='
6  'split'
7  '+'
8  'do'
9  '%'
10  'again'
11  '?'

You can even use this feature to change the values of variables in a program you are debugging, which can be a legitimate strategy for seeing how your program behaves under different circumstances. If the way your program constructs the value of some internal variable is complex and it would require numerous changes in the input to have it form the variable differently, then a good way of playing “What if?” is to stop the program at the right place in the debugger and change the value by hand. How would we stop it? Let's see …

7.2.5. Breakpointing: c, b, L

An important feature of a debugger is the ability to allow your program to continue executing until some condition is met. The most common such condition is the arrival of the debugger at a particular line in your source. You can tell the Perl debugger to run until a particular line number with the c (for continue) command:

main::(debug.pl:4):     my @parole = qw(Salutations Hello Hey);
  DB<1> c
						16
main::print_line(debug.pl:16):        print "$_ Perl World
";
  DB<2>

What the debugger actually did was set a one-time breakpoint at line 16 and then executed your code until it got there. If it had hit another breakpoint earlier, it would have stopped there first.

So what's a breakpoint? It's a marker set by you immediately before a line of code, invisible to anyone but the perl debugger, which causes it to halt when it gets there and return control to you with a debugger prompt. If you have a breakpoint set at a line of code that gets printed out with one of the source examination commands listed earlier, you'll see a b next to it. It's analogous to putting a horse pill in the trail of bread crumbs the mouse follows so the mouse gets indigestion and stops to take a breather (we really have to give up this metaphor soon).

You set a breakpoint with the b command; the most useful forms are b line or b subroutine to set a breakpoint either at a given line number or immediately upon entering a subroutine. To run until the next breakpoint, type c. To delete a breakpoint, use d line to delete the breakpoint at line number line or D to delete all breakpoints.

In certain situations you won't want to break the next time you hit a particular breakpoint, but only when some condition is true, like every hundredth time through a loop. You can add a third argument to b specifying a condition that must be true before the debugger will stop at the breakpoint. For example,

main::(debug.pl:4):   my @parole = qw(Salutations Hello Hey);
  DB<1> l
4==>    my @parole = qw(Salutations Hello Hey);
5
6:      print_line(@parole);
7:      print "Done
";
8
9       # Our subroutine which accepts an array, then prints
10      # the value of each element appended to "Perl World."
11      sub print_line
12         {
13:        my @parole = @_;
  DB<1> l
14:        foreach (@parole)
15            {
16:           print "$_ Perl World
";
17            }
18         }
  DB<1> b
						16 /Hey/
  DB<2> c
Salutations Perl World
Hello Perl World
main::print_line(debug.pl:16):        print "$_ Perl World
";
  DB<2> p
Hey

Notice that we've demonstrated several things here: the source listing command l, the conditional breakpoint with the criterion that $_ must match /Hey/, and that $_ is the default variable for the p command (because p just calls print).

The capability of the debugger to insert code that gets executed in the context of the program being debugged does not exist in compiled languages and is a significant example of the kind of thing that is possible in a language as well designed as Perl.

The command L lists all breakpoints.

7.2.6. Taking Action: a, A

An even more advanced use of the facility to execute arbitrary code in the debugger is the action capability. With the a command (syntax: a line code), you can specify code to be executed just before a line would be executed. (If a breakpoint is set for that line, the action executes first; then you get the debugger prompt.) The action can be arbitrarily complicated and, unlike this facility in debuggers for compiled languages, lets you reach into the program itself:

main::(debug.pl:4):     my @parole = qw(Salutations Hello Hey);
  DB<1> a
						16 s/Hey/Greetings/
  DB<2> c
Salutations Perl World
Hello Perl World
Greetings Perl World
Done
Debugged program terminated.

L also lists any actions you have created. Delete all the installed actions with the A command. This process is commonly used to insert tracing code on the fly. For example, suppose you have a program executing a loop containing way too much code to step through, but you want to monitor the state of certain variables each time it goes around the loop. You might want to confirm what's actually being ordered in a shopping cart application test (looking at just a fragment of an imaginary such application here):

  DB<1> l
						79-91
79:     while (my $item = shift @shopping_basket)
80         {
81:        if ($item->in_stock)
82            {
83:           $inventory->remove($item);
84:           $order->add($item);
85            }
86         else
87            {
88:           $order->back_order($item);
89:           $inventory->order($item);
90            }
91         }
  DB<2> a
						81 printf "Item: %25s, Cost: %5.2f
",
						$item->name, $item->cost
  DB<3> c
						92
Item:          Forbidden Planet, Cost: 24.50
Item:      Kentucky Fried Movie, Cost: 29.95
Item:                Eraserhead, Cost: 14.75
main::(cart.pl:92):    $customer->charge($order->total);
  DB<3>

7.2.7. Watch It: W

Suppose you want to break on a condition that is dictated not by a particular line of code but by a change in a particular variable. This is called a watchpoint, and in Perl you set it with the W command followed by the name of a variable.[3]

[3] In fact, Perl can monitor anything that evaluates to an lvalue, so you can watch just specific array or hash entries, for example.

Let's say you're reading a file of telephone numbers and to whom they belong into a hash, and you want to stop once you've read in the number 555-1212 to inspect the next input line before going on to check other things:

main::(foo:1):  my %phone;
  DB<1> l
						1-5
1==>    my %phone;
2:      while (<>) {
3:        my ($k, $v) = split;
4:        $phone{$k} = $v;
5       }
  DB<2> W
						$phone{'555-1212'}
  DB<3> c
Watchpoint 0:   $phone{'555-1212'} changed:
    old value:  undef
    new value:  'Information'
main::(foo:2):  while (<>) {
  DB<3> n
main::(foo:3):    my ($k, $v) = split;
  DB<3> p
555-1234 Weather

Delete all watchpoints with a blank W command.

7.2.8. Trace: t

The debugger's t command provides a trace mode for those instances that require a complete trace of program execution. Running the program with an active trace mode:

% perl -wd debug.pl
main::(debug.pl:4):     my @parole = qw(Salutations Hello Hey);
  DB<1> t
Trace = on
  DB<1> n
main::(debug.pl:6):     print_line(@parole);
  DB<1> n
main::print_line(debug.pl:13):     my @parole = @_;
main::print_line(debug.pl:14):     foreach (@parole)
main::print_line(debug.pl:15):        {
main::print_line(debug.pl:16):        print "$_ Perl World
";
Salutations Perl World
main::print_line(debug.pl:14):     foreach (@parole)
main::print_line(debug.pl:15):        {
main::print_line(debug.pl:16):        print "$_ Perl World
";
Hello Perl World
main::print_line(debug.pl:14):     foreach (@parole)
main::print_line(debug.pl:15):        {
main::print_line(debug.pl:16):        print "$_ Perl World
";
Hey Perl World
main::print_line(debug.pl:14):     foreach (@parole)
main::print_line(debug.pl:15):        {
main::(debug.pl:7):     print "Done
";

Notice that trace mode causes the debugger to output the call tree when execution enters the print_line subroutine.

7.2.9. Programmatic Interaction with the Debugger

You can put code in your program to force a call to the debugger at a particular point. For instance, suppose you're processing a long input file line by line and you want to start tracing when it reaches a particular line. You could set a conditional breakpoint, but you could also extend the semantics of your input by creating “enable debugger” lines. Consider the following code:

while (<INPUT>)
   {
   $DB::trace = 1, next if /debug/;
   $DB::trace = 0, next if /nodebug/;
   # more code
   }

When run under the debugger, this enables tracing when the loop encounters an input line containing “debug” and ceases tracing upon reading one containing “nodebug”. You can even force the debugger to breakpoint by setting the variable $DB::single to 1, which also happens to provide a way you can debug code in BEGIN blocks (which otherwise are executed before control is given to the debugger).

7.2.10. Optimization

Although the Perl debugger displays lines of code as it runs, it's important to note that these are not what actually executes. Perl internally executes its compiled opcode tree, which doesn't always have a contiguous mapping to the lines of code you typed, due to the processes of compilation and optimization. If you have used interactive debuggers on C code in the past, you may be familiar with this process.

When debugging C programs on VAX/VMS, it was common for me to want to examine an important variable only to get the message that the variable was not in memory and had been “optimized away.”


Perl has an optimizer to do as good a job as it can—in the short amount of time people will wait for compilation—of taking shortcuts in the code you've given it. For instance, in a process called constant folding, it does things like build a single string in places where you concatenate various constant strings together so that the concatenation operator need not be called at run-time.

The optimization process also means that perl may execute opcodes in an order different from the order of statements in your program, and therefore when the debugger displays the current statement, you may see it jump around oddly. As recently as version 5.004_04 of perl, this could be observed in a program like the following:

1   my @a = qw(one two three);
2   while ($_ = pop @a)
3      {
4      print "$_
";
5      }
6   1;

See what happens when we step through this, again using perl 5.004_04 or earlier:

main::(while.pl:1):   my @a = qw(one two three);
  DB<1> n
main::(while.pl:6):   1;
  DB<1>
main::(while.pl:4):     print "$_
";
  DB<1>
three
main::(while.pl:2):   while ($_ = pop @a)
  DB<1>
main::(while.pl:4):     print "$_
";
  DB<1>
two

In fact, if we set a breakpoint for line 6 and ran to it, we'd get there before the loop executed at all. So it's important to realize that under some circumstances, what the debugger tells you about where you are can be confusing. If this inconveniences you, upgrade.

7.2.11. Another “Gotcha”

If you set a lexical variable as the last statement of a block, there is no way to see what it was set to if the block exits to a scope that doesn't include the lexical. Why would code do that? In a word, closures. For example,

{                  # Start a closure-enclosing block
my $spam_type;     # This lexical will outlive its block
sub type_spam
   {
   # ...
   $spam_type = $spam_types[complex_func()];
   }
}

In this case, either type_spam or some other subroutine in the closure block would have a good reason for seeing the last value of $spam_type. But if you're stepping through in the debugger, you won't see the value it gets set to on the last line because, after the statement executes, the debugger pops out to a scope where $spam_type is not in scope (unless type_spam() was called from within the enclosing block). Unfortunately, in this case, if the result of the function is not used by the caller, you're out of luck.

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

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