PREVIOUS  TABLE OF CONTENTS  NEXT 

Debugging and Devel::

Randy J. Ray

You might have learned about Perl's -d switch, which launches Perl's internal symbolic debugger. If not, read the perldebug documentation and get started; it lets you set breakpoints and step through your programs line by line. The debugger is one of Perl's most attractive tools for developers, in part because it's not so much a debugger as a debugging system into which you can you place the debugger of your choosing. All of those debuggers can be found in the Devel:: category of the CPAN, and in this article, I'd like to describe all the modules you can find there.

Devel::DProf Reports the time used by the program and its subroutines
Devel::Peek Displays Perl values and structures
Devel::SmallProf Profiles individual lines
Devel::TraceFuncs Provides call sequences of subroutines
Devel::Symdump Provides symbolic dumps for variables and symbol tables
Devel::WeakRef Creates weak references to objects
Devel::DumpStack Displays the current subroutine's stack in human-readable format
Devel::DebugInit Creates initialization files for C/C++ symbolic debuggers
Devel::Coverage Coverage analysis of Perl scripts and libraries
Devel::CoreStack Generates a stack trace from a core dump using best available debugger

Not all of these modules are debuggers, but the ones that are can be launched as perl -d: Module. DProf, SmallProf, and Coverage can be used in this way; the rest can't. Many of these are currently in early stages of development, so read the documentation carefully before using them.

RUN-TIME EXAMINATION OF DATA

Devel::Peek and Devel::Symdump help you examine Perl data as your program runs. Devel::Peek, by Ilya Zakharevich, uncovers Perl's internal representation for your data, letting you verify that XS subroutines are doing the right thing. The Devel::Symdump module, by Andreas König and Tom Christiansen, lets you examine your variables—and entire symbol tables. The Devel::Symdump package is used by the Apache/ Perl project (mod_perl; see TPJ #9) to provide run-time status of the Perl interpreter in a running Apache web server. Highly recommended.

Devel::TraceFuncs and Devel::DumpStack are handy for inspecting the subroutine stack or following the paths to subroutines. Devel::TraceFuncs, by Joe Hildebrand, allows selective enabling of the tracing of function entry, useful when you need to confirm that a subroutine is being called in the right sequence with respect to other routines. Jack Shirazi's Devel::DumpStack provides neatly-formatted stack traces. it lets you tune the depth of the trace and the formatting indentation.

All four of these modules can be brought in on demand from the debugger; at any time, you can load them with use.

PROFILING AND COVERAGE TESTING

Devel::DProf and Devel::SmallProf. Profiling tells you how much time was spent executing different portions of your program. Devel::DProf, by Dean Roehrich, measures time spent by the application itself, as well as time spent in individual subroutines. Ted Ashton's Devel::SmallProf module profiles individual lines, determining how often each was executed and how long each line took. Both tools write the profile information to an external file. DProf writes it in a binary format, providing a utility (dprofpp) to interpret the information. SmallProf geneates human-readable ASCII.

Devel::Coverage. One of the newer (and most unstable) packages is my alpha version of Devel::Coverage. Coverage analysis is similar to profiling, telling you how often lines and subroutines are reached, with an eye toward identifying sections that are never reached. This helps you develop test suites guaranteed to exercise every line of your code. The module writes results to an external file (readable with coverperl, provided with the module) and can monitor multiple runs of your program.

REFERENCE MANIPULATION

Devel::WeakRef. The Devel::WeakRef module, by Jesse Glick, enables the creation of "weak references" to data, duplicating an existing reference without changing Perl's internal reference count. These weak references can still be used to access the data, but deleting them poses no threat to the data, and deleting the data itself will proceed regardless of how many weak references still exist.

HELPING C AND C++ PROGRAMMERS

Devel::CoreStack and Devel::DebugInit aid C and C++ development. Neither of these is a Perl debugger, and therefore should be invoked with use or Perl's -M switch. Jason Stewart's Devel::DebugInit module parses C and C++ header files and creates an initialization file for symbolic debuggers (currently only gdb) that identify the macro definitions. Devel::CoreStack, by Alligator Descartes and Tim Bunce, takes a core file from a recent program crash, locates the best available debugger and fashions a short command-file to produce a stack trace of what the program was thinking when it crashed.

ROLLING YOUR OWN

Writing your own development tools can be tricky. If the tool you are designing is similar to Devel::Symdump or Devel::TraceFuncs, then write it as you would any other module. If you want a module more like the profilers or Devel::Coverage, read on.

The DB:: Namespace. The debugger resides in a special namespace called DB. Tools that need to operate on programs line-by-line can install themselves into this namespace to get the same type of capabilities that the debugger has for line-by-line stepping, subroutine breakpoints, and so on. These features are enabled by the -d switch. By itself, -d loads Perl's default debugger from perl5db.pl. If you combine the switch with a module name (for example, -d:SmallProf), Perl looks for the module in the Devel/ directory (e.g. Devel/SmallProf.pm). The module is expected to provide certain functions.

The most basic function is DB::DB(), which can called for every executable line, just as it's about to be executed. Perl's caller() function is used to identify the package name, file name, and line number; debuggers can use this information to prompt for commands if the user is single-stepping through the program. (Devel::Coverage uses it to increment the hit count for the line.)

Whenever a subroutine is entered, DB::sub() is called. $DB::sub will have been set to the name of the subroutine. The debugger system maintains a hash, %DB::sub, whose keys are the fully-qualified subroutine names, and whose values are of the form file: startline- endline. The argument list for DB::sub() is the same argument list passed to the subroutine. Don't modify it unless you know what you're doing.

DB::postponed() is called whenever a file (either program or module) has been compiled. (It can also be called after individual subroutines have been compiled, if the subroutine names exist in %DB::postponed.) When DB::postponed() is called, the only argument is a reference to a glob containing the debugger information for the file. The scalar instantiation of the glob is '_<FILENAME'. Interpreted as a list, the glob contains all lines of the file, with a strange and useful property: if compared to 0 with ==, the result is true if the line might conceivably have a breakpoint, and false otherwise. (You might want to include the statement local $^W = 0 beforehand to suppress warnings.) The profilers and Devel::Coverage use this feature to identify lines that needn't be tracked. The hash instantiation of the glob contains breakpoint information for the file, keyed by line number.

Whenever a line is reached that might conceivably have a breakpoint, DB::DB() is called if any of $DB::trace, $DB::signal, or $DB::single are true. $DB::trace, if set to true, simulates the user having typed the 't' command in the debugger. $DB::signal simulates a conditional breakpoint, and $DB::single simulates an 's' command for single-stepping lines. Given the interpreter's name for a file (those files located in @INC will be full pathnames, those that are relative to the process running directory will be relative paths), *{"_<filename"} will give you access to the debugger data for filename.

For more on this topic, read the perldebug documentation, perl5db.pl, and any of Devel::DProf, Devel::SmallProf, or Devel::Coverage.

WHICH ONES SHOULD YOU USE?

If you need to improve run-time performance, use one of the profilers. If you are building test suites, use my coverage tool. For general debugging, Devel::Peek is invaluable.

__END__


PREVIOUS  TABLE OF CONTENTS  NEXT