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 minimize your keystrokes.
Example code
my $myvar = 'some value';
my @list = ('zero', 1, 'two', "3");
my %hash = ('one' => 2, 'three' => 4);
use Debug::Statements;
my $d = 1;
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
Advantages of 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 problems.
Printing the names of executing subroutines 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.
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) }
Disadvantages of traditional "print" debug statements
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 later
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.
my $myvar = 'some value';
use Debug::Statements;
my $d = 1;
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 one line each for $myvar, $myvar2, and $myvar3
Alternate syntax with parentheses
d('$myvar');
OPTIONS
Options may be specifed with an 2nd argment to d()
b print suBroutine name (on by 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');
Negating options
To negate an option, capitialize it (use 'B' instead of 'b')
Persistent options
Options are only valid for the current debug statement
To make the current options global (peristent), append a star *
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()
Your code must not already contain a local subroutine called 'd()', since this function is imported
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}, ... );
This provides an easy way for others to set your code into debug mode. They can then capture stdout and email it to you.
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 entering the d() subroutine.
Example #1
d "Found pattern: $mynum in file $filename";
Output #1
DEBUG sub mysub: Found pattern asdf in file foo.txt
Example #2
d "Found $key and replaced with $subtable_ref->{$key} on: $line"
Output #2
DEBUG sub mysub: 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
Not supported
Array slices such as
$listvar[1:3]
Some special variables such as
$1 $_ @_
...but any of these can be printed by using "double quotes", since this will cause Perl 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, prints an ls -l listing of $myfilename.
Note that ' ' is not used inside ls()
Perl versions
This module has been tested on
Linux 5.8.6, 5.8.8, 5.12, 5.14, and 5.20
It will probably work as far back as 5.8.0
Windows 5.20
GORY DETAILS
How it works
PadWalker::peek_my()
gets the value of $d and the contents of your variables (from outside its scope!) The variable values are stored in an internal hash reference
It does NOT change the values of your variables.
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, frequent calls to PadWalker::peek_my()
and caller()
may be too intensive
Solutions
Globally disable all functionality by calling
Debug::Statements::disable();
The PadWalker and caller functions will not be called. Debug statements will not be printed.OR comment out some of your calls to
d()
within performance-critical loopsOR completely disable this code is to define you own empty d() subroutines.
#use Debug::Statements qw(d d2); sub d{}; sub d2{};
AUTHOR
Chris Koknat 2018 chris.koknat@gmail.com
COPYRIGHT AND LICENSE
This software is copyright (c) 2013-18 by Chris Koknat.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.