Perl provides special debugging hooks at both compile time and run time for creating debugging environments such as the standard debugger. These hooks are not to be confused with the perl -D options, which are usable only if your Perl was built with -DDEBUGGING support.
For example, whenever you call Perl's built-in caller function from the package DB, the arguments that the corresponding stack frame was called with are copied to the the @DB::args array. When you invoke Perl with the -d switch, the following additional features are enabled:
Perl inserts the contents of $ENV{PERL5DB} (or BEGIN {require 'perl5db.pl'} if not present) before the first line of your program.
The array @{"_<$filename"} holds the lines of $filename for all files compiled by Perl. The same for evaled strings that contain subroutines or are currently being executed. The $filename for evaled strings looks like (eval 34). Code assertions in regular expressions look like (re_eval 19).
The hash %{"_<$filename"} contains breakpoints and actions keyed by line number. You can set individual entries as opposed to the whole hash. Perl only cares about Boolean truth here, although the values used by perl5db.pl have the form "$break_condition\0$action". Values in this hash are magical in numeric context: they are zeros if the line is not breakable.
The same holds for evaluated strings that contain subroutines or are currently being executed. The $filename for evaled strings looks like (eval 34) or (re_eval 19).
The scalar ${"_<$filename"} contains "_<$filename". This is also the case for evaluated strings that contain subroutines or are currently being executed. The $filename for evaled strings looks like (eval 34) or (re_eval 19).
After each required file is compiled, but before it is executed, DB::postponed(*{"_<$filename"}) is called if the subroutine DB::postponed exists. Here, the $filename is the expanded name of the required file, as found in the values of %INC.
After each subroutine subname is compiled, the existence of $DB::postponed{subname} is checked. If this key exists, DB::postponed(subname) is called if the DB::postponed subroutine also exists.
A hash %DB::sub is maintained, whose keys are subroutine names and whose values have the form filename:startline-endline. filename has the form (eval 34) for subroutines defined inside evals, or (re_eval 19) for those within regular expression code assertions.
When the execution of your program reaches a point that might hold a breakpoint, the DB::DB() subroutine is called if any of the variables $DB::trace, $DB::single, or $DB::signal is true. These variables are not localizable. This feature is disabled when executing inside DB::DB(), including functions called from it unless $^D & (1<<30) holds true.
When execution of the program reaches a subroutine call, a call to &DB::sub(args) is made instead, with $DB::sub holding the name of the called subroutine. This doesn't happen if the subroutine was compiled in the DB package.
Note that if &DB::sub needs external data for it to work, no subroutine call is possible until this is done. For the standard debugger, the $DB::deep variable (how many levels of recursion deep into the debugger you can go before a mandatory break) gives an example of such a dependency.
The minimal working debugger consists of one line:
which, since it does nothing whatsoever, can easily be defined via the PERL5DB environment variable:sub DB::DB {}
Another tiny debugger, slightly more useful, could be created like this:% PERL5DB="sub DB::DB {}" perl -d your-program
This little debugger would print the sequential number of each encountered statement and would wait for you to hit a newline before continuing.sub DB::DB {print ++$i; scalar <STDIN>}
The following debugger, small though it may appear, is really quite functional:
It prints the sequential number of the subroutine call and the name of the called subroutine. Note that &DB::sub must be compiled from the package DB, as we've done here.{ package DB; sub DB {} sub sub {print ++$i, " $sub\n"; &$sub} }
If you base your new debugger on the current debugger, there are some hooks that can help you customize it. At startup, the debugger reads your init file from the current directory or your home directory. After the file is read, the debugger reads the PERLDB_OPTS environment variable and parses this as the remainder of an O ... line such as you might enter at the debugger prompt.
The debugger also maintains magical internal variables, such as @DB::dbline, %DB::dbline, which are aliases for @{":::_<current_file"} %{"::_<current_file"}. Here current_file is the currently selected file, either explicitly chosen with the debugger's f command or implicitly by flow of execution.
Some functions can help with customization. DB::parse_options(STRING) parses a line like the O option. DB::dump_trace(SKIP[,COUNT]) skips the specified number of frames and returns a list containing information about the calling frames (all of them, if COUNT is missing). Each entry is a reference to a hash with keys "context" (either ., $, or @), "sub" (subroutine name, or info about eval), "args" (undef or a reference to an array), "file", and "line". DB::print_trace(FH,SKIP[,COUNT[,SHORT]]) prints formatted info about caller frames to the supplied filehandle. The last two functions may be convenient as arguments to the debugger's < and << commands.
You don't need to learn all that--most of us haven't. In fact, when we need to debug a program, we usually just insert a few print statements here and there and rerun the program.
On our better days, we'll even remember to turn on warnings first. That often spotlights the problem right away, thus saving a great deal of wear and tear on our hair (what's left of it). But when that doesn't work, it's nice to know that, waiting for you patiently behind that -d switch, there is a lovely debugger that can do darn near anything except find your bug for you.
But if you're going to remember one thing about customizing the debugger, perhaps it is this: don't limit your notion of bugs to things that make Perl unhappy. It's also a bug if your program makes you unhappy. Earlier, we showed you a couple of really simple custom debuggers. In the next section, we'll show you an example of a different sort of custom debugger, one that may (or may not) help you debug the bug known as "Is this thing ever gonna finish?"
Copyright © 2002 O'Reilly & Associates. All rights reserved.