The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Devel::Chitin - Programmatic interface to the Perl debugging API

SYNOPSIS

  package CLIENT;
  use base 'Devel::Chitin';

  # These inherited methods can be called by the client class
  CLIENT->attach();             # Register with the debugging system
  CLIENT->detach();             # Un-register with the debugging system
  CLIENT->step();               # single-step into subs
  CLIENT->stepover();           # single-step over subs
  CLIENT->stepout();            # Return from the current sub, then stop
  CLIENT->continue();           # Run until the next breakpoint
  CLIENT->trace([$flag]);       # Get/set the trace flag
  CLIENT->disable_debugger();   # Deactivate the debugging system
  CLIENT->is_loaded($file);     # Return true if the file is loaded
  CLIENT->loaded_files();       # Return a list of loaded file names
  CLIENT->postpone($file, $subref);     # Run $subref->() when $file is loaded
  CLIENT->is_breakable($file, $line);   # Return true if the line is executable
  CLIENT->stack();              # Return Devel::Chitin::Stack
  CLIENT->current_location();   # Where is the program stopped at?
  CLIENT->next_statement();     # Return the next statement to execute
  CLIENT->next_fragment([$parents]); # Return the next op to execute
  CLIENT->add_watchexpr($expr); # Add a new watch expression
  CLIENT->remove_watchexpr($expr);  # Remove a watch expression

  # These methods are called by the debugging system at the appropriate time.
  # Base-class methods do nothing.  These methods must not block.
  CLIENT->init();                       # Called when the debugging system is ready
  CLIENT->poll($location);              # Return true if there is user input
  CLIENT->idle($location);              # Handle user interaction (can block)
  CLIENT->notify_trace($location);      # Called on each executable statement
  CLIENT->notify_trace_resumed($location);  # Called before the program gets control after trace
  CLIENT->notify_stopped($location);    # Called when a break has occured
  CLIENT->notify_resumed($location);    # Called before the program gets control after a break
  CLIENT->notify_fork_parent($location,$pid);   # Called after fork() in parent
  CLIENT->notify_fork_child($location);         # Called after fork() in child
  CLIENT->notify_program_terminated($?);    # Called as the program is finishing 
  CLIENT->notify_program_exit();            # Called as the program is exiting
  CLIENT->notify_uncaught_exception($exc);  # Called after an uncaught exception
  CLIENT->notify_watch_expr($location, $expr, $old, $new);
                                        # Called when a watch expr changes

DESCRIPTION

This class exposes the Perl debugging facilities as an API useful for implementing debuggers, tracers, profilers, etc so they can all benefit from common code.

Devel::Chitin is not a usable debugger per se. It has no mechanism for interacting with a user such as reading command input or printing results. Instead, clients of this API may call methods to inspect the debugged program state. The debugger core calls methods on clients when certain events occur, such as when the program is stopped by breakpoint or when the program exits. Multiple clients can attach themselves to Devel::Chitin simultaneously within the same debugged program.

CONSTRUCTOR

This class does not supply a constructor. Clients wishing to use this API must inherit from this class and call the attach method. They may use whatever mechanism they wish to implement their object or class.

API Methods

These methods are provided by the debugging API and may be called as inherited methods by clients.

CLIENT->attach()

Attaches a client to the debugging API. May be called as a class or instance method. When later client methods are called by the debugging API, the same invocant will be used.

CLIENT->detach()

Removes a client from the debugging API. The invocant must match a previous attach call.

CLIENT->trace([1 | 0])

Get or set the trace flag. If trace is on, the client will get notified before every executable statement by having its notify_trace method called, and before returning to the debugged program by having its notify_trace_resumed method called.

CLIENT->disable_debugger()

Turn off the debugging system. The debugged program will continue normally. The debugger system will not be triggered afterward.

CLIENT->postpone($file, $subref)

Causes $subref to be called when $file is loaded. If $file is already loaded, then $subref will be called immediately.

Program control methods

CLIENT->step()

Single-step the next statement in the debugged program. If the next statement is a subroutine call, the debugger will stop on its first executable statement.

CLIENT->stepover()

Single-step the next statement in the debugged program. If the next statement is a subroutine call, the debugger will stop on its first executable statement after that subroutine call returns.

CLIENT->stepout()

Continue running the debugged program until the current subroutine returns or until the next breakpoint, whichever comes first.

CLIENT->continue()

Continue running the debugged program until the next breakpoint.

CLIENT->user_requested_exit()

Sets a flag that indicates the program should completely exit after the debugged program ends. Normally, the debugger will regain control after the program ends.

CLIENT->eval($string, $wantarray, $coderef);

Evaluate the given string in the context of the most recent stack frame of the program being debugged. Because of the limitations of Perl's debugging hooks, this function does not return the value directly. Instead, the caller must cede control back to the debugger system and the eval will be done before the next statement in the program being debugged. If the debugged program is currently stopped at a breakpoint, then the eval will be done before resuming.

The result is delivered by calling the given $coderef with two arguments: the $result and $exception. If $wantarray was true, then the result will be an arrayref.

CLIENT->eval_at($string [, $level]);

Evaluate the given string in the context of the program being debugged. If $level is omitted, the string is run in the context of the most recent stack frame of the debugged program. Otherwise, $level is the number of stack frames before the most recent to evaluate the code in. Negative numbers are treated as 0. eval_at returns a list of two items, the result and exception.

This method requires the PadWalker module.

This method is not yet implemented.

CLIENT->get_var_at_level($string, $level);

Return the value of the given variable expression. $level is the stack level in the context of the debugged program; 0 is the most recent level. $string is the name of the variable to inspect, including the sigil. This method handles some more complicated expressions such array and hash elements and slices.

This method is temporary, until eval_at() is implemented.

Informational methods

CLIENT->is_loaded($file)

Return true if the file is loaded

CLIENT->loaded_files()

Return a list of loaded file names

CLIENT->is_breakable($file, $line)

Return true if the line has an executable statement. Only lines with executable statements may have breakpoints. In particular, line containing only comments, whitespace or block delimiters are typically not breakable.

CLIENT->subroutine_location($subroutine)

Return a Devel::Chitin::SubroutineLocation instance for where the named subroutine was defined. $subroutine should be fully qualified including the package name.

If the named function does not exist, it returns undef.

CLIENT->stack()

Return an instance of Devel::Chitin::Stack. This object represents the execution/call stack of the debugged program.

CLIENT->current_location()

Return an instance of Devel::Chitin::Location representing the currently stopped location in the debugged program. This method returns undef if called when the debugged program is actively running.

CLIENT->next_statement()

Returns a string representing the next Perl statement to execute when control returns to the debugged program with "step over". This involves inspecting the OpTree of the currently executing subroutine and deparsing it at the stopped location. Since the returned string is a reconstruction based on the OpTree, it may not match the original source code exactly.

The deparse normally starts by finding the closest contol OP (COP) before the current OP, then deparsing its sibling. In some cases this results in a misleading deparse, so some adjustments may be made to the starting OP:

while loop (enterloop/leaveloop)

Return the while loop condition instead of the whole loop

list or function call (pushmark)

Return either the list construction or the function call

if() or unless() statement

Return the if () condition instead of the entire if()/unless() statement

block map/grep (mapstart/grepstart)

Return the list being mapped/grepped over

Requires the Devel::Callsite module to be installed.

CLIENT->next_fragment($parents)

Returns a string representing the next Perl operation to execute when control returns to the debugged program. This differes from next_statement() in that next_fragment() only deparses the immediately next opcode (and its children). $parents is an optional param to indicate how many parent OPs to back up before deparsing.

CLIENT->file_source($filename)

Return a list of strings containing the source code for a loaded file.

Breakpoints and Actions

See Devel::Chitin::Actionable for documentation on setting breakpoints and actions.

Watch expressions

Watch expressions are evaluated before each statement in the program. If a watched expression's value ever changes, the client that added the expression will be notified via its notify_watch_expr() method. These expressions are always evaluated in list context. They are considered changed if the list's length changes, or if one of the elements has a different value when compared as strings. This comparison is only shallow; it will not recurse into references or nested data structures.

CLIENT->add_watch_expr($expression)

Adds a new watch expression linked to the calling client.

CLIENT->remove_watch_expr($expression)

Remove a previously added watch expression. Returns false if the expression was not previously added with add_watch_expr().

CLIENT METHODS

These methods exist in the base class, but only as empty stubs. They are called at the appropriate time by the debugging system. Clients may provide their own implementation.

With the exception of idle, these client-provided methods must not block so that other clients may get called.

CLIENT->init()

Called before the first breakpoint, usually before the first executable statement in the debugged program. Its return value is ignored

CLIENT->poll($location)

Called when the debugger is stopped on a line. This method should return true to indicate that it wants its idle method called. $location is an instance of Devel::Chitin::Location indicating the next statement to be executed in the debugged program.

CLIENT->idle($location)

Called when the client can block, to accept and process user input, for example. This method should return true to indicate to the debugger system that it has finished processing, and that it is OK to continue the debugged program. The loop around calls to idle will stop when all clients return true.

CLIENT->notify_trace($location)

If a client has turned on the trace flag, this method will be called before each executable statement. The return value is ignored.

CLIENT->notify_trace_resumed($location)

If a client has turned on the trace flag, this method will be called before the debugged program regains control. The return value is ignored.

notify_trace() will be called only on clients that have requested tracing by calling CLIENT->trace(1).

CLIENT->notify_stopped($location)

This method is called when a breakpoint has occurred. Its return value is ignored.

CLIENT->notify_resumed($location)

This method is called after a breakpoint, after any calls to idle, and just before the debugged program resumes execution. The return value is ignored.

CLIENT->notify_fork_parent($location, $pid)

This method is called immediately after the debugged program calls fork() in the context of the parent process. $pid is the child process ID created by the fork. The return value is ignored.

Note that the $location will be the first executable statement after the fork() in the parent process.

CLIENT->notify_fork_child($location)

This method is called immediately after the debugged program calls fork() in the context of the child process. The return value is ignored.

Note that the $location will be the first executable statement after the fork() in the parent process.

CLIENT->notify_program_terminated($?)

This method is called after the last executable statement in the debugged program. After all clients are notified, the debugger system emulates one final breakpoint inside a function called at_exit and the program remains running, though stopped.

CLIENT->notify_program_exit()

If the a client has requested that the program terminate completely by calling CLIENT->user_requested_exit(), then this method will be called during the debugger's END block as the interpreter is cleaning up.

CLIENT->notify_uncaught_exception($exception)

The debugger system installs a __DIE__ handler to trap exceptions that are not otherwise handled by the debugged program. When an uncaught exception occurs, this method is called. $exception is an instance of Devel::Chitin::Exception.

CLIENT->notify_watch_expr($location, $expr, $old, $new);

Called when a client has added a watchexpr expression and its value has changed. Since watch expressions are evaluated in list context, $old and $new are listrefs containing the previous and new values.

The location reported is whichever program line was executing immediately prior to the current line.

Note that this does not stop execution of the debugged program. The notify_watch_expr() method should call CLIENT->step to trigger a breakpoint.

BUGS

As this is an extremely early release, this API should be considered experimental. It was developed to extract the debugger-specific code from Devel::hdb. I encourage others to make suggestions and submit bug reports so we can converge on a usable API quickly.

SEE ALSO

Devel::Chitin::Location, Devel::Chitin::Exception, Devel::Chitin::Stack, Devel::Chitin::Actionable, Devel::Chitin::GetVarAtLevel, Devel::Callsite

The API for this module was inspired by DB

AUTHOR

Anthony Brummett <brummett@cpan.org>

COPYRIGHT

Copyright 2017, Anthony Brummett. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.