Basic Debugging in Perl
Steven
Schubiger
Copyright (c) 2005-2011, 2015-2016 Steven Schubiger
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
Texts. A copy of the license is included in the section entitled "GNU
Free Documentation License".
print statements
As the most intuitive debugging facility to the one becoming acquainted with
Perl will likely be first print statements. Such statements may print either
simply debugging hints, contents of variables or anything part of a valid print
expression while the program is being executed. In order to interrupt the
execution of code (what is commonly known as a breakpoint in "proper"
debugging) the Perl program may also wait for random input by the user.
Imagine we were receiving a result from complex mathematical calculations and
somehow it failed our expectations; unfortunately, we have already been spending
time proofreading our code for semantical errors and did not come across any or
have overlooked their subtle occurrences.
So, let us say, $result contains one result that failed our expectations.
By inserting the following statement
print "$result\n";
the value of $result is printed at runtime with a newline appended.
This may suffice in normal cases where we do not have much data printed, but it will
fail perhaps if it is vice-versa where the output flow is too fast to be followed
by the human eye. As previously mentioned, in such cases, we will have to interrupt.
We will extend our single statement to following ones
print "$result\n";
<STDIN>;
$result is being printed as before, no change introduced here;
we do interfere now by forcing it to wait for input by the user reading from the
standard input. This mostly makes sense for interactive terminal uses, but not so
much for a CGI script, however.
An useful and basic feature most debuggers ship with as do most "equivalent" editors
is marking the line one is stepping at or editing by printing the line number at
the left. If we would like to emulate this feature, we could use __LINE__
(which although is a literal, represents the line number of the source on which it
occurs).
print __LINE__ . ":\t$result\n";
<STDIN>;
The line number followed by a colon will be printed including the interpolated
variable separated by a tab space.
It is common for many compilers to state unambiguously with <FILE:LINE: error>
kind of error messages where the programmer has to overlook the source again in case
of an error.
We could emulate this feature by prepending __FILE__
(which contains the filename of the current source file being executed).
print __FILE__ . ':' . __LINE__ . ":\t$result\n";
<STDIN>;
Data::Dumper
Simple print statements may be suitable as long as no complex data structures,
i.e. nested ones or references are being involved. A disadvantage that goes
along with such statements is that the source is temporarily being polluted,
which requires that we either have a backup handy or manually remove the
statements after debugging with the risk of eventually introducing subtle
bugs when not all occurrences have been purged.
Data::Dumper has been a core module since the beginnings of Perl
5 and it stringifies perl data structures for printing as well as for evaling them.
Data::Dumper may be used in old-fashioned function exporting way,
but also in object-oriented one which compared to former one exposes more methods.
The following excerpt loads Data::Dumper at compile-time
and prints the result from calling Dumper($result) to the
standard output.
use Data::Dumper;
...
print Dumper($result);
Examine the output of a more complex data structure, for example:
use Data::Dumper;
...
$results = [ $calc, { result1 => $calc ** 2, result2 => $calc * 3 } ];
$d = Data::Dumper->new($results);
print $d->Dump;
built-in debugger alias -d
perl invoked in the command-line with the -d switch
will launch the debugger. The most essential command is h,
which prints the summary of commands - the equivalent to --help
in many programs with gnuish long arguments. h h lists an even
more extended summary.
It is important to become aware of that the Perl debugger, unlike the GNU
C debugger (gdb), assumes implicitly a given breakpoint at line 1 regardless
whether we choose to "step" by saying step (s) or next
(n). The number in the angle brackets of __DB<1>_
does not, as one could be tempted to believe, indicate which line of source we are stepping at:
it functions as a counter of how many operations have been performed (except h)
since start-up.
As mentioned above, s or n are two variants
to step within code: former steps one line and enters subroutines (i.e. subs)
whenever one appears, whereas latter one skips subs by stepping over them.
__DB<1>_ s # step line, entering subs
__DB<1>_ n # step line, skipping subs
Given that we do approximately know where a flaw was introduced, we can set a breakpoint
on a line or sub by entering, for example, b 13, which will break at
line 13 if we force the debugger to run through the code via c [ln|sub]
(continue) until the first breakpoint (e.g. line 13) occurs.
__DB<1>_ b 13 # set breakpoint at line 13
__DB<1>_ c # continue until first breakpoint reached
In case a function or method associated with a foreign class/package gets called,
it could take up a specific extended amount of time for it to return; r
will enforce early returnment from the current subroutine being executed.
__DB<1>_ r # return from current subroutine
All these operations would probably not suffice, if we could not examine variables to
see whether they hold the expected data or not. The most basic operation concerning
data output is p expr which will print the expression supplied.
__DB<1>_ p $result # print data in $result
Assuming we would lose orientation where execution has already progressed to, we could regain
control by either typing . for the current line or -
for the previous one to be displayed. If we choose to view around our current line,
v is suited therefore.
__DB<1>_ . # print current line
__DB<1>_ - # print previous line
__DB<1>_ v # view around line
Sometimes it will be of use to have an automatic action executed whenever execution reaches
a certain line in the source.
__DB<1>_ a 2 p line2 # set action which will be executed before entering line 2
__DB<1>_ A ln|* # delete an/all action(s)
__DB<1>_ L # list all breakpoints, actions, watch expressions
localtime() is context-sensitive: it returns in scalar context the current time in
readable format, whereas in list context a list is populated with time entries.
List context evaluation may be enforced by x.
__DB<1>_ p scalar localtime # print time in readable format
__DB<1>_ x localtime # print indexed elements of list returned by localtime
The variables of a certain package can be listed via V package;
an optional pattern may be provided to list a selection of variables only. Module
versions may be displayed by M. i class
allows for insight into the inheritance tree of a class.
__DB<1>_ V [package] # list all variables or of a certain package
__DB<1>_ M # show module versions
__DB<1>_ i class # display class inheritance tree
A well-known feature to a frequent shell user is the redo feature which relieves from the
necessity of retyping previous commands; a command may be redone by typing a !
followed by an integer. A positive integer will be added
to the start of the history list to pick a command, whereas a negative one has the
effect of "looking" back.
__DB<1>_ !1 # redo first command
__DB<1>_ !-1 # redo last command
When execution comes to an end, the code may be rerun by R.
__DB<1>_ R # attempt a restart