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