NAME
Debug::Statements provides an easy way to insert and enable/disable debug statements.
SYNOPSIS
The d()
function prints the name of the variable AND its value.
This implementation been optimized to save programmer keystrokes (yours).
Example code
use Debug::Statements;
my $d = 1;
my $myvar = 'some value';
my @list = ('zero', 1, 'two', "3");
my %hash = ('one' => 2, 'three' => 4);
d "Hello world";
d '$myvar';
d '@list %hash';
Output
DEBUG sub mysub: Hello world
DEBUG sub mysub: $myvar = 'some value'
DEBUG sub mysub: @list = [
'zero',
1,
'two',
'3'
]
DEBUG sub mysub: %hash = {
'one' => 2,
'three' => 4
}
BACKGROUND
Traditional debug statement example:
my $d = 1;
my $myvar = 'some value';
if ($d) { print "DEBUG sub xyz: \$myvar is $myvar\n" }
use Dumpvalue;
if ($d) { print "\nDEBUG: Dumping \@list:\n"; Dumpvalue->new->dumpValue(\@list) }
if ($d) { print "\nDEBUG: Dumping \%hash:\n"; Dumpvalue->new->dumpValue(\%hash) }
Advantages of traditional "print" debug statements:
"The most effective debugging tool is still careful thought, coupled with judiciously placed print statements" - Brian Kernighan, "Unix for Beginners" (1979)
Familiarity. Everyone has used them.
When strategically placed, they show the values of key variables as well as the flow of control.
May be left in the code to facilitate debugging, when the code next needs to be enhanced.
May be turned on to help remotely debug and find solutions around problems.
Printing the names of subroutines executing can be particularly useful when debugging large unfamiliar programs produced by multiple developers over the span of years.
Can be used in conjuction with a debugger, which can be used to change variables on-the-fly, step into libraries, or skip/repeat sections of code
If the results are saved to a file, file comparisons can be useful during regression testing.
Disadvantages:
Tedious, require many keystrokes to type
Reduces readability of the source code.
Print statements clutter the standard output
Need to be removed or commented out
If some statements are mistakenly left in, the output can cause problems or confusion
The next time the code needs to be enhanced, any removed print statements need to be re-inserted or uncommented
Debug::Statements EXAMPLE
Debug::Statements::d()
provides an easy way to insert and enable/disable debug statements.
use Debug::Statements;
my $d = 1;
my $myvar = 'some value';
d '$myvar';
Output:
DEBUG sub mysub: $myvar = 'some value'
This is all you need to know to get started.
FEATURES
Arrays, hashes and refs:
d '@list';
d '$list[2]';
d '$list[$i]';
d '%hash';
d '$nestedhash{key}';
d '$nestedhash{$key1}{$key2}';
d '$listref';
d '$arrayref';
d '$arrayref->[2]';
d '$hashref->{key}';
d '$hashref->{$key}';
Plain text can be entered as a comment:
d 'Processing...';
d "This comment prints the value of a variable: $myvar";
Multiple debug levels:
use Debug::Statements qw(d d2 d0 D);
my $d = 1;
d '$myvar'; # prints
d2 '$myvar'; # does not print since $d < 2
$d = 2;
d '$myvar'; # prints
d2 '$myvar'; # prints
D '$myvar'; # always prints, even if $d is 0 or undef
# this is useful for short term debugging
# of existing code
d0 '$myvar'; # same as D
Supports newlines or other characters before/after the variable
d '\n $myvar';
d '\n$myvar\n\n';
d '\n-------\n@list\n--------\n';
Multiple variables can be printed easily:
d '$myvar $myvar2 $myvar3';
or
d '$myvar,$myvar2,$myvar3';
or
d '$myvar, $myvar2, $myvar3';
or
d '($myvar, $myvar2, $myvar3)';
Each of these examples prints three lines
Alternate syntax:
d('$myvar');
OPTIONS
Options may be specifed with an optional 2nd argment to d()
:
b print suBroutine name (default)
c Chomp newline before printing, useful when printing captured $line from a parsed input file
e print # of Elements contained in top level of the array or hash
n print line Number $. of the input file
q treat the string as text, do not try to evaluate it. This is useful if you are parsing another Perl script, and the text contains sigil characters $@%
r tRuncate output (defaults to 10 lines)
s Sort contents of arrays (hashes are always sorted)
t print Timestamp using localtime()
and Time::HiRes::gettimeofday()
x die when code reaches this line
z compress array and hash dumps to save screen space
Examples:
To print $line chomped and with line number and timestamp
d('$line', 'cnt');
To print %hash in a compressed format
d('%hash', 'z');
More about options
Options are only valid for the current debug statement
To negate an option, capitialize it (use 'B' instead of 'b')
Global options:
To make the current options global (peristent), append a *
For example, to set timestamp globally:
d('$var', 't*');
For example, to unset timestamp globally:
'$var', 'T*');
REQUIREMENTS
PadWalker must be installed
In addition, the test suites require Test::Fatal, Test::More, and Test::Output
$d variable
Your code must have a variable '$d' defined to enable the debug statements
Exception: D()
does not require the $d variable to exist. It always prints. See "Multiple debug levels" above.
$d was chosen because it is easy to type and intuitive
If your code already uses '$d' for another purpose, this can be changed with Debug::Statements::setFlag()
Consider enabling $d through the command line of your script:
use Getopt::Long;
my %opt;
my $d = 0;
GetOptions( \%opt, 'd' => sub{$d=1}, 'dd' => sub{$d=2}, ... );
Your code must not already contain a local subroutine called 'd()', since this function is imported
Quoting
Calls to d() should use 'single quotes' instead of "double quotes"
Exception: To produce custom output, call d() with double-quotes. As is always the case with double-quotes in Perl, variables will be interpolated into values before reaching the d() subroutine.
Example #1:
d "Found pattern: $mynum in file $filename";
Output #1:
DEBUG sub abc: Found pattern asdf in file foo.txt
Example #2:
d "Found $key and replaced with $subtable_ref->{$key} on: $line"
Output #2:
DEBUG sub abc: Found foo and replaced with bar on: foobar
Remember that when using escaped \$ \@ \% within "double quotes", this is equivalent to using $ @ % within 'single quotes'
This means that d() will try to print the names and values of those variables.
Functions
The module includes functions which affect global operation:
Debug::Statements::enable(); # enable operation (default)
Debug::Statements::disable(); # disable operation, even if $d >= 1
Debug::Statements::setFlag('$yourvar'); # default is '$d'
Debug::Statements::setPrintDebug(""); # default is "DEBUG: "
Debug::Statements::setTruncate(10); # default is 10 lines
LIMITATIONS
Does not support:
array slices such as $listvar[1:3]
some special variables such as $1 $_ @_
...but any of these IS supported when using "double quotes", since this will cause Perl itself to evaluate the expression before calling d(). For example: d "@_"
The evaluation is of variables does not support the full range of Perl syntax. Most cases work, for example: d '$hash{$key}' However hashes used as hash keys will not work, for example: d '$hash{$hash2{$key}}'
As a workaround, use "double quotes": d "\$hash{$hash2{$key}}"
instead. The rule is similar for arrays
Additional features
ls()
ls() is also provided for convenience, but not exported by default
use Debug::Statements qw(d d0 d1 d2 d3 D ls);
ls($myfilename);
When $d==1, this prints an ls -l listing of $myfilename.
Note that ' ' is not used inside ls()
ADVANCED
Perl versions:
This module has been tested on 5.8.6, 5.8.8, 5.12, 5.14, and 5.20
It may even work on 5.006
How it works:
PadWalker::peek_my()
gets the value of $d and the contents of your variable (from outside its scope!)
This is stored in the internal hash reference $h
caller()[3]
gets the name of subroutine which encloses your code
Data::Dumper
pretty-prints the contents of your variable
Performance:
For performance-critical applications, the calls to PadWalker::peek_my()
and caller()
may be problematic
Solutions:
Globally disable all functionality by calling Debug::Statements::disable();
The PadWalker and caller functions will not be called. No DEBUG statements will be printed.
A second technique is to comment out some of your calls to d()
within performance-critical loops
A third technique to completely disable this code is to define you own empty d() subroutines.
#use Debug::Statements qw(d d2);
d{}; d2{};
Debugging this module:
To turn on internal debug print statements, set $d to be negative.
AUTHOR
Chris Koknat 2014 chris.koknat@gmail.com