Basic Debugging in Perl

Steven Schubiger


Table of Contents

print statements
Data::Dumper
built-in debugger alias -d

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

Example 1. 

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

Example 2. 

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).

Example 3. 

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).

Example 4. 

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.

Example 5. 

use Data::Dumper;

...

print Dumper($result);

Examine the output of a more complex data structure, for example:

Example 6. 

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.

Example 7. 

__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.

Example 8. 

__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.

Example 9. 

__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.

Example 10. 

 __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.

Example 11. 

__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.

Example 12. 

__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.

Example 13. 

__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.

Example 14. 

__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.

Example 15. 

 __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.

Example 16. 

 __DB<1>_ R                        # attempt a restart