|
Softpanorama
(slightly skeptical)
Open Source Software Educational Society |
May the
source be with you,
but remember the KISS principle ;-)
|
Perl Debugging
Debugging of Perl scripts is a challenging problem due to the complexity and
power of the language. The quality of the debugger is good, but still it is a free
open source implementation and as such it is limited to command line. The debugger's
command-line history mechanism provides command-line editing like many shells but
you need correct build of Perl interpreter to enjoy this (you can also execute previous
lines with using exclamation point syntax).
Also Perl is very high level language and to understand to what part of the program
you should attribute a particular error message is rather hard in the current Perl
implementation. Here recommendations of the famous book
How to Solve It are
directly applicable and I highly recommend it for any Perl student.
Minimal set of commands that you probably need to know in order to be able to use
Perl debugger includes the following 12 commands:
- s - single step including stepping into subs
- n - steps without stepping into subroutines.
- c - continue processing until a break of some sort
- r - continue running until you do a return from the current sub. Essentially
you tell the Perl debugger to finish executing the subroutine and return
to the statement after the subroutine call.
- v - shows a "window" of code around the current line. More convenient then
l in many cases.
- b - sets a break point (uses line number *or* sub name)
- X - displays value of variables (*including* arrays & hashes), for example:
DB<6> X regarray
- w - Add a global watch-expression. We hope you know what one of these is,
because they're supposed to be obvious.
- T - displays a stack back trace
- L - list all the breaks
- D - delete all the breaks
- R - restart
csh-like history mechanism
The debugger prompt is something like:
DB<8>
where the number shows how many commands you've executed. A
csh-like history mechanism allows you to access previous
commands by number. For example, !17 would repeat command
number 17. The number of angle brackets indicates the depth of the debugger. For
example, you get more than one set of brackets if you're already at a breakpoint
and then print out the result of a function call that itself also has a breakpoint.
Some additional tips
-
You can display the lines immediately preceding the last displayed
line by entering the - command.
- V [pkg [vars]]
- Display all (or some) variables in package (defaulting to
main)
using a data pretty-printer
- ! number
- Redo a previous command (defaults to the previous command).
- l min+incr
- List
incr+1 lines starting at min.
Switching to step-by-step execution from within the program
You can switch to step-by-step execution using Perl debugger from within your
Perl program itself. to transfer control back to the debugger use the following
statement, which is harmless if the debugger is not running:
$DB::single = 1;
If you set $DB::single to 2, it's equivalent to the
n command, whereas a value of 1 emulates the
s command. The $DB::trace variable
should be set to 1 to simulate the t command.
A Watchful Eye
You can set a watch-expression (an expression which
triggers a halt if its value changes) with the "w" command, as in the following
example:
DB<1> w $count
DB<<3>> L
Watch-expressions:
$count
Now that the watch-expression has been set, you can see how it works by writing
some code to alter the value of the $count variable. The debugger will display a
message every time the variable's value changes.
Notes:
- Those pages are written by people for whom English is not a
native language. Some amount of grammar and spelling errors
should be expected.
- This is a Spartan WHYFF (We Help You For Free) site. It
cannot replace the best teachers and
the
best books.
- The site contain some obsolete pages as it develops like a
living tree... Some links on older pages
are broken. Please
try to use Google, Open directory, etc. to find a replacement link
(see
HOWTO search the WEB for details).
We would appreciate if you can
mail us a correct link.
|
|
On this page, I will post aides and tools that Perl provides which
allow you to more efficently debug your Perl code. I will post updates
as we cover material necessary for understanding the tools mentioned.
CGI::Dump
Dump is one of the functions exported in CGI.pm's
:standard set. It's functionality is similar to that of
Data::Dumper. Rather than pretty-printing a complex
data structure, however, this module pretty-prints all of the
parameters passed to your CGI script. That is to say that when
called, it generates an HTML list of each parameter's name and
value, so that you can see exactly what parameters were passed
to your script. Don't forget that you must print the return
value of this function - it doesn't do any printing on its own.use CGI qw/:standard/;
print Dump;
Benchmark
- As you know by now, one of Perl's mottos is "There's More
Than One Way To Do It" (TMTOWTDI ©). This is usually a Good
Thing, but can occasionally lead to confusion. One of the most
common forms of confusion that Perl's verstaility causes is
wondering which of multiple ways one should use to get the job
done most quickly.
Analyzing two or more chunks of code to see how they compare
time-wise is known as "Benchmarking". Perl provides a standard
module that will Benchmark your code for you. It is named, unsurprisingly,
Benchmark. Benchmark provides several
helpful subroutines, but the most common is called cmpthese().
This subroutine takes two arguments: The number of iterations
to run each method, and a hashref containing the code blocks
(subroutines) you want to compare, keyed by a label for each
block. It will run each subroutine the number of times specified,
and then print out statistics telling you how they compare.
For example,
my solution to
ICA5
contained three different ways of creating a two dimensional
array. Which one of these ways is "best"? Let's have Benchmark
tell us:
#!/usr/bin/perl
use strict;
use warnings;
use Benchmark 'cmpthese';
sub explicit {
my @two_d = ([ ('x') x 10 ],
[ ('x') x 10 ],
[ ('x') x 10 ],
[ ('x') x 10 ],
[ ('x') x 10 ]);
}
sub new_per_loop {
my @two_d;
for (0..4){
my @inner = ('x') x 10;
push @two_d, \@inner;
}
}
sub anon_ref_per_loop {
my @two_d;
for (0..4){
push @two_d, [ ('x') x 10 ];
}
}
sub nested {
my @two_d;
for my $i (0..4){
for my $j (0..9){
$two_d[$i][$j] = 'x';
}
}
}
cmpthese (10_000, {
'Explicit' => \&explicit,
'New Array Per Loop' => \&new_per_loop,
'Anon. Ref Per Loop' => \&anon_ref_per_loop,
'Nested Loops' => \&nested,
}
);
The above code will print out the following statistics (numbers
may be slightly off, of course):
Benchmark: timing 10000 iterations of Anon. Ref Per Loop, Explicit, Nested Loops, New Array Per Loop...
Anon. Ref Per Loop: 2 wallclock secs ( 1.53 usr + 0.00 sys = 1.53 CPU) @ 6535.95/s (n=10000)
Explicit: 1 wallclock secs ( 1.24 usr + 0.00 sys = 1.24 CPU) @ 8064.52/s (n=10000)
Nested Loops: 4 wallclock secs ( 4.01 usr + 0.00 sys = 4.01 CPU) @ 2493.77/s (n=10000)
New Array Per Loop: 2 wallclock secs ( 1.76 usr + 0.00 sys = 1.76 CPU) @ 5681.82/s (n=10000)
Rate Nested Loops New Array Per Loop Anon. Ref Per Loop Explicit
Nested Loops 2494/s -- -56% -62% -69%
New Array Per Loop 5682/s 128% -- -13% -30%
Anon. Ref Per Loop 6536/s 162% 15% -- -19%
Explicit 8065/s 223% 42% 23% --
The benchmark first tells us how many iterations of which
subroutines it's running. It then tells us how long each method
took to run the given number of iterations. Finally, it prints
out the statistics table, sorted from slowest to fastest. The
Rate column tells us how many iterations each subroutine
was able to perform per second. The remaining colums tells us
how fast each method was in comparison to each of the other
methods. (For example, 'Explicit' was 223% faster than 'Nested
Loops', while 'New Array Per Loop' is 13% slower than 'Anon.
Ref Per Loop'). From the above, we can see that 'Explicit' is
by far the fastest of the four methods. It is, however, only
23% faster than 'Ref Per Loop', which requires far less typing
and is much more easily maintainable (if your boss suddenly
tells you he'd rather have the two-d array be 20x17, and each
cell init'ed to 'X' rather than 'x', which of the two would
you rather had been used?).
You can, of course, read more about this module, and see
its other options, by reading: perldoc Benchmark
- Command-line options
- Perl provides several command-line options which make it
possible to write very quick and very useful "one-liners". For
more information on all the options available, refer to
perldoc perlrun
-e
- This option takes a string and evaluates the Perl code
within. This is the primary means of executing a one-liner
perl -e'print qq{Hello World\n};'
(In windows, you may have to use double-quotes rather than
single. Either way, it's probably better to use q// and
qq// within your one liner, rather than remembering to escape
the quotes).
-l
- This option has two distinct effects that work in conjunction.
First, it sets $\ (the output record terminator) to the
current value of $/ (the input record separator). In effect,
this means that every print statement will automatically
have a newline appended. Secondly, it auto-chomps any input
read via the <> operator, saving you the typing necessary
to do it.
perl -le 'while (<>){ $_ .= q{testing}; print; }'
The above would automatically chomp $_, and then add the
newline back on at the print statement, so that "testing"
appears on the same line as the entered string.
-w
- This is the standard way to enable warnings in your
one liners. This saves you from having to type
use
warnings;
-M
- This option auto-
uses a given module.
perl -MData::Dumper -le'my @foo=(1..10); print Dumper(\@foo);'
-n
- This disturbingly powerful option wraps your entire
one-liner in a
while (<>) { ... } loop. That
is, your one-liner will be executed once for each line of
each file specified on the command line, each time setting
$_ to the current line and $. to current line number.
perl -ne 'print if /^\d/' foo.txt beta.txt
The above one-line of code would loop through foo.txt and
beta.txt, printing out all the lines that start with a digit.
($_ is assigned via the implicit while (<>)
loop, and both print and m// operate on $_ if an explict
argument isn't given).
-p
- This is essentially the same thing as
-n,
except that it places a continue { print; }
block after the while (<>) { ... } loop in
which your code is wrapped. This is useful for reading through
a list of files, making some sort of modification, and printing
the results.
perl -pe 's/Paul/John/' email.txt
Open the file email.txt, loop through each line, replacing
any instance of "Paul" with "John", and print every line
(modified or not) to STDOUT
-i
- This one sometimes astounds people that such a thing
is possible with so little typing. -i is used in conjunction
with either -n or -p. It causes the files specified on the
command line to be edited "in-place", meaning that while
you're looping through the lines of the files, all print
statements are directed back to the original files. (That
goes for both explicit
prints, as well as the
print in the continue block added by -p.)
If you give -i a string, this string will be used to create
a back-up copy of the original file. Like so:
perl -pi.bkp -e's/Paul/John/' email.txt msg.txt
The above opens email.txt, replaces each line's instance
of "Paul" with "John", and prints the results back to email.txt.
The original email.txt is saved as email.txt.bkp. The same
is then done for msg.txt
Remember that any of the command-line options listed here
can also be given at the end of the shebang in non-oneliners.
(But please do not start using -w in your real programs -
use warnings; is still preferred because of its
lexical scope and configurability).
Data::Dumper
- The standard Data::Dumper module is very useful for examining
exactly what is contained in your data structure (be it hash,
array, or object (when we come to them) ). When you
use
this module, it exports one function, named Dumper.
This function takes a reference to a data structure and returns
a nicely formatted description of what that structure contains.
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my @foo = (5..10);
#add one element to the end of the array
#do you see the error?
$foo[@foo+1] = 'last';
print Dumper(\@foo);
When run, this program shows you exactly what is inside @foo:
$VAR1 = [
5,
6,
7,
8,
9,
10,
undef,
'last'
];
(I know we haven't covered references yet. For now, just
accept my assertion that you create a reference by prepending
the variable name with a backslash...)
__DATA__ & <DATA>
- Perl uses the __DATA__ marker as a pseudo-datafile. You
can use this marker to write quick tests which would involve
finding a file name, opening that file, and reading from that
file. If you just want to test a piece of code that requires
a file to be read (but don't want to test the actual file opening
and reading), place the data that would be in the input file
under the __DATA__ marker. You can then read from this pseudo-file
using <DATA>, without bothering to open an actual file:
#!/usr/bin/env perl
use strict;
use warnings;
while (my $line = <DATA>) {
chomp $line;
print "Size of line $.: ", length $line, "\n";
}
__DATA__
hello world
42
abcde
The above program would print:
Size of line 1: 11
Size of line 2: 2
Size of line 3: 5
$.
- The
$. variable keeps track of the line numbers
of the file currently being processed via a while (<$fh>)
{ ... } loop. More explicitly, it is the number of the
last line read of the last file read.
__FILE__ & __LINE__
- These are two special markers that return, respectively,
the name of the file Perl is currently executing, and the Line
number where it resides. These can be used in your own debugging
statements, to remind yourself where your outputs were in the
source code:
print "On line " . __LINE__ . " of file " . __FILE__ . ", \$foo = $foo\n";
Note that neither of these markers are variables, so they
cannot be interpolated in a double-quoted string
warn() & die()
- These are the most basic of all debugging techniques.
warn() takes a list of strings, and prints them
to STDERR. If the last element of the list does not end in a
newline, warn() will also print the current filename
and line number on which the warning occurred. Execution then
proceeds as normal.die() is identical to
warn(), with one major exception - the program
exits after printing the list of strings.
All debugging statements should make use of either
warn() or die() rather than print().
This will insure you see your debugging output even if STDOUT
has been redirected, and will give you the helpful clues of
exactly where in your code the warning occurred.
"Complexity is the enemy, and our aim is to kill it."
-Jan Baan
One of Perl's greatest strengths is its expressiveness and extreme conciseness.
Complexity is the bane of software development: when a program grows beyond
a certain size, it becomes much harder to test, maintain, read, or extend. Unfortunately,
today's problems mean this is true for every program we need. Anything you can
do to minimize the complexity of your program will pay handsome dividends.
The complexity of a program is a function of several factors:
- The number of distinct lexical tokens
- The number of characters
- The number of branches in which control can pass to a different point
- The number of distinct program objects in scope at any time
Whenever a language allows you to change some code to reduce any of these
factors, you reduce complexity.
When debugging a Perl program remotely, the program is executed on the remote
system and the debug output is sent to Komodo. Komodo controls the debugging
session (e.g. stepping and breakpoints) once the session starts on the remote
system.
Perl remote debugging works on any system that can run the version of
perl5db.pl distributed with Komodo.
ActivePerl and most other distributions of Perl (version 5.6 or greater)
will work.
Note: If you have the ActiveState Perl Development Kit (PDK)
installed, follow the instructions
for PDK users to disable the
PDK debugger before continuing.
To debug Perl programs remotely:
Step One: Configure the Remote Machine
- Log in to the remote machine.
- Copy Komodo's perl debugger and its associated libraries to the remote
machine by copying the entire dbgp/perllib sub-directory of the
Komodo installation to the new machine, or download a package from the
Komodo Remote Debugging page.
Note: Do not copy perl5db.pl to the Perl "lib"
directory on the remote machine, as this will overwrite the standard
perl5db.pl file.
- On the remote machine, set the
PERL5LIB environment variable
to the location of the new perl5db.pl and its libraries. For example,
if the remote machine is running Windows and perllib directory
was copied to C:\misc\perllib, set the variable as follows:
set PERL5LIB=C:\misc\perllib
For example, if the remote machine is running Linux and perllib
was copied to the /home/me/perl/komodo_perl_debugging directory,
set the variable as follows:
export PERL5LIB=/home/me/perl/komodo_perl_debugging/perllib
- On the remote machine, set the
PERLDB_OPTS and DBGP_IDEKEY
variables. This tells the Perl interpreter on the remote machine where to
connect to Komodo or the
DBGP Proxy and how to identify itself.
PERLDB_OPTS=RemotePort=<hostname>:<port>
DBGP_IDEKEY=<ide_key>
- The port number must match the port number specified in
Edit|Preferences|Debugger. Click Debug|Listener Status
to check the current port.
- Replace
<hostname> with the name or IP address of the
machine running Komodo.
- If DBGP_IDEKEY is unset, the USER or USERNAME environment variable
is used as the IDE Key.
- The variable definitions must be on one line.
For example:
Windows 2000, NT, XP
set PERLDB_OPTS=RemotePort=127.0.0.1:9000
set DBGP_IDEKEY=jdoe
Windows Me
Use the MSCONFIG utility (Start|Run|MSCONFIG). Select
the Environment tab, and create a new variable with the
Variable Name of PERLDB_OPTS, and the Variable Value of
RemotePort=127.0.0.1:9000.
Unix Systems
export PERLDB_OPTS="RemotePort=127.0.0.1:9000"
export DBGP_IDEKEY="jdoe"
Step Two: Listen for Remote Debugger
In Komodo, on the Debug menu, click Listen for Remote
Debugger.
Step Three: Start the Perl Program on the Remote Machine
Start the debugging process using the "-d" flag:
perl -d program_name.pl
A Perl Debug tab is displayed in Komodo.
Step Four: Debug the Perl Program using Komodo
Use 'F11' to Step In, or 'F5' (Go) to run
to the first breakpoint. See
Komodo Debugger Functions for full instructions on using Komodo's debugging
functionality.
If you have installed the ActiveState Perl Development Kit (PDK) on the remote
machine, the system may be configured to use the PDK debugger when a Perl debug
session (perl -d) is launched. To use Komodo's debugger,
disable the PDK debugger
on the remote machine first. If necessary, you can re-enable the PDK debugger
on the remote machine later.
To disable the PDK debugger on the remote machine, perform one of the following
three procedures:
Option 1: (Windows and Unix)
At the command shell, enter the following command (depending on your operating
system):
Windows
set PERL5DB=BEGIN { require 'perl5db.pl'; }
Unix
export PERL5DB="BEGIN { require 'perl5db.pl'; }"
To re-enable the PDK debugger, set the PERL5DB variable to an
empty string.
Option 2: (Windows)
- Right-click the My Computer icon and select
Properties.
- Click the Advanced tab.
- Click Environment Variables.
- In the System variables section, click New.
- Set the Variable Name field to
PERL5DB.
- Set the Variable Value field to
BEGIN { require
'perl5db.pl'}.
- Click OK three times to exit.
These changes take effect only in new DOS windows. To re-enable the PDK debugger,
delete the PERL5DB variable.
Option 3: (Windows)
Change the registry setting for HKEY_LOCAL_MACHINE\SOFTWARE\Perl.
Rename the variable PERL5DB to xPERL5DB.
Warning: This registry setting is semi-permanent and persists
through machine restarts.
This change takes effect only in new DOS windows. To re-enable the PDK debugger,
rename the xPERL5DB registry variable back to PERL5DB.
Debugging CGI programs on live production servers can seriously impair performance.
We recommend using a test server for CGI debugging. Instructions for configuring
Microsoft IIS and Apache (Unix) servers are shown below; for other web servers,
use these examples and the web server software documentation as a guide for
modifying the server environment.
The settings and paths listed are examples only. Substitute these with the
specific paths, hostnames and port numbers of your server as necessary
Richard: Being able to interactively work with your code is
a very powerful mechanism, really. Because, you can just experiment on the fly
with all sort of constructs. You know, simple constructs, or things like when
you’re unsure about how somethings evaluates in a list context, or scalar context,
or whether it is a list or scalar context. So you can just experiment and find
out.Josh: Okay, so on the subject of the book, you divided
into three primary sections. The first being a general overview of the debugger,
the second takes you through usage scenarios and customizing the debugger, and
the final is a reference section. So, I was just wondering why you chose to
layout the book in this way.
Richard: It seemed a fairly natural spread for me, I think,
I was hoping that the book would be able to appeal to both people that hadn’t
really used the debugger before, hadn’t really had much experience in, and therefore
the first several chapters are completely oriented toward the beginner. If you
are a more experienced Perl user and, quite possibly, already a debugger user,
but you weren’t sure of all the details, then that is where middle of the book
was going to come more into effect, to get you up to speed there, really. And
of course you need to have a reference, and every books have references, goodness
me. The debugger command, well, there’s quite lot of them really, especially
the options. We needed to have some form of definitive reference of all the
various command possibilities and the various syntaxes. Which, while they are
multiplely intuitive, some of them can be a bit obscure, especially when you
get down to the options. You need to have the absolutely right name in
some places. So, you sort of need to know where to look and that’s where the
online help, again, comes into hand there. In the debugger itself, there is
a small screen, a help screen, that you can use. That you can bring up just
with a simple “h” command at the prompt. And that gives you almost all the information
you need to know at that point. To get more information, you would use “h” and
then the command name itself.
Josh: You covered debugging modules and debugging objects
separately in the book, and it seems like those two areas kind of overlap some
in Perl because of the very loose object definitions. So, what is the difference
when you’re debugging those?
Richard: Essentially, a module is just a library. We’ve
come to associate object oriented programming with little modules, but the two
aren’t strictly bound together in that way. I think treating a number of libraries
as a debuggable entity is valid on its own, and there are things the debugger
can help you with there. Equally when you use object oriented code it’s entirely
irrelevant whether you are actually using modules or you just got a whole bundle
of code in a single file. You can still set up inheritance and member variables,
classes and so on. And the debugger offers you certain commands to help you
with your object oriented programming as well, like the “i” command, which just
tells you a little bit about the inheritance of the object you’re looking at.
Or rather the executable statement you’ve got in front of you, but you would
normally use it on an object.
Josh: Whenever I debug CGI scripts, typically I just print
out to the screen and run them through the web browser, or something. So, you
can actually debug [CGI scripts using, or] Perl CGI scripts, using the Perl
debugger, but it does seem like it been a little bit tricky, if not impossible,
honestly to me. So how would you go about doing that?
Richard: The debugger is a very useful tool when you are
dealing with CGI scripts, just like any other Perl program. There’s no particular
magic there. You would just call your CGI script using “-d,” where appropriate,
at the end of the Perl path, and the debugger kicks in, takes control of the
situation and offers you a prompt. The point where it gets tricky is if you
are using Apache mod_perl, for example, where you are combining with a web server
and you’ve got the debugger essentially already sitting inside, well Perl, already
sitting inside C space, inside the web server. Even there, thanks to Doug MacEachern’s
work with the mod_perl code, Pierce provided a very simple interface which you
can effectively switch on, and you can step inside mod_perl, as well. All you
have to do is you have to get the server running in single user mode, and there’s
a small configuration file change you have to make in the Apache server config
file. And then you can step through mod_perl programs, j
ust like you can step through any other Perl programs. So, it’s very, very sweet,
and very, very, simple. It’s not complicated at all. The only complicated about
it is trying it the first time. And once you have done that you think “Oh, it’s
so easy, why didn’t I do this before.” And that’s the case over and over again
I found that with the debugger. That people always where saying “isn’t it complicated”
or “isn’t it strange,” you know, and “how can you possibly do this and do that.”
And it basically just makes your live much, much easier and much more straightforward.
It’s a very powerful tool that people seem to miss because it’s essentially
unfamiliar.
I was at one job here in Germany where there were some people on a programming
team here that spent, I think probably months of man-hours, well maybe weeks.
They were trying to get a tracing mechanism in their moderately complicated
Perl code, and they were using caller all the time to essentially step in all
the time and print out stack traces during the runtime execution of their program.
And all of this, of course, comes in standard with Perl. You can get stack traces,
printing out all the arguments going into subroutines, what the return values
are, just with a switch, with the “-t,” inside the Perl debugger. It was an
awful lot of work, which would have been completely saved had they bothered
to read the documentation, you know. Which isn’t to say that none of us ever
don’t like to reinvent every now and then, but that was an lot of work for absolutely
nothing. It wasn’t even as good, of course, as the debugger solution.
Josh: Whenever you say “reinventing the wheel,” I am not
sure if this is a reinvention or an improvement, or not, but there are some
debugger replacements, or at least supplements, that are available, and one
of these is “Devel-ebug.” And how does this module actually compare to the Perl
debugger?
Richard: I’ve not yet time to recall on this to dive into
that a great deal. But I believe that’s Leon’s newer module, or at least I know
he has been working on one. And I think any approach to making debugging Perl
programs is a good thing. And if there’s several debuggers out there to choose
from that can only be a good thing too. There have been various attempts in
the past, I think, to replace the debugger, because it is a little bit crusty.
It essentially had a bit of a rewrite when Perl 5 came in, and since it has
only been extended and extended and extended, and as you can imagine that led
to very spaghetti like bundle of code to maintain, which can lead to its own
problems. Essentially, any further development tools that we can get our hands
on can only be a good thing.
Josh: Is there any GUI to sit on top of the debugger to
maybe make it easier for beginning users to use that, other than the command
line where you have to remember the commands?
Richard: Yeah, the classic one is probably coming from the
UNIX background, is the ptkdb, the Perl/Tk Debugger interface, which works very
well with all the Linux platforms. You just have Tk installed and you can run
that. And that’s a very mouse oriented debugger interface, and quite friendly.
The other one, quite well known, is the Komodo debugger and Perl IDE from ActiveState,
which, because I work mostly on UNIX and Linux I don’t have actually much experience
with. But from what I have seen of it it’s very sweet. Any of the GUI interfaces
that are available. Excellent particularly for CGI debugging. I mean, this is
the case where with ptkdb, for example, you can set a display variable where
you are using it. A neat little trick is the Perl code that’s running by a web
server will send a window to wherever set the display variable to, and you can
debug from there. So you can debug from a completely remote machine.
Josh: In the book, you also show how to debug threaded applications
and POE based applications. So, what kind of special support does the debugger
have to make debugging these types of applications easier?
Richard: Where POE is concerned, really, there is all the
extra support needed, because it’s simply a single running application that’s
pretending to be threaded, or forked. So it works with that quite happily. Where
you are using actual fork calls, and some program do, the debugger does support
that. The only problem at hand is setting up a new console. And than you have
to a little bit of higgery-pokery to get the correct console running, the correct
xterm. The support for forked programs is moderately limited, but it is actually
there. What we actually need is for some fork experts to come along, and to
write all the various chunks of code to support the debugger on those platforms.
The main problem at the moment really is that the Perl debugger is running inside
what it thinks is an xterm, and it doesn’t really know how to create more, because
you need a new input and output for the debugger itself, not the program as
such. There is some minimal support there for forks.
Where threads are concerned, that was a problem until quite recently, because
all the variables inside the Perl debugger were being shared among all the threaded
programs, and no one really knew which variable was in which thread. And that
led to some quite interesting results. But that was fixed quite recently, 5.8.5
I think. And you can now use the Perl debugger with threads as well.
Josh: One feature of the debugger that looks really neat
was the ability to debug regular expressions. But before we dive into that,
a non-debugger related tip for working with regexes was to avoid contracting
the “Leaning Toothpick Syndrome,” and what is that, and how do I avoid it?
Richard: Right, Leaning Toothpick Syndrome is when you normally
have a regexp, you normally start with a forward slash and then you end with
a forward slash. And so, if you got any forward slash in your regex, like for
example in a typical path name on a UNIX like system, it can get very confusing
very quickly, which is the start slash and which is the end slash, and which
is the slash in the middle, and has it been backslashed yet to escape it. If
you just use different delimiters at the end, maybe a comment mark or open and
close brackets or braces, that can make the regex a lot easier to read. And
then you don’t have to backslash your forward slashes any more, and you no longer
get the Leaning Toothpick Syndrome. So when you just have so many forward slashes
and backslashes to try to keep them in sync, it get very bitterly confusing.
Josh: Regular expression debugging seem to produce a lot
of data, but then again regexes are almost miniature programs within your script.
So, what types of information and controls does the debugger provide for regular
expressions?
Richard: The debugger itself doesn’t actually provide terribly
much regex support itself. It uses what support there is in Perl already. Perl
has its own regex engine, that will give you a lot of information if you ask
it correctly. It seemed moderately relevant to include a chapter on this in
the book, because there was so little information about it anywhere else, and
people do have problems with their regexes. So it’s nice to see one thing exploded.
Essentially, you’ve got a couple of choices with regexes. You can use the
re pragma, use regular expression module then
or pragma (use re), which will show you what Perl
is going to be doing, or show you what the regex engine is going to be doing
in terms of compiling and optimizing your regex. Or you can use the debugging
flags. You have to use -Dr for that. But for that
you need to have a special Perl build with debugging support, which, agai
n, is not very complicated and shared to do that in the book. But its one more
step you have to go through. But its quite nice to see your rexex actually exploded
out, and walk through step by step.
Josh: When do I just use the print
statement and when do I pull out the debugger? Is there something to look for,
some kind of signals, to know that its time to actually invoke the debugger
and get beyond the print?
Richard: If you don’t turn around and just use it first
off because its a convenient and a close friend, then yeah, you probably gonna
be, maybe, at a stage where you are struggling with the
print command. When you are saying something like
“that just can’t be right,” and you are going round and round in circles, looking
at data that is clearly not what you’re expecting. And that really is a time
when you should stop and say “Okay, I’m gonna step back and I’m just gonna force
myself to go in there, to step inside the code, and to step through it and find
out what the values of these variables actually are at the time, rather than
assuming there are something of.” And the debugger can help you do that. The
nice thing about it is that it can do it without you actually altering your
code in any way. One of the problems is, very often people will actually try
to find a bug and help fix it. Finding it has involved, perhaps, change in the
code in numerous place
s to put printstatements in, and then fixing it
involves changing the code again and taking all that
print statements out correctly, without affecting aversely, in any way,
the running of the program. Sounds very straightforward, but I have seen a number
of bugs appear just by people finding and fixing bugs actually introduce their
own. And the debugger can help there, with just keeping you slightly arms-length.
You are not changing the code in any way. You have complete control of what
you are doing.
Josh: That was all the questions I had. Is there anything
in the book that you think is really important that we should let people know
about?
Richard: I think the last comment is a critical one: Don’t
change the source code. Don’t change the code, just get in there and run your
program and have a look around as it’s running. It is a very powerful mechanism,
and it’s a very convenient mechanism. And it’s a lot of fun as well.
Josh: Thank you. Well, the book is “Pro Perl Debugging.”
It’s published by Apress, and is written by Richard Foley, who we’ve been talking
to here on Perlcast. Richard, thank you very much for the interview.
This title was published in hardcover in March 2005 by Apress, a relatively
new member of the technical publishing world. The publisher has a
Web page for the book that includes links to all of the source code in a
Zip file, the table of contents in PDF format, and a form for submitting errata.
The book comprises 269 pages, the majority of which are organized into 16 chapters:
Introduction (not to be confused with the true Introduction immediately
preceding it),
- Inspecting Variables and Getting Help,
- Controlling Program Execution,
- Debugging a Simple Command Line Program,
- Tracing Execution, Debugging Modules,
- Debugging Object-Oriented Perl,
- Using the Debugger As a Shell,
- Debugging a CGI Program,
- Perl Threads and Forked Processes,
- Debugging Regular Expressions,
- Debugger Customization,
- Optimization and Performance Hints and Tips,
- Command Line and GUI Debuggers,
- Comprehensive Command Reference,
- Book References and URLs.
Open Perl IDE - User Manual
This section explains how to use Open Perl IDE for debugging.
Important: Open Perl IDE is not able to debug any scripts, if it does not
know a path to "perl.exe". If the PATH environment variable contains a valid
location, then "perl.exe" will be detected automatically. Otherwise it is necessary
to enter a valid location into the "Preferences | General | Directories
| Path to perl.exe" field.
There are two methods to debug a script:
- Set one or more breakpoints (as explained in section 5.1 Breakpoints)
and run the script, which is executed until a breakpoint is reached.
- Choose Step Over from the Run Menu. After the script and all required
modules are loaded and initalized, the execution stops on the first line
of non-initialization code.
After execution is stopped, it is possible to analyse the actual state of the
script by
- Viewing Console Output, see section 4.2 Compile and Run a script
- Evaluating some variables, see section 5.2 Variable Evaluation
- Viewing the list of loaded modules, see section 5.3 Other debug windows
- Viewing the callstack, see section 5.3 Other debug windows
Furthermore, it is possible to set/delete breakpoints (see section 5.1 Breakpoints)
or to continue/abort the execution of the script. The following table shows
the different navigation possibilities:
Table: Debug Navigation
| Name |
Shortcut |
Description |
| Run |
F9 |
Start/Continue script execution until next breakpoint is reached. |
| Step Over |
F8 |
Execute the current script line, not tracing into subroutines. |
| Step Into |
F7 |
Execute the next command in the current script line, tracing into
subroutines. |
| Abort |
CTRL-F2 |
Request termination of debug session. |
| Force Termination |
CTRL-ALT-F12 |
Immediately terminate debug session.
You should only use "Force Termination" if you see no other way to
stop script execution. Dont't expect Open Perl IDE to work correctly
after using forced termination ! |
If script execution has finished, then Open Perl IDE automatically switches
back from debug mode to edit mode.
Dr. Dobb's Web Site Code Coverage Analysis in Perl by Brian D Foy
Is all of your Perl code
actually executing? Are some statements invoked as expected? The Devel::Coverage
module can give you the answer, but you've got to ask the right questions.
Perl comes with
a built-in debugging mechanism that allows you to create your own debugger
or use one created by someone else. Perl coders can also use the
Devel::Coverage debugging module to determine just how much of a program
actually executes. This testing process is called
coverage analysis.
The Devel::Coverage module is available on the
Comprehensive Perl Archive
Network (CPAN). I'll show you how to use this sort of analysis to actually
test all parts of your target program.
You use coverage analysis while testing your
code (and everyone tests their code, right?) to ensure that you have done everything
that necessary to exercise as much of the code as possible. Proper coverage
analysis by itself does not make your code better or your programs bulletproof,
but allows you to focus on problems in your code or in the test cases by examining
different coverage metrics.
You can use several different coverage analysis
Teodor Zlatanov walks you through both
the built-in Perl debugger and CPAN's Devel::ptkdb. The Perl debugger is powerful
but frustrating to navigate. CPAN's Devel::ptkdb, on the other hand, works wonders
by simplifying code debugging and thereby saving hours of your precious time.
In his discussion Zlatanov concentrates on explaining debugging methods and
general concepts rather than looking at specific tools.
Bugs are as inevitable as death and taxes.
Nevertheless, the following material should help you avoid the pitfalls of bugs.
Some of the examples will require Perl 5.6.0 or at least 5.005. If you want to try
the Emacs examples, you may also need to install the Emacs editor.
First of all, have you tried using the -w switch? If you're new to
the Perl debugger, you may prefer to read
perldebtut, which is a tutorial introduction to the debugger .
If you invoke Perl with the -d switch, your script runs under the
Perl source debugger. This works like an interactive Perl environment, prompting
for debugger commands that let you examine source code, set breakpoints, get
stack backtraces, change the values of variables, etc. This is so convenient
that you often fire up the debugger all by itself just to test out Perl constructs
interactively to see what they do. For example:
$ perl -d -e 42
In Perl, the debugger is not a separate program the way it usually is in
the typical compiled environment. Instead, the -d flag tells the compiler
to insert source information into the parse trees it's about to hand off to
the interpreter. That means your code must first compile correctly for the debugger
to work on it. Then when the interpreter starts up, it preloads a special Perl
library file containing the debugger.
The program will halt right before the first run-time executable statement
(but see below regarding compile-time statements) and ask you to enter a debugger
command. Contrary to popular expectations, whenever the debugger halts and shows
you a line of code, it always displays the line it's about to execute,
rather than the one it has just executed.
Any command not recognized by the debugger is directly executed (eval'd)
as Perl code in the current package. (The debugger uses the DB package for keeping
its own state information.)
Note that the said eval is bound by an implicit scope. As a
result any newly introduced lexical variable or any modified capture buffer
content is lost after the eval. The debugger is a nice environment to learn
Perl, but if you interactively experiment using material which should be in
the same scope, stuff it in one line.
For any text entered at the debugger prompt, leading and trailing whitespace
is first stripped before further processing. If a debugger command coincides
with some function in your own program, merely precede the function with something
that doesn't look like a debugger command, such as a leading ;
or perhaps a +, or by wrapping it with parentheses or braces.
The debugger understands the following commands:
- h
- Prints out a summary help message
- h [command]
- Prints out a help message for the given debugger command.
- h h
- The special argument of
h h produces the entire help page,
which is quite long.If the output of the h h command (or
any command, for that matter) scrolls past your screen, precede the command
with a leading pipe symbol so that it's run through your pager, as in
DB> |h h
You may change the pager which is used via o pager=... command.
- p expr
- Same as
print {$DB::OUT} expr in the current package. In
particular, because this is just Perl's own print function,
this means that nested data structures and objects are not dumped, unlike
with the x command.The DB::OUT filehandle is
opened to /dev/tty, regardless of where STDOUT may be redirected
to.
- x [maxdepth] expr
- Evaluates its expression in list context and dumps out the result in
a pretty-printed fashion. Nested data structures are printed out recursively,
unlike the real
print function in Perl. When dumping hashes,
you'll probably prefer 'x \%h' rather than 'x %h'. See
Dumpvalue if you'd like to do this yourself.The output format is
governed by multiple options described under
"Configurable Options".
If the maxdepth is included, it must be a numeral N;
the value is dumped only N levels deep, as if the dumpDepth
option had been temporarily set to N.
- V [pkg [vars]]
- Display all (or some) variables in package (defaulting to
main)
using a data pretty-printer (hashes show their keys and values so you see
what's what, control characters are made printable, etc.). Make sure you
don't put the type specifier (like $) there, just the symbol
names, like this: V DB filename line
Use ~pattern and !pattern for positive and
negative regexes.
This is similar to calling the x command on each applicable
var.
- X [vars]
- Same as
V currentpackage [vars].
- y [level [vars]]
- Display all (or some) lexical variables (mnemonic:
mY variables)
in the current scope or level scopes higher. You can limit the variables
that you see with vars which works exactly as it does for the
V and X commands. Requires the PadWalker
module version 0.08 or higher; will warn if this isn't installed. Output
is pretty-printed in the same style as for V and the format
is controlled by the same options.
- T
- Produce a stack backtrace. See below for details on its output.
- s [expr]
- Single step. Executes until the beginning of another statement, descending
into subroutine calls. If an expression is supplied that includes function
calls, it too will be single-stepped.
- n [expr]
- Next. Executes over subroutine calls, until the beginning of the next
statement. If an expression is supplied that includes function calls, those
functions will be executed with stops before each statement.
- r
- Continue until the return from the current subroutine. Dump the return
value if the
PrintRet option is set (default).
- <CR>
- Repeat last
n or s command.
- c [line|sub]
- Continue, optionally inserting a one-time-only breakpoint at the specified
line or subroutine.
- l
- List next window of lines.
- l min+incr
- List
incr+1 lines starting at min.
- l min-max
- List lines
min through max. l -
is synonymous to -.
- l line
- List a single line.
- l subname
- List first window of lines from subroutine. subname may be a
variable that contains a code reference.
- -
- List previous window of lines.
- v [line]
- View a few lines of code around the current line.
- .
- Return the internal debugger pointer to the line last executed, and
print out that line.
- f filename
- Switch to viewing a different file or
eval statement. If
filename is not a full pathname found in the values of %INC, it is
considered a regex.evaled strings (when accessible) are
considered to be filenames: f (eval 7) and f eval 7\b
access the body of the 7th evaled string (in the order of execution).
The bodies of the currently executed eval and of evaled
strings that define subroutines are saved and thus accessible.
- /pattern/
- Search forwards for pattern (a Perl regex); final / is optional. The
search is case-insensitive by default.
- ?pattern?
- Search backwards for pattern; final ? is optional. The search is case-insensitive
by default.
- L [abw]
- List (default all) actions, breakpoints and watch expressions
- S [[!]regex]
- List subroutine names [not] matching the regex.
- t
- Toggle trace mode (see also the
AutoTrace option).
- t expr
- Trace through execution of
expr. See
"Frame Listing Output Examples" in perldebguts for examples.
- b
- Sets breakpoint on current line
- b [line] [condition]
- Set a breakpoint before the given line. If a condition is specified,
it's evaluated each time the statement is reached: a breakpoint is taken
only if the condition is true. Breakpoints may only be set on lines that
begin an executable statement. Conditions don't use
if: b 237 $x > 30
b 237 ++$count237 < 11
b 33 /pattern/i
- b subname [condition]
- Set a breakpoint before the first line of the named subroutine. subname
may be a variable containing a code reference (in this case condition
is not supported).
- b postpone subname [condition]
- Set a breakpoint at first line of subroutine after it is compiled.
- b load filename
- Set a breakpoint before the first executed line of the filename,
which should be a full pathname found amongst the %INC values.
- b compile subname
- Sets a breakpoint before the first statement executed after the specified
subroutine is compiled.
- B line
- Delete a breakpoint from the specified line.
- B *
- Delete all installed breakpoints.
- a [line] command
- Set an action to be done before the line is executed. If line
is omitted, set an action on the line about to be executed. The sequence
of steps taken by the debugger is
1. check for a breakpoint at this line
2. print the line if necessary (tracing)
3. do any actions associated with that line
4. prompt user if at a breakpoint or in single-step
5. evaluate line
For example, this will print out $foo every time line 53 is passed:
a 53 print "DB FOUND $foo\n"
- A line
- Delete an action from the specified line.
- A *
- Delete all installed actions.
- w expr
- Add a global watch-expression. We hope you know what one of these is,
because they're supposed to be obvious.
- W expr
- Delete watch-expression
- W *
- Delete all watch-expressions.
- o
- Display all options
- o booloption ...
- Set each listed Boolean option to the value
1.
- o anyoption? ...
- Print out the value of one or more options.
- o option=value ...
- Set the value of one or more options. If the value has internal whitespace,
it should be quoted. For example, you could set
o pager="less -MQeicsNfr"
to call less with those specific options. You may use either single
or double quotes, but if you do, you must escape any embedded instances
of same sort of quote you began with, as well as any escaping any escapes
that immediately precede that quote but which are not meant to escape the
quote itself. In other words, you follow single-quoting rules irrespective
of the quote; eg: o option='this isn\'t bad' or o option="She
said, \"Isn't it?\"".For historical reasons, the =value
is optional, but defaults to 1 only where it is safe to do so--that is,
mostly for Boolean options. It is always better to assign a specific value
using =. The option can be abbreviated, but for
clarity probably should not be. Several options can be set together. See
"Configurable Options"
for a list of these.
- < ?
- List out all pre-prompt Perl command actions.
- < [ command ]
- Set an action (Perl command) to happen before every debugger prompt.
A multi-line command may be entered by backslashing the newlines.
- < *
- Delete all pre-prompt Perl command actions.
- << command
- Add an action (Perl command) to happen before every debugger prompt.
A multi-line command may be entered by backwhacking the newlines.
- > ?
- List out post-prompt Perl command actions.
- > command
- Set an action (Perl command) to happen after the prompt when you've
just given a command to return to executing the script. A multi-line command
may be entered by backslashing the newlines (we bet you couldn't've guessed
this by now).
- > *
- Delete all post-prompt Perl command actions.
- >> command
- Adds an action (Perl command) to happen after the prompt when you've
just given a command to return to executing the script. A multi-line command
may be entered by backslashing the newlines.
- { ?
- List out pre-prompt debugger commands.
- { [ command ]
- Set an action (debugger command) to happen before every debugger prompt.
A multi-line command may be entered in the customary fashion.
Because
this command is in some senses new, a warning is issued if you appear to
have accidentally entered a block instead. If that's what you mean to do,
write it as with ;{ ... } or even do { ... }.
- { *
- Delete all pre-prompt debugger commands.
- {{ command
- Add an action (debugger command) to happen before every debugger prompt.
A multi-line command may be entered, if you can guess how: see above.
- ! number
- Redo a previous command (defaults to the previous command).
- ! -number
- Redo number'th previous command.
- ! pattern
- Redo last command that started with pattern. See
o recallCommand,
too.
- !! cmd
- Run cmd in a subprocess (reads from DB::IN, writes to DB::OUT) See
o shellBang, also. Note that the user's current shell (well,
their $ENV{SHELL} variable) will be used, which can interfere
with proper interpretation of exit status or signal and coredump information.
- source file
- Read and execute debugger commands from file. file may
itself contain
source commands.
- H -number
- Display last n commands. Only commands longer than one character are
listed. If number is omitted, list them all.
- q or ^D
- Quit. ("quit" doesn't work for this, unless you've made an alias) This
is the only supported way to exit the debugger, though typing
exit
twice might work.Set the inhibit_exit option to 0 if you
want to be able to step off the end the script. You may also need to set
$finished to 0 if you want to step through global destruction.
- R
- Restart the debugger by
exec()ing a new session. We try
to maintain your history across this, but internal settings and command-line
options may be lost.The following setting are currently preserved: history,
breakpoints, actions, debugger options, and the Perl command-line options
-w, -I, and -e.
- |dbcmd
- Run the debugger command, piping DB::OUT into your current pager.
- ||dbcmd
- Same as
|dbcmd but DB::OUT is temporarily selected
as well.
- = [alias value]
- Define a command alias, like
= quit q
or list current aliases.
- command
- Execute command as a Perl statement. A trailing semicolon will be supplied.
If the Perl statement would otherwise be confused for a Perl debugger, use
a leading semicolon, too.
- m expr
- List which methods may be called on the result of the evaluated expression.
The expression may evaluated to a reference to a blessed object, or to a
package name.
- M
- Displays all loaded modules and their versions
- man [manpage]
- Despite its name, this calls your system's default documentation viewer
on the given page, or on the viewer itself if manpage is omitted.
If that viewer is man, the current
Config information
is used to invoke man using the proper MANPATH or -M manpath
option. Failed lookups of the form XXX that match known manpages
of the form perlXXX will be retried. This lets you type man
debug or man op from the debugger.On systems traditionally
bereft of a usable man command, the debugger invokes perldoc.
Occasionally this determination is incorrect due to recalcitrant vendors
or rather more felicitously, to enterprising users. If you fall into either
category, just manually set the $DB::doccmd variable to whatever viewer
to view the Perl documentation on your system. This may be set in an rc
file, or through direct assignment. We're still waiting for a working example
of something along the lines of:
$DB::doccmd = 'netscape -remote http://something.here/';
The debugger has numerous options settable using the o command,
either interactively or from the environment or an rc file. (./.perldb or ~/.perldb
under Unix.)
recallCommand,
ShellBang
- The characters used to recall command or spawn shell. By default, both
are set to
!, which is unfortunate.
pager
- Program to use for output of pager-piped commands (those beginning with
a
| character.) By default, $ENV{PAGER} will be
used. Because the debugger uses your current terminal characteristics for
bold and underlining, if the chosen pager does not pass escape sequences
through unchanged, the output of some debugger commands will not be readable
when sent through the pager.
tkRunning
- Run Tk while prompting (with ReadLine).
signalLevel,
warnLevel, dieLevel
- Level of verbosity. By default, the debugger leaves your exceptions
and warnings alone, because altering them can break correctly running programs.
It will attempt to print a message when uncaught INT, BUS, or SEGV signals
arrive. (But see the mention of signals in
BUGS
below.)
To disable this default safe mode, set these values to something
higher than 0. At a level of 1, you get backtraces upon receiving any kind
of warning (this is often annoying) or exception (this is often valuable).
Unfortunately, the debugger cannot discern fatal exceptions from non-fatal
ones. If dieLevel is even 1, then your non-fatal exceptions
are also traced and unceremoniously altered if they came from eval'd
strings or from any kind of eval within modules you're attempting
to load. If dieLevel is 2, the debugger doesn't care where
they came from: It usurps your exception handler and prints out a trace,
then modifies all exceptions with its own embellishments. This may perhaps
be useful for some tracing purposes, but tends to hopelessly destroy any
program that takes its exception handling seriously.
AutoTrace
- Trace mode (similar to
t command, but can be put into
PERLDB_OPTS).
LineInfo
- File or pipe to print line number info to. If it is a pipe (say,
|visual_perl_db), then a short message is used. This is the
mechanism used to interact with a slave editor or visual debugger, such
as the special vi or emacs hooks, or the
ddd graphical debugger.
inhibit_exit
- If 0, allows stepping off the end of the script.
PrintRet
- Print return value after
r command if set (default).
ornaments
- Affects screen appearance of the command line (see
Term::ReadLine). There is currently no way to disable these, which can
render some output illegible on some displays, or with some pagers. This
is considered a bug.
frame
- Affects the printing of messages upon entry and exit from subroutines.
If
frame & 2 is false, messages are printed on entry only.
(Printing on exit might be useful if interspersed with other messages.)
If frame & 4, arguments to functions are printed, plus context
and caller info. If frame & 8, overloaded stringify
and tied FETCH is enabled on the printed arguments.
If frame & 16, the return value from the subroutine is printed.
The length at which the argument list is truncated is governed by the
next option:
maxTraceLen
- Length to truncate the argument list when the
frame option's
bit 4 is set.
windowSize
- Change the size of code list window (default is 10 lines).
The following options affect what happens with V, X,
and x commands:
arrayDepth, hashDepth
- Print only first N elements ('' for all).
dumpDepth
- Limit recursion depth to N levels when dumping structures. Negative
values are interpreted as infinity. Default: infinity.
compactDump,
veryCompact
- Change the style of array and hash output. If
compactDump,
short array may be printed on one line.
globPrint
- Whether to print contents of globs.
DumpDBFiles
- Dump arrays holding debugged files.
DumpPackages
- Dump symbol tables of packages.
DumpReused
- Dump contents of "reused" addresses.
quote, HighBit,
undefPrint
- Change the style of string dump. The default value for
quote
is auto; one can enable double-quotish or single-quotish format
by setting it to " or ', respectively. By default,
characters with their high bit set are printed verbatim.
UsageOnly
- Rudimentary per-package memory usage dump. Calculates total size of
strings found in variables in the package. This does not include lexicals
in a module's file scope, or lost in closures.
After the rc file is read, the debugger reads the $ENV{PERLDB_OPTS}
environment variable and parses this as the remainder of a "O ..." line as one
might enter at the debugger prompt. You may place the initialization options
TTY, noTTY, ReadLine, and NonStop
there.
If your rc file contains:
parse_options("NonStop=1 LineInfo=db.out AutoTrace");
then your script will run without human intervention, putting trace information
into the file db.out. (If you interrupt it, you'd better reset
LineInfo to /dev/tty if you expect to see anything.)
TTY
- The TTY to use for debugging I/O.
noTTY
- If set, the debugger goes into
NonStop mode and will not
connect to a TTY. If interrupted (or if control goes to the debugger via
explicit setting of $DB::signal or $DB::single from the Perl script), it
connects to a TTY specified in the TTY option at startup, or
to a tty found at runtime using the Term::Rendezvous module
of your choice.This module should implement a method named new
that returns an object with two methods: IN and OUT.
These should return filehandles to use for debugging input and output correspondingly.
The new method should inspect an argument containing the value
of $ENV{PERLDB_NOTTY} at startup, or "$ENV{HOME}/.perldbtty$$"
otherwise. This file is not inspected for proper ownership, so security
hazards are theoretically possible.
ReadLine
- If false, readline support in the debugger is disabled in order to debug
applications that themselves use ReadLine.
NonStop
- If set, the debugger goes into non-interactive mode until interrupted,
or programmatically by setting $DB::signal or $DB::single.
Here's an example of using the $ENV{PERLDB_OPTS} variable:
$ PERLDB_OPTS="NonStop frame=2" perl -d myprogram
That will run the script myprogram without human intervention, printing
out the call tree with entry and exit points. Note that NonStop=1 frame=2
is equivalent to N f=2, and that originally, options could be uniquely
abbreviated by the first letter (modulo the Dump* options). It
is nevertheless recommended that you always spell them out in full for legibility
and future compatibility.
Other examples include
$ PERLDB_OPTS="NonStop LineInfo=listing frame=2" perl -d myprogram
which runs script non-interactively, printing info on each entry into a subroutine
and each executed line into the file named listing. (If you interrupt
it, you would better reset LineInfo to something "interactive"!)
Other examples include (using standard shell syntax to show environment variable
settings):
$ ( PERLDB_OPTS="NonStop frame=1 AutoTrace LineInfo=tperl.out"
perl -d myprogram )
which may be useful for debugging a program that uses Term::ReadLine
itself. Do not forget to detach your shell from the TTY in the window that corresponds
to /dev/ttyXX, say, by issuing a command like
$ sleep 1000000
See
"Debugger Internals" in perldebguts for details.
- Prompt
- The debugger prompt is something like
DB<8>
or even
DB<<17>>
where that number is the command number, and which you'd use to access
with the built-in csh-like history mechanism. For example,
!17 would repeat command number 17. The depth of the angle brackets
indicates the nesting depth of the debugger. You could get more than one
set of brackets, for example, if you'd already at a breakpoint and then
printed the result of a function call that itself has a breakpoint, or you
step into an expression via s/n/t expression command.
- Multiline commands
- If you want to enter a multi-line command, such as a subroutine definition
with several statements or a format, escape the newline that would normally
end the debugger command with a backslash. Here's an example:
DB<1> for (1..4) { \
cont: print "ok\n"; \
cont: }
ok
ok
ok
ok
Note that this business of escaping a newline is specific to interactive
commands typed into the debugger.
- Stack backtrace
- Here's an example of what a stack backtrace via
T command
might look like: $ = main::infested called from file `Ambulation.pm' line 10
@ = Ambulation::legs(1, 2, 3, 4) called from file `camel_flea' line 7
$ = main::pests('bactrian', 4) called from file `camel_flea' line 4
The left-hand character up there indicates the context in which the function
was called, with $ and @ meaning scalar or list
contexts respectively, and . meaning void context (which is
actually a sort of scalar context). The display above says that you were
in the function main::infested when you ran the stack dump,
and that it was called in scalar context from line 10 of the file Ambulation.pm,
but without any arguments at all, meaning it was called as &infested.
The next stack frame shows that the function Ambulation::legs
was called in list context from the camel_flea file with four arguments.
The last stack frame shows that main::pests was called in scalar
context, also from camel_flea, but from line 4.
If you execute the T command from inside an active
use statement, the backtrace will contain both a require
frame and an eval) frame.
- Line Listing Format
- This shows the sorts of output the
l command can produce: DB<<13>> l
101: @i{@i} = ();
102:b @isa{@i,$pack} = ()
103 if(exists $i{$prevpack} || exists $isa{$pack});
104 }
105
106 next
107==> if(exists $isa{$pack});
108
109:a if ($extra-- > 0) {
110: %isa = ($pack,1);
Breakable lines are marked with :. Lines with breakpoints
are marked by b and those with actions by a. The
line that's about to be executed is marked by ==>.
Please be aware that code in debugger listings may not look the same
as your original source code. Line directives and external source filters
can alter the code before Perl sees it, causing code to move from its original
positions or take on entirely different forms.
- Frame listing
- When the
frame option is set, the debugger would print
entered (and optionally exited) subroutines in different styles. See
perldebguts for incredibly long examples of these.
If you have compile-time executable statements (such as code within BEGIN
and CHECK blocks or use statements), these will not be stopped
by debugger, although requires and INIT blocks will, and compile-time
statements can be traced with AutoTrace option set in PERLDB_OPTS).
From your own Perl code, however, you can transfer control back to the debugger
using the following statement, which is harmless if the debugger is not running:
$DB::single = 1;
If you set $DB::single to 2, it's equivalent to having just
typed the n command, whereas a value of 1 means the s
command. The $DB::trace variable should be set to 1 to simulate
having typed the t command.
Another way to debug compile-time code is to start the debugger, set a breakpoint
on the load of some module:
DB<7> b load f:/perllib/lib/Carp.pm
Will stop on load of `f:/perllib/lib/Carp.pm'.
and then restart the debugger using the R command (if possible).
One can use b compile subname for the same purpose.
The debugger probably contains enough configuration hooks that you won't
ever have to modify it yourself. You may change the behaviour of debugger from
within the debugger using its o command, from the command line
via the PERLDB_OPTS environment variable, and from customization
files.
You can do some customization by setting up a .perldb file, which
contains initialization code. For instance, you could make aliases like these
(the last one is one people expect to be there):
$DB::alias{'len'} = 's/^len(.*)/p length($1)/';
$DB::alias{'stop'} = 's/^stop (at|in)/b/';
$DB::alias{'ps'} = 's/^ps\b/p scalar /';
$DB::alias{'quit'} = 's/^quit(\s*)/exit/';
You can change options from .perldb by using calls like this one;
parse_options("NonStop=1 LineInfo=db.out AutoTrace=1 frame=2");
The code is executed in the package DB. Note that .perldb
is processed before processing PERLDB_OPTS. If .perldb
defines the subroutine afterinit, that function is called after
debugger initialization ends. .perldb may be contained in the current
directory, or in the home directory. Because this file is sourced in by Perl
and may contain arbitrary commands, for security reasons, it must be owned by
the superuser or the current user, and writable by no one but its owner.
You can mock TTY input to debugger by adding arbitrary commands to @DB::typeahead.
For example, your .perldb file might contain:
sub afterinit { push @DB::typeahead, "b 4", "b 6"; }
Which would attempt to set breakpoints on lines 4 and 6 immediately after
debugger initialization. Note that @DB::typeahead is not a supported interface
and is subject to change in future releases.
If you want to modify the debugger, copy perl5db.pl from the Perl
library to another name and hack it to your heart's content. You'll then want
to set your PERL5DB environment variable to say something like
this:
BEGIN { require "myperl5db.pl" }
As a last resort, you could also use PERL5DB to customize the
debugger by directly setting internal variables or calling debugger functions.
Note that any variables and functions that are not documented in this document
(or in
perldebguts) are considered for internal use only, and as such are subject
to change without notice.
As shipped, the only command-line history supplied is a simplistic one that
checks for leading exclamation points. However, if you install the Term::ReadKey
and Term::ReadLine modules from CPAN, you will have full editing capabilities
much like GNU readline(3) provides. Look for these in the modules/by-module/Term
directory on CPAN. These do not support normal vi command-line editing,
however.
A rudimentary command-line completion is also available. Unfortunately, the
names of lexical variables are not available for completion.
If you have the FSF's version of emacs installed on your system, it
can interact with the Perl debugger to provide an integrated software development
environment reminiscent of its interactions with C debuggers.
Perl comes with a start file for making emacs act like a syntax-directed
editor that understands (some of) Perl's syntax. Look in the emacs directory
of the Perl source distribution.
A similar setup by Tom Christiansen for interacting with any vendor-shipped
vi and the X11 window system is also available. This works similarly
to the integrated multiwindow support that emacs provides, where the
debugger drives the editor. At the time of this writing, however, that tool's
eventual location in the Perl distribution was uncertain.
Users of vi should also look into vim and gvim, the
mousey and windy version, for coloring of Perl keywords.
Note that only perl can truly parse Perl, so all such CASE tools fall somewhat
short of the mark, especially if you don't program your Perl as a C programmer
might.
If you wish to supply an alternative debugger for Perl to run, just invoke
your script with a colon and a package argument given to the -d flag.
The most popular alternative debuggers for Perl is the Perl profiler. Devel::DProf
is now included with the standard Perl distribution. To profile your Perl program
in the file mycode.pl, just type:
$ perl -d:DProf mycode.pl
When the script terminates the profiler will dump the profile information
to a file called tmon.out. A tool like dprofpp, also supplied
with the standard Perl distribution, can be used to interpret the information
in that profile.
See also
Rx, the Perl
Regular Expression Debugger
use re 'debug' enables you to see the gory details of how the
Perl regular expression engine works. In order to understand this typically
voluminous output, one must not only have some idea about how regular expression
matching works in general, but also know how Perl's regular expressions are
internally compiled into an automaton. These matters are explored in some detail
in
"Debugging regular expressions" in perldebguts.
Perl contains internal support for reporting its own memory usage, but this
is a fairly advanced concept that requires some understanding of how memory
allocation works. See
"Debugging Perl memory usage" in perldebguts for the details.
You did try the -w switch, didn't you?
perldebtut,
perldebguts,
re,
DB,
Devel::DProf,
dprofpp,
Dumpvalue, and
perlrun.
When debugging a script that uses #! and is thus normally found in $PATH,
the -S option causes perl to search $PATH for it, so you don't have to type
the path or which $scriptname.
$ perl -Sd foo.pl
Debugging Tips
The goal is pretty simple: you want your code to work. The current state of
affairs is also pretty simple: it ain't working. This note is all about how
to get from where you are to where you want to be. In PHENIX, we have all sorts
of code. There are interpreted scripts in shell, perl and ROOT macros. There
are programs compiled from C, C++ and Fortran source code. There are standalone
programs. There are shared libraries. And there are dynamically loaded shared
libraries. It's quite a zoo and figuring out what you should do to diagnose
the source of a problem when something isn't working properly can be a bit daunting.
I recently gave a
presentation about debugging in PHENIX.Before I start describing specific
techniques for debugging code, here is some more general advice.
- Suspect the obvious. Before you convince yourself that omicron
rays from Alpha Centauri are responsible for your latest seg fault, investigate
a few of the more likely causes. See if your environment is set up properly.
See if you've made a typo. It may sound like silly advice, but I get a lot
of people dropping into my office swearing that the most unlikely things
are at fault.
- Localize the error. Often the most productive goal to have when
debugging is to find out precisely where the error is happening. Check
the state of the running program at that point and more likely than not
you'll see the cause of the problem.
- Make simple test cases. Your code will often be part of a much
bigger, much more complex system. Try making a simple standalone program
to exercise your code in relative isolation.
- Check your facts. Take care to be sure you know what you think
you know. I frequently see people launch themselves down long slow dead-ends
because they thought (based on pretty sloppy evidence) that something couldn't
be at fault.
- Learn a bit about what's under the hood. If you don't have
even the vaguest idea of what a compiler does or how shared libraries are
used or how a Makefile works, many simple things will confuse you and debugging
your code will be much more difficult.
- The usual suspects. Check your use of pointers. See if you are
overstepping the bounds of some array. There, you've probably investigated
the cause of 50% of all bugs.
The long-time favored refuge of those who don't know how to use better techniques.
I hope this note will help you learn a few of these better techniques and make
the practice of embedding print statements in your code a thing of the past.
Is there a place for the print statement in the armamentarium of the serious
debugger? Well, yes, but those places are few and far between. Depending on
the environment in which you're working, a print statement may be the most sophisticated
option at your disposal for debugging distributed programs like those built
on top of MPI (message passing interface). It might be your only option
if you're debugging code running on stripped down embedded processors.
You're not debugging code under those conditions. Stop using print statements
to debug your code.
Script tracing
What's a script? Roughly speaking, it's a piece of code that gets fed to an
interpreter whenever you want to use it. It's not quite that simple in real
life, but that'll do for now. We have lots of scripting languages. Shell scripts,
perl scripts, and ROOT macros are all scripts. There are a few good techniques
for debugging scripts. One of the easiest is tracing.
Tracing means telling the interpreter that's handling the script to print out
each line of the script before it executes it. The output of the script also
gets printed, so you end up with a log of source lines intertwined with script
output. It's a bit like embedding a print statement before each and every line
of your script. It can result in a lot of undigested information, and sometimes
that's enough to track down the reason your script isn't working the way you
thought it was supposed to. Tracing, like embedding pring statements, has the
downside that it's not very flexible; you're just a passive observer as your
script runs and spews its output to the screen. You can't control your program.
You can't change the values of any variables. You can' do much of anything excpet
watch. But sometimes that's enough.
Tracing shell scripts
Shell scripts are very useful. They must be useful since they come in such a
variety of flavors. Among the choices you have are sh, csh, tcsh, ksh, zsh and
ash scripts. They're all similar, yet quirkily different. Most of
the scripts you'll encounter (or write) will be sh or tcsh scripts. You can
turn on tracing for these type of scripts (and for some of the others also)
by adding the command option "-xv" to the command line of the appropriate interpreter.
For instance, suppose you have a tcsh script called dpm.csh that contains
the following lines:
foreach n (5 2 1 0)
@ i = 10 / $n
echo $i
end
If you run this straight away, you should see this:
% tcsh dpm.csh
2
5
10
Division by 0.
OK, there's a bug in the code (but then