structure and services provided by C<perl5db.pl>, and to describe how you
can use them.
+See L<perldebug> for an overview of how to use the debugger.
+
=head1 GENERAL NOTES
The debugger can look pretty forbidding to many Perl programmers. There are
The scalar C<${"_<$filename"}> simply contains the string C<$filename>.
This is also the case for evaluated strings that contain subroutines, or
which are currently being executed. The $filename for C<eval>ed strings looks
-like C<(eval 34).
+like C<(eval 34)>.
=head1 DEBUGGER STARTUP
use strict;
+use Cwd ();
+
+my $_initial_cwd;
+
BEGIN {eval 'use IO::Handle'}; # Needed for flush only? breaks under miniperl
BEGIN {
require feature;
$^V =~ /^v(\d+\.\d+)/;
feature->import(":$1");
+ $_initial_cwd = Cwd::getcwd();
}
# Debugger for Perl 5.00x; perl5db.pl patch level:
use vars qw($VERSION $header);
-$VERSION = '1.39_10';
+# bump to X.XX in blead, only use X.XX_XX in maint
+$VERSION = '1.59';
$header = "perl5db.pl version $VERSION";
$filename
$histfile
$histsize
+ $histitemminlength
$IN
$inhibit_exit
@ini_INC
# Since we're only saving $@, we only have to localize the array element
# that it will be stored in.
local $saved[0]; # Preserve the old value of $@
- eval { DB::save() };
+ eval { &DB::save };
# Now see whether we need to report an error back to the user.
if ($at) {
lock($DBGR);
print "Threads support enabled\n";
} else {
+ *lock = sub(*) {};
*share = sub(\[$@%]) {};
}
}
@options = qw(
CommandSet HistFile HistSize
+ HistItemMinLength
hashDepth arrayDepth dumpDepth
DumpDBFiles DumpPackages DumpReused
compactDump veryCompact quote
windowSize => \$window,
HistFile => \$histfile,
HistSize => \$histsize,
+ HistItemMinLength => \$histitemminlength
);
=pod
share($pre);
share($post);
share($pretype);
-share($rl);
share($CreateTTY);
share($CommandSet);
{
*get_fork_TTY = \&xterm_get_fork_TTY; # use the xterm version
}
+ elsif ( $ENV{TMUX} ) {
+ *get_fork_TTY = \&tmux_get_fork_TTY;
+ }
elsif ( $^O eq 'os2' ) { # If this is OS/2,
*get_fork_TTY = \&os2_get_fork_TTY; # use the OS/2 version
}
PERLDB_RESTART - flag only, contains no restart data itself.
PERLDB_HIST - command history, if it's available
PERLDB_ON_LOAD - breakpoints set by the rc file
- PERLDB_POSTPONE - subs that have been loaded/not executed, and have actions
+ PERLDB_POSTPONE - subs that have been loaded/not executed,
+ and have actions
PERLDB_VISITED - files that had breakpoints
PERLDB_FILE_... - breakpoints for a file
PERLDB_OPT - active options
undef $console;
}
-=item * Unix - use F</dev/tty>.
+=item * Windows or MSDOS - use C<con>.
=cut
- elsif ( -e "/dev/tty" ) {
- $console = "/dev/tty";
+ elsif ( $^O eq 'dos' or -e "con" or $^O eq 'MSWin32' ) {
+ $console = "con";
}
-=item * Windows or MSDOS - use C<con>.
+=item * AmigaOS - use C<CONSOLE:>.
=cut
- elsif ( $^O eq 'dos' or -e "con" or $^O eq 'MSWin32' ) {
- $console = "con";
+ elsif ( $^O eq 'amigaos' ) {
+ $console = "CONSOLE:";
}
=item * VMS - use C<sys$command>.
=cut
- else {
+ elsif ($^O eq 'VMS') {
+ $console = 'sys$command';
+ }
- # everything else is ...
- $console = "sys\$command";
+# Keep this penultimate, on the grounds that it satisfies a wide variety of
+# Unix-like systems that would otherwise need to be identified individually.
+
+=item * Unix - use F</dev/tty>.
+
+=cut
+
+ elsif ( -e "/dev/tty" ) {
+ $console = "/dev/tty";
+ }
+
+# Keep this last.
+
+ else {
+ _db_warn("Can't figure out your console, using stdin");
+ undef $console;
}
=pod
$o = $i unless defined $o;
# read/write on in, or just read, or read on STDIN.
- open( IN, "+<$i" )
- || open( IN, "<$i" )
+ open( IN, '+<', $i )
+ || open( IN, '<', $i )
|| open( IN, "<&STDIN" );
# read/write/create/clobber out, or write/create/clobber out,
# or merge with STDERR, or merge with STDOUT.
- open( OUT, "+>$o" )
- || open( OUT, ">$o" )
+ open( OUT, '+>', $o )
+ || open( OUT, '>', $o )
|| open( OUT, ">&STDERR" )
|| open( OUT, ">&STDOUT" ); # so we don't dongle stdout
# see if we should stop. If so, remove the one-time sigil.
elsif ($stop) {
$evalarg = "\$DB::signal |= 1 if do {$stop}";
- DB::eval();
+ # The &-call is here to ascertain the mutability of @_.
+ &DB::eval;
# If the breakpoint is temporary, then delete its enabled status.
if ($dbline{$line} =~ s/;9($|\0)/$1/) {
_cancel_breakpoint_temp_enabled_status($filename, $line);
$cmd =~ s/\A\s+//s; # trim annoying leading whitespace
$cmd =~ s/\s+\z//s; # trim annoying trailing whitespace
- my ($verb, $args) = $cmd =~ m{\A(\S*)\s*(.*)}s;
+ # A single-character debugger command can be immediately followed by its
+ # argument if they aren't both alphanumeric; otherwise require space
+ # between commands and arguments:
+ my ($verb, $args) = $cmd =~ m{\A([^\.-]\b|\S*)\s*(.*)}s;
$obj->cmd_verb($verb);
$obj->cmd_args($args);
= $obj->cmd_args =~ /\A(?:(\d*)\s*(.*))?\z/) {
# See if we've got the necessary support.
- if (!eval { require PadWalker; PadWalker->VERSION(0.08) }) {
+ if (!eval {
+ local @INC = @INC;
+ pop @INC if $INC[-1] eq '.';
+ require PadWalker; PadWalker->VERSION(0.08) }) {
my $Err = $@;
_db_warn(
$Err =~ /locate/
my @vars = split( ' ', $match_vars || '' );
# Find the pad.
- my $h = eval { PadWalker::peek_my( ( $match_level || 0 ) + 1 ) };
+ my $h = eval { PadWalker::peek_my( ( $match_level || 0 ) + 2 ) };
# Oops. Can't find it.
if (my $Err = $@) {
# R - restart execution.
# rerun - controlled restart execution.
if ($cmd_cmd eq 'rerun' or $cmd_params eq '') {
+
+ # Change directory to the initial current working directory on
+ # the script startup, so if the debugged program changed the
+ # directory, then we will still be able to find the path to the
+ # program. (perl 5 RT #121509 ).
+ chdir ($_initial_cwd);
+
my @args = ($cmd_cmd eq 'R' ? restart() : rerun($cmd_params));
# Close all non-system fds for a clean restart. A more
open( OUT, ">&SAVEOUT" ) || _db_warn("Can't restore DB::OUT");
}
+ # Let Readline know about the new filehandles.
+ reset_IN_OUT( \*IN, \*OUT );
+
# Close filehandle pager was using, restore the normal one
# if necessary,
close(SAVEOUT);
return;
}
-# 't' is type.
-# 'm' is method.
-# 'v' is the value (i.e: method name or subroutine ref).
-# 's' is subroutine.
-my %cmd_lookup =
-(
- '-' => { t => 'm', v => '_handle_dash_command', },
- '.' => { t => 's', v => \&_DB__handle_dot_command, },
- '=' => { t => 'm', v => '_handle_equal_sign_command', },
- 'H' => { t => 'm', v => '_handle_H_command', },
- 'S' => { t => 'm', v => '_handle_S_command', },
- 'T' => { t => 'm', v => '_handle_T_command', },
- 'W' => { t => 'm', v => '_handle_W_command', },
- 'c' => { t => 's', v => \&_DB__handle_c_command, },
- 'f' => { t => 's', v => \&_DB__handle_f_command, },
- 'm' => { t => 's', v => \&_DB__handle_m_command, },
- 'n' => { t => 'm', v => '_handle_n_command', },
- 'p' => { t => 'm', v => '_handle_p_command', },
- 'q' => { t => 'm', v => '_handle_q_command', },
- 'r' => { t => 'm', v => '_handle_r_command', },
- 's' => { t => 'm', v => '_handle_s_command', },
- 'save' => { t => 'm', v => '_handle_save_command', },
- 'source' => { t => 'm', v => '_handle_source_command', },
- 't' => { t => 'm', v => '_handle_t_command', },
- 'w' => { t => 'm', v => '_handle_w_command', },
- 'x' => { t => 'm', v => '_handle_x_command', },
- 'y' => { t => 's', v => \&_DB__handle_y_command, },
- (map { $_ => { t => 'm', v => '_handle_V_command_and_X_command', }, }
- ('X', 'V')),
- (map { $_ => { t => 'm', v => '_handle_enable_disable_commands', }, }
- qw(enable disable)),
- (map { $_ =>
- { t => 's', v => \&_DB__handle_restart_and_rerun_commands, },
- } qw(R rerun)),
- (map { $_ => {t => 'm', v => '_handle_cmd_wrapper_commands' }, }
- qw(a A b B e E h i l L M o O P v w W)),
-);
+=head3 C<_DB__handle_i_command> - inheritance display
-sub DB {
+Display the (nested) parentage of the module or object given.
- # lock the debugger and get the thread id for the prompt
- lock($DBGR);
- my $tid;
- my $position;
- my ($prefix, $after, $infix);
- my $pat;
- my $explicit_stop;
- my $piped;
- my $selected;
+=cut
- if ($ENV{PERL5DB_THREADED}) {
- $tid = eval { "[".threads->tid."]" };
+sub _DB__handle_i_command {
+ my $self = shift;
+
+ my $line = $self->cmd_args;
+ require mro;
+ foreach my $isa ( split( /\s+/, $line ) ) {
+ $evalarg = "$isa";
+ # The &-call is here to ascertain the mutability of @_.
+ ($isa) = &DB::eval;
+ no strict 'refs';
+ print join(
+ ', ',
+ map {
+ "$_"
+ . (
+ defined( ${"$_\::VERSION"} )
+ ? ' ' . ${"$_\::VERSION"}
+ : undef )
+ } @{mro::get_linear_isa(ref($isa) || $isa)}
+ );
+ print "\n";
}
+ next CMD;
+}
- my $cmd_verb;
- my $cmd_args;
+=head3 C<cmd_l> - list lines (command)
- my $obj = DB::Obj->new(
- {
- position => \$position,
- prefix => \$prefix,
- after => \$after,
- explicit_stop => \$explicit_stop,
- infix => \$infix,
- cmd_args => \$cmd_args,
- cmd_verb => \$cmd_verb,
- pat => \$pat,
- piped => \$piped,
- selected => \$selected,
- },
- );
+Most of the command is taken up with transforming all the different line
+specification syntaxes into 'start-stop'. After that is done, the command
+runs a loop over C<@dbline> for the specified range of lines. It handles
+the printing of each line and any markers (C<==E<gt>> for current line,
+C<b> for break on this line, C<a> for action on this line, C<:> for this
+line breakable).
- $obj->_DB_on_init__initialize_globals(@_);
+We save the last line listed in the C<$start> global for further listing
+later.
- # Preserve current values of $@, $!, $^E, $,, $/, $\, $^W.
- # The code being debugged may have altered them.
- DB::save();
+=cut
- # Since DB::DB gets called after every line, we can use caller() to
- # figure out where we last were executing. Sneaky, eh? This works because
- # caller is returning all the extra information when called from the
- # debugger.
- local ( $package, $filename, $line ) = caller;
- $filename_ini = $filename;
+sub _min {
+ my $min = shift;
+ foreach my $v (@_) {
+ if ($min > $v) {
+ $min = $v;
+ }
+ }
+ return $min;
+}
- # set up the context for DB::eval, so it can properly execute
- # code on behalf of the user. We add the package in so that the
- # code is eval'ed in the proper package (not in the debugger!).
- local $usercontext = _calc_usercontext($package);
+sub _max {
+ my $max = shift;
+ foreach my $v (@_) {
+ if ($max < $v) {
+ $max = $v;
+ }
+ }
+ return $max;
+}
- # Create an alias to the active file magical array to simplify
- # the code here.
- local (*dbline) = $main::{ '_<' . $filename };
+sub _minify_to_max {
+ my $ref = shift;
- # Last line in the program.
- $max = $#dbline;
+ $$ref = _min($$ref, $max);
- _DB__determine_if_we_should_break(@_);
+ return;
+}
- # Preserve the current stop-or-not, and see if any of the W
- # (watch expressions) has changed.
- my $was_signal = $signal;
+sub _cmd_l_handle_var_name {
+ my $var_name = shift;
- # If we have any watch expressions ...
- _DB__handle_watch_expressions($obj);
+ $evalarg = $var_name;
-=head2 C<watchfunction()>
+ my ($s) = DB::eval();
-C<watchfunction()> is a function that can be defined by the user; it is a
-function which will be run on each entry to C<DB::DB>; it gets the
-current package, filename, and line as its parameters.
+ # Ooops. Bad scalar.
+ if ($@) {
+ print {$OUT} "Error: $@\n";
+ next CMD;
+ }
-The watchfunction can do anything it likes; it is executing in the
-debugger's context, so it has access to all of the debugger's internal
-data structures and functions.
+ # Good scalar. If it's a reference, find what it points to.
+ $s = CvGV_name($s);
+ print {$OUT} "Interpreted as: $1 $s\n";
+ $line = "$1 $s";
-C<watchfunction()> can control the debugger's actions. Any of the following
-will cause the debugger to return control to the user's program after
-C<watchfunction()> executes:
+ # Call self recursively to really do the command.
+ return _cmd_l_main( $s );
+}
-=over 4
+sub _cmd_l_handle_subname {
-=item *
+ my $s = $subname;
-Returning a false value from the C<watchfunction()> itself.
+ # De-Perl4.
+ $subname =~ s/\'/::/;
-=item *
+ # Put it in this package unless it starts with ::.
+ $subname = $package . "::" . $subname unless $subname =~ /::/;
-Altering C<$single> to a false value.
+ # Put it in CORE::GLOBAL if t doesn't start with :: and
+ # it doesn't live in this package and it lives in CORE::GLOBAL.
+ $subname = "CORE::GLOBAL::$s"
+ if not defined &$subname
+ and $s !~ /::/
+ and defined &{"CORE::GLOBAL::$s"};
-=item *
+ # Put leading '::' names into 'main::'.
+ $subname = "main" . $subname if substr( $subname, 0, 2 ) eq "::";
-Altering C<$signal> to a false value.
+ # Get name:start-stop from find_sub, and break this up at
+ # colons.
+ my @pieces = split( /:/, find_sub($subname) || $sub{$subname} );
-=item *
+ # Pull off start-stop.
+ my $subrange = pop @pieces;
-Turning off the C<4> bit in C<$trace> (this also disables the
-check for C<watchfunction()>. This can be done with
+ # If the name contained colons, the split broke it up.
+ # Put it back together.
+ $file = join( ':', @pieces );
- $trace &= ~4;
+ # If we're not in that file, switch over to it.
+ if ( $file ne $filename ) {
+ if (! $slave_editor) {
+ print {$OUT} "Switching to file '$file'.\n";
+ }
-=back
+ # Switch debugger's magic structures.
+ *dbline = $main::{ '_<' . $file };
+ $max = $#dbline;
+ $filename = $file;
+ } ## end if ($file ne $filename)
-=cut
+ # Subrange is 'start-stop'. If this is less than a window full,
+ # swap it to 'start+', which will list a window from the start point.
+ if ($subrange) {
+ if ( eval($subrange) < -$window ) {
+ $subrange =~ s/-.*/+/;
+ }
- # If there's a user-defined DB::watchfunction, call it with the
- # current package, filename, and line. The function executes in
- # the DB:: package.
- if ( $trace & 4 ) { # User-installed watch
- return
- if watchfunction( $package, $filename, $line )
- and not $single
- and not $was_signal
- and not( $trace & ~4 );
- } ## end if ($trace & 4)
-
- # Pick up any alteration to $signal in the watchfunction, and
- # turn off the signal now.
- $was_signal = $signal;
- $signal = 0;
-
-=head2 GETTING READY TO EXECUTE COMMANDS
-
-The debugger decides to take control if single-step mode is on, the
-C<t> command was entered, or the user generated a signal. If the program
-has fallen off the end, we set things up so that entering further commands
-won't cause trouble, and we say that the program is over.
-
-=cut
-
- # Make sure that we always print if asked for explicitly regardless
- # of $trace_to_depth .
- $explicit_stop = ($single || $was_signal);
-
- # Check to see if we should grab control ($single true,
- # trace set appropriately, or we got a signal).
- if ( $explicit_stop || ( $trace & 1 ) ) {
- $obj->_DB__grab_control(@_);
- } ## end if ($single || ($trace...
-
-=pod
-
-If there's an action to be executed for the line we stopped at, execute it.
-If there are any preprompt actions, execute those as well.
-
-=cut
+ # Call self recursively to list the range.
+ return _cmd_l_main( $subrange );
+ } ## end if ($subrange)
- # If there's an action, do it now.
- if ($action) {
- $evalarg = $action;
- DB::eval();
+ # Couldn't find it.
+ else {
+ print {$OUT} "Subroutine $subname not found.\n";
+ return;
}
+}
- # Are we nested another level (e.g., did we evaluate a function
- # that had a breakpoint in it at the debugger prompt)?
- if ( $single || $was_signal ) {
-
- # Yes, go down a level.
- local $level = $level + 1;
-
- # Do any pre-prompt actions.
- foreach $evalarg (@$pre) {
- DB::eval();
- }
-
- # Complain about too much recursion if we passed the limit.
- if ($single & 4) {
- print $OUT $stack_depth . " levels deep in subroutine calls!\n";
- }
-
- # The line we're currently on. Set $incr to -1 to stay here
- # until we get a command that tells us to advance.
- $start = $line;
- $incr = -1; # for backward motion.
-
- # Tack preprompt debugger actions ahead of any actual input.
- @typeahead = ( @$pretype, @typeahead );
-
-=head2 WHERE ARE WE?
-
-XXX Relocate this section?
-
-The debugger normally shows the line corresponding to the current line of
-execution. Sometimes, though, we want to see the next line, or to move elsewhere
-in the file. This is done via the C<$incr>, C<$start>, and C<$max> variables.
-
-C<$incr> controls by how many lines the I<current> line should move forward
-after a command is executed. If set to -1, this indicates that the I<current>
-line shouldn't change.
-
-C<$start> is the I<current> line. It is used for things like knowing where to
-move forwards or backwards from when doing an C<L> or C<-> command.
+sub _cmd_l_empty {
+ # Compute new range to list.
+ $incr = $window - 1;
-C<$max> tells the debugger where the last line of the current file is. It's
-used to terminate loops most often.
+ # Recurse to do it.
+ return _cmd_l_main( $start . '-' . ( $start + $incr ) );
+}
-=head2 THE COMMAND LOOP
+sub _cmd_l_plus {
+ my ($new_start, $new_incr) = @_;
-Most of C<DB::DB> is actually a command parsing and dispatch loop. It comes
-in two parts:
+ # Don't reset start for 'l +nnn'.
+ $start = $new_start if $new_start;
-=over 4
+ # Increment for list. Use window size if not specified.
+ # (Allows 'l +' to work.)
+ $incr = $new_incr || ($window - 1);
-=item *
+ # Create a line range we'll understand, and recurse to do it.
+ return _cmd_l_main( $start . '-' . ( $start + $incr ) );
+}
-The outer part of the loop, starting at the C<CMD> label. This loop
-reads a command and then executes it.
+sub _cmd_l_calc_initial_end_and_i {
+ my ($spec, $start_match, $end_match) = @_;
-=item *
+ # Determine end point; use end of file if not specified.
+ my $end = ( !defined $start_match ) ? $max :
+ ( $end_match ? $end_match : $start_match );
-The inner part of the loop, starting at the C<PIPE> label. This part
-is wholly contained inside the C<CMD> block and only executes a command.
-Used to handle commands running inside a pager.
+ # Go on to the end, and then stop.
+ _minify_to_max(\$end);
-=back
+ # Determine start line.
+ my $i = $start_match;
-So why have two labels to restart the loop? Because sometimes, it's easier to
-have a command I<generate> another command and then re-execute the loop to do
-the new command. This is faster, but perhaps a bit more convoluted.
+ if ($i eq '.') {
+ $i = $spec;
+ }
-=cut
+ $i = _max($i, 1);
- # The big command dispatch loop. It keeps running until the
- # user yields up control again.
- #
- # If we have a terminal for input, and we get something back
- # from readline(), keep on processing.
+ $incr = $end - $i;
- CMD:
- while (_DB__read_next_cmd($tid))
- {
+ return ($end, $i);
+}
- share($cmd);
- # ... try to execute the input as debugger commands.
+sub _cmd_l_range {
+ my ($spec, $current_line, $start_match, $end_match) = @_;
- # Don't stop running.
- $single = 0;
+ my ($end, $i) =
+ _cmd_l_calc_initial_end_and_i($spec, $start_match, $end_match);
- # No signal is active.
- $signal = 0;
+ # If we're running under a slave editor, force it to show the lines.
+ if ($slave_editor) {
+ print {$OUT} "\032\032$filename:$i:0\n";
+ $i = $end;
+ }
+ # We're doing it ourselves. We want to show the line and special
+ # markers for:
+ # - the current line in execution
+ # - whether a line is breakable or not
+ # - whether a line has a break or not
+ # - whether a line has an action or not
+ else {
+ I_TO_END:
+ for ( ; $i <= $end ; $i++ ) {
- # Handle continued commands (ending with \):
- if ($cmd =~ s/\\\z/\n/) {
- $cmd .= DB::readline(" cont: ");
- redo CMD;
+ # Check for breakpoints and actions.
+ my ( $stop, $action );
+ if ($dbline{$i}) {
+ ( $stop, $action ) = split( /\0/, $dbline{$i} );
}
-=head4 The null command
+ # ==> if this is the current line in execution,
+ # : if it's breakable.
+ my $arrow =
+ ( $i == $current_line and $filename eq $filename_ini )
+ ? '==>'
+ : ( $dbline[$i] + 0 ? ':' : ' ' );
-A newline entered by itself means I<re-execute the last command>. We grab the
-command out of C<$laststep> (where it was recorded previously), and copy it
-back into C<$cmd> to be executed below. If there wasn't any previous command,
-we'll do nothing below (no command will match). If there was, we also save it
-in the command history and fall through to allow the command parsing to pick
-it up.
+ # Add break and action indicators.
+ $arrow .= 'b' if $stop;
+ $arrow .= 'a' if $action;
-=cut
+ # Print the line.
+ print {$OUT} "$i$arrow\t", $dbline[$i];
- # Empty input means repeat the last command.
- if ($cmd eq '') {
- $cmd = $laststep;
- }
- chomp($cmd); # get rid of the annoying extra newline
- if (length($cmd) >= 2) {
- push( @hist, $cmd );
+ # Move on to the next line. Drop out on an interrupt.
+ if ($signal) {
+ $i++;
+ last I_TO_END;
}
- push( @truehist, $cmd );
- share(@hist);
- share(@truehist);
-
- # This is a restart point for commands that didn't arrive
- # via direct user input. It allows us to 'redo PIPE' to
- # re-execute command processing without reading a new command.
- PIPE: {
- _DB__trim_command_and_return_first_component($obj);
-
-=head3 COMMAND ALIASES
-
-The debugger can create aliases for commands (these are stored in the
-C<%alias> hash). Before a command is executed, the command loop looks it up
-in the alias hash and substitutes the contents of the alias for the command,
-completely replacing it.
-
-=cut
-
- # See if there's an alias for the command, and set it up if so.
- if ( $alias{$cmd_verb} ) {
-
- # Squelch signal handling; we want to keep control here
- # if something goes loco during the alias eval.
- local $SIG{__DIE__};
- local $SIG{__WARN__};
-
- # This is a command, so we eval it in the DEBUGGER's
- # scope! Otherwise, we can't see the special debugger
- # variables, or get to the debugger's subs. (Well, we
- # _could_, but why make it even more complicated?)
- eval "\$cmd =~ $alias{$cmd_verb}";
- if ($@) {
- local $\ = '';
- print $OUT "Couldn't evaluate '$cmd_verb' alias: $@";
- next CMD;
- }
- _DB__trim_command_and_return_first_component($obj);
- } ## end if ($alias{$cmd_verb})
-
-=head3 MAIN-LINE COMMANDS
-
-All of these commands work up to and after the program being debugged has
-terminated.
-
-=head4 C<q> - quit
-
-Quit the debugger. This entails setting the C<$fall_off_end> flag, so we don't
-try to execute further, cleaning any restart-related stuff out of the
-environment, and executing with the last value of C<$?>.
-
-=cut
-
- # All of these commands were remapped in perl 5.8.0;
- # we send them off to the secondary dispatcher (see below).
- $obj->_handle_special_char_cmd_wrapper_commands;
- _DB__trim_command_and_return_first_component($obj);
-
- if (my $cmd_rec = $cmd_lookup{$cmd_verb}) {
- my $type = $cmd_rec->{t};
- my $val = $cmd_rec->{v};
- if ($type eq 'm') {
- $obj->$val();
- }
- elsif ($type eq 's') {
- $val->($obj);
- }
- }
-
-=head4 C<t> - trace [n]
-
-Turn tracing on or off. Inverts the appropriate bit in C<$trace> (q.v.).
-If level is specified, set C<$trace_to_depth>.
-
-=head4 C<S> - list subroutines matching/not matching a pattern
-
-Walks through C<%sub>, checking to see whether or not to print the name.
-
-=head4 C<X> - list variables in current package
-
-Since the C<V> command actually processes this, just change this to the
-appropriate C<V> command and fall through.
-
-=head4 C<V> - list variables
+ } ## end for (; $i <= $end ; $i++)
-Uses C<dumpvar.pl> to dump out the current values for selected variables.
+ # Line the prompt up; print a newline if the last line listed
+ # didn't have a newline.
+ if ($dbline[ $i - 1 ] !~ /\n\z/) {
+ print {$OUT} "\n";
+ }
+ } ## end else [ if ($slave_editor)
-=head4 C<x> - evaluate and print an expression
+ # Save the point we last listed to in case another relative 'l'
+ # command is desired. Don't let it run off the end.
+ $start = $i;
+ _minify_to_max(\$start);
-Hands the expression off to C<DB::eval>, setting it up to print the value
-via C<dumpvar.pl> instead of just printing it directly.
+ return;
+}
-=head4 C<m> - print methods
+sub _cmd_l_main {
+ my $spec = shift;
-Just uses C<DB::methods> to determine what methods are available.
+ # If this is '-something', delete any spaces after the dash.
+ $spec =~ s/\A-\s*\z/-/;
-=head4 C<f> - switch files
+ # If the line is '$something', assume this is a scalar containing a
+ # line number.
+ # Set up for DB::eval() - evaluate in *user* context.
+ if ( my ($var_name) = $spec =~ /\A(\$.*)/s ) {
+ return _cmd_l_handle_var_name($var_name);
+ }
+ # l name. Try to find a sub by that name.
+ elsif ( ($subname) = $spec =~ /\A([\':A-Za-z_][\':\w]*(?:\[.*\])?)/s ) {
+ return _cmd_l_handle_subname();
+ }
+ # Bare 'l' command.
+ elsif ( $spec !~ /\S/ ) {
+ return _cmd_l_empty();
+ }
+ # l [start]+number_of_lines
+ elsif ( my ($new_start, $new_incr) = $spec =~ /\A(\d*)\+(\d*)\z/ ) {
+ return _cmd_l_plus($new_start, $new_incr);
+ }
+ # l start-stop or l start,stop
+ elsif (my ($s, $e) = $spec =~ /^(?:(-?[\d\$\.]+)(?:[-,]([\d\$\.]+))?)?/ ) {
+ return _cmd_l_range($spec, $line, $s, $e);
+ }
-Switch to a different filename.
+ return;
+} ## end sub cmd_l
-=head4 C<.> - return to last-executed line.
+sub _DB__handle_l_command {
+ my $self = shift;
-We set C<$incr> to -1 to indicate that the debugger shouldn't move ahead,
-and then we look up the line in the magical C<%dbline> hash.
+ _cmd_l_main($self->cmd_args);
+ next CMD;
+}
-=head4 C<-> - back one window
-We change C<$start> to be one window back; if we go back past the first line,
-we set it to be the first line. We ser C<$incr> to put us back at the
-currently-executing line, and then put a C<l $start +> (list one window from
-C<$start>) in C<$cmd> to be executed later.
+# 't' is type.
+# 'm' is method.
+# 'v' is the value (i.e: method name or subroutine ref).
+# 's' is subroutine.
+my %cmd_lookup;
-=head3 PRE-580 COMMANDS VS. NEW COMMANDS: C<a, A, b, B, h, l, L, M, o, O, P, v, w, W, E<lt>, E<lt>E<lt>, E<0x7B>, E<0x7B>E<0x7B>>
+BEGIN
+{
+ %cmd_lookup =
+(
+ '-' => { t => 'm', v => '_handle_dash_command', },
+ '.' => { t => 's', v => \&_DB__handle_dot_command, },
+ '=' => { t => 'm', v => '_handle_equal_sign_command', },
+ 'H' => { t => 'm', v => '_handle_H_command', },
+ 'S' => { t => 'm', v => '_handle_S_command', },
+ 'T' => { t => 'm', v => '_handle_T_command', },
+ 'W' => { t => 'm', v => '_handle_W_command', },
+ 'c' => { t => 's', v => \&_DB__handle_c_command, },
+ 'f' => { t => 's', v => \&_DB__handle_f_command, },
+ 'i' => { t => 's', v => \&_DB__handle_i_command, },
+ 'l' => { t => 's', v => \&_DB__handle_l_command, },
+ 'm' => { t => 's', v => \&_DB__handle_m_command, },
+ 'n' => { t => 'm', v => '_handle_n_command', },
+ 'p' => { t => 'm', v => '_handle_p_command', },
+ 'q' => { t => 'm', v => '_handle_q_command', },
+ 'r' => { t => 'm', v => '_handle_r_command', },
+ 's' => { t => 'm', v => '_handle_s_command', },
+ 'save' => { t => 'm', v => '_handle_save_command', },
+ 'source' => { t => 'm', v => '_handle_source_command', },
+ 't' => { t => 'm', v => '_handle_t_command', },
+ 'w' => { t => 'm', v => '_handle_w_command', },
+ 'x' => { t => 'm', v => '_handle_x_command', },
+ 'y' => { t => 's', v => \&_DB__handle_y_command, },
+ (map { $_ => { t => 'm', v => '_handle_V_command_and_X_command', }, }
+ ('X', 'V')),
+ (map { $_ => { t => 'm', v => '_handle_enable_disable_commands', }, }
+ qw(enable disable)),
+ (map { $_ =>
+ { t => 's', v => \&_DB__handle_restart_and_rerun_commands, },
+ } qw(R rerun)),
+ (map { $_ => {t => 'm', v => '_handle_cmd_wrapper_commands' }, }
+ qw(a A b B e E h L M o O v w W)),
+);
+};
-In Perl 5.8.0, a realignment of the commands was done to fix up a number of
-problems, most notably that the default case of several commands destroying
-the user's work in setting watchpoints, actions, etc. We wanted, however, to
-retain the old commands for those who were used to using them or who preferred
-them. At this point, we check for the new commands and call C<cmd_wrapper> to
-deal with them instead of processing them in-line.
+sub DB {
-=head4 C<y> - List lexicals in higher scope
+ # lock the debugger and get the thread id for the prompt
+ lock($DBGR);
+ my $tid;
+ my $position;
+ my ($prefix, $after, $infix);
+ my $pat;
+ my $explicit_stop;
+ my $piped;
+ my $selected;
-Uses C<PadWalker> to find the lexicals supplied as arguments in a scope
-above the current one and then displays then using C<dumpvar.pl>.
+ if ($ENV{PERL5DB_THREADED}) {
+ $tid = eval { "[".threads->tid."]" };
+ }
-=head3 COMMANDS NOT WORKING AFTER PROGRAM ENDS
+ my $cmd_verb;
+ my $cmd_args;
-All of the commands below this point don't work after the program being
-debugged has ended. All of them check to see if the program has ended; this
-allows the commands to be relocated without worrying about a 'line of
-demarcation' above which commands can be entered anytime, and below which
-they can't.
+ my $obj = DB::Obj->new(
+ {
+ position => \$position,
+ prefix => \$prefix,
+ after => \$after,
+ explicit_stop => \$explicit_stop,
+ infix => \$infix,
+ cmd_args => \$cmd_args,
+ cmd_verb => \$cmd_verb,
+ pat => \$pat,
+ piped => \$piped,
+ selected => \$selected,
+ },
+ );
-=head4 C<n> - single step, but don't trace down into subs
+ $obj->_DB_on_init__initialize_globals(@_);
-Done by setting C<$single> to 2, which forces subs to execute straight through
-when entered (see C<DB::sub>). We also save the C<n> command in C<$laststep>,
-so a null command knows what to re-execute.
+ # Preserve current values of $@, $!, $^E, $,, $/, $\, $^W.
+ # The code being debugged may have altered them.
+ DB::save();
-=head4 C<s> - single-step, entering subs
+ # Since DB::DB gets called after every line, we can use caller() to
+ # figure out where we last were executing. Sneaky, eh? This works because
+ # caller is returning all the extra information when called from the
+ # debugger.
+ local ( $package, $filename, $line ) = caller;
+ $filename_ini = $filename;
-Sets C<$single> to 1, which causes C<DB::sub> to continue tracing inside
-subs. Also saves C<s> as C<$lastcmd>.
+ # set up the context for DB::eval, so it can properly execute
+ # code on behalf of the user. We add the package in so that the
+ # code is eval'ed in the proper package (not in the debugger!).
+ local $usercontext = _calc_usercontext($package);
-=head4 C<c> - run continuously, setting an optional breakpoint
+ # Create an alias to the active file magical array to simplify
+ # the code here.
+ local (*dbline) = $main::{ '_<' . $filename };
-Most of the code for this command is taken up with locating the optional
-breakpoint, which is either a subroutine name or a line number. We set
-the appropriate one-time-break in C<@dbline> and then turn off single-stepping
-in this and all call levels above this one.
+ # Last line in the program.
+ $max = $#dbline;
-=head4 C<r> - return from a subroutine
+ # The &-call is here to ascertain the mutability of @_.
+ &_DB__determine_if_we_should_break;
-For C<r> to work properly, the debugger has to stop execution again
-immediately after the return is executed. This is done by forcing
-single-stepping to be on in the call level above the current one. If
-we are printing return values when a C<r> is executed, set C<$doret>
-appropriately, and force us out of the command loop.
+ # Preserve the current stop-or-not, and see if any of the W
+ # (watch expressions) has changed.
+ my $was_signal = $signal;
-=head4 C<T> - stack trace
+ # If we have any watch expressions ...
+ _DB__handle_watch_expressions($obj);
-Just calls C<DB::print_trace>.
+=head2 C<watchfunction()>
-=head4 C<w> - List window around current line.
+C<watchfunction()> is a function that can be defined by the user; it is a
+function which will be run on each entry to C<DB::DB>; it gets the
+current package, filename, and line as its parameters.
-Just calls C<DB::cmd_w>.
+The watchfunction can do anything it likes; it is executing in the
+debugger's context, so it has access to all of the debugger's internal
+data structures and functions.
-=head4 C<W> - watch-expression processing.
+C<watchfunction()> can control the debugger's actions. Any of the following
+will cause the debugger to return control to the user's program after
+C<watchfunction()> executes:
-Just calls C<DB::cmd_W>.
+=over 4
-=head4 C</> - search forward for a string in the source
+=item *
-We take the argument and treat it as a pattern. If it turns out to be a
-bad one, we return the error we got from trying to C<eval> it and exit.
-If not, we create some code to do the search and C<eval> it so it can't
-mess us up.
+Returning a false value from the C<watchfunction()> itself.
-=cut
+=item *
- _DB__handle_forward_slash_command($obj);
+Altering C<$single> to a false value.
-=head4 C<?> - search backward for a string in the source
+=item *
-Same as for C</>, except the loop runs backwards.
+Altering C<$signal> to a false value.
-=cut
+=item *
- _DB__handle_question_mark_command($obj);
+Turning off the C<4> bit in C<$trace> (this also disables the
+check for C<watchfunction()>. This can be done with
-=head4 C<$rc> - Recall command
+ $trace &= ~4;
-Manages the commands in C<@hist> (which is created if C<Term::ReadLine> reports
-that the terminal supports history). It finds the command required, puts it
-into C<$cmd>, and redoes the loop to execute it.
+=back
=cut
- # $rc - recall command.
- $obj->_handle_rc_recall_command;
+ # If there's a user-defined DB::watchfunction, call it with the
+ # current package, filename, and line. The function executes in
+ # the DB:: package.
+ if ( $trace & 4 ) { # User-installed watch
+ return
+ if watchfunction( $package, $filename, $line )
+ and not $single
+ and not $was_signal
+ and not( $trace & ~4 );
+ } ## end if ($trace & 4)
-=head4 C<$sh$sh> - C<system()> command
+ # Pick up any alteration to $signal in the watchfunction, and
+ # turn off the signal now.
+ $was_signal = $signal;
+ $signal = 0;
-Calls the C<_db_system()> to handle the command. This keeps the C<STDIN> and
-C<STDOUT> from getting messed up.
+=head2 GETTING READY TO EXECUTE COMMANDS
+
+The debugger decides to take control if single-step mode is on, the
+C<t> command was entered, or the user generated a signal. If the program
+has fallen off the end, we set things up so that entering further commands
+won't cause trouble, and we say that the program is over.
=cut
- $obj->_handle_sh_command;
+ # Make sure that we always print if asked for explicitly regardless
+ # of $trace_to_depth .
+ $explicit_stop = ($single || $was_signal);
-=head4 C<$rc I<pattern> $rc> - Search command history
+ # Check to see if we should grab control ($single true,
+ # trace set appropriately, or we got a signal).
+ if ( $explicit_stop || ( $trace & 1 ) ) {
+ $obj->_DB__grab_control(@_);
+ } ## end if ($single || ($trace...
-Another command to manipulate C<@hist>: this one searches it with a pattern.
-If a command is found, it is placed in C<$cmd> and executed via C<redo>.
+=pod
+
+If there's an action to be executed for the line we stopped at, execute it.
+If there are any preprompt actions, execute those as well.
=cut
- $obj->_handle_rc_search_history_command;
+ # If there's an action, do it now.
+ if ($action) {
+ $evalarg = $action;
+ # The &-call is here to ascertain the mutability of @_.
+ &DB::eval;
+ }
+ undef $action;
-=head4 C<$sh> - Invoke a shell
+ # Are we nested another level (e.g., did we evaluate a function
+ # that had a breakpoint in it at the debugger prompt)?
+ if ( $single || $was_signal ) {
-Uses C<_db_system()> to invoke a shell.
+ # Yes, go down a level.
+ local $level = $level + 1;
-=cut
+ # Do any pre-prompt actions.
+ foreach $evalarg (@$pre) {
+ # The &-call is here to ascertain the mutability of @_.
+ &DB::eval;
+ }
-=head4 C<$sh I<command>> - Force execution of a command in a shell
+ # Complain about too much recursion if we passed the limit.
+ if ($single & 4) {
+ print $OUT $stack_depth . " levels deep in subroutine calls!\n";
+ }
-Like the above, but the command is passed to the shell. Again, we use
-C<_db_system()> to avoid problems with C<STDIN> and C<STDOUT>.
+ # The line we're currently on. Set $incr to -1 to stay here
+ # until we get a command that tells us to advance.
+ $start = $line;
+ $incr = -1; # for backward motion.
-=head4 C<H> - display commands in history
+ # Tack preprompt debugger actions ahead of any actual input.
+ @typeahead = ( @$pretype, @typeahead );
-Prints the contents of C<@hist> (if any).
+=head2 WHERE ARE WE?
-=head4 C<man, doc, perldoc> - look up documentation
+XXX Relocate this section?
-Just calls C<runman()> to print the appropriate document.
+The debugger normally shows the line corresponding to the current line of
+execution. Sometimes, though, we want to see the next line, or to move elsewhere
+in the file. This is done via the C<$incr>, C<$start>, and C<$max> variables.
-=cut
+C<$incr> controls by how many lines the I<current> line should move forward
+after a command is executed. If set to -1, this indicates that the I<current>
+line shouldn't change.
- $obj->_handle_doc_command;
+C<$start> is the I<current> line. It is used for things like knowing where to
+move forwards or backwards from when doing an C<L> or C<-> command.
-=head4 C<p> - print
+C<$max> tells the debugger where the last line of the current file is. It's
+used to terminate loops most often.
-Builds a C<print EXPR> expression in the C<$cmd>; this will get executed at
-the bottom of the loop.
+=head2 THE COMMAND LOOP
-=head4 C<=> - define command alias
+Most of C<DB::DB> is actually a command parsing and dispatch loop. It comes
+in two parts:
-Manipulates C<%alias> to add or list command aliases.
+=over 4
-=head4 C<source> - read commands from a file.
+=item *
-Opens a lexical filehandle and stacks it on C<@cmdfhs>; C<DB::readline> will
-pick it up.
+The outer part of the loop, starting at the C<CMD> label. This loop
+reads a command and then executes it.
-=head4 C<enable> C<disable> - enable or disable breakpoints
+=item *
-This enables or disables breakpoints.
+The inner part of the loop, starting at the C<PIPE> label. This part
+is wholly contained inside the C<CMD> block and only executes a command.
+Used to handle commands running inside a pager.
-=head4 C<save> - send current history to a file
+=back
-Takes the complete history, (not the shrunken version you see with C<H>),
-and saves it to the given filename, so it can be replayed using C<source>.
+So why have two labels to restart the loop? Because sometimes, it's easier to
+have a command I<generate> another command and then re-execute the loop to do
+the new command. This is faster, but perhaps a bit more convoluted.
-Note that all C<^(save|source)>'s are commented out with a view to minimise recursion.
+=cut
-=head4 C<R> - restart
+ # The big command dispatch loop. It keeps running until the
+ # user yields up control again.
+ #
+ # If we have a terminal for input, and we get something back
+ # from readline(), keep on processing.
-Restart the debugger session.
+ CMD:
+ while (_DB__read_next_cmd($tid))
+ {
-=head4 C<rerun> - rerun the current session
+ share($cmd);
+ # ... try to execute the input as debugger commands.
-Return to any given position in the B<true>-history list
+ # Don't stop running.
+ $single = 0;
-=head4 C<|, ||> - pipe output through the pager.
+ # No signal is active.
+ $signal = 0;
-For C<|>, we save C<OUT> (the debugger's output filehandle) and C<STDOUT>
-(the program's standard output). For C<||>, we only save C<OUT>. We open a
-pipe to the pager (restoring the output filehandles if this fails). If this
-is the C<|> command, we also set up a C<SIGPIPE> handler which will simply
-set C<$signal>, sending us back into the debugger.
+ # Handle continued commands (ending with \):
+ if ($cmd =~ s/\\\z/\n/) {
+ $cmd .= DB::readline(" cont: ");
+ redo CMD;
+ }
-We then trim off the pipe symbols and C<redo> the command loop at the
-C<PIPE> label, causing us to evaluate the command in C<$cmd> without
-reading another.
+=head4 The null command
+
+A newline entered by itself means I<re-execute the last command>. We grab the
+command out of C<$laststep> (where it was recorded previously), and copy it
+back into C<$cmd> to be executed below. If there wasn't any previous command,
+we'll do nothing below (no command will match). If there was, we also save it
+in the command history and fall through to allow the command parsing to pick
+it up.
=cut
- # || - run command in the pager, with output to DB::OUT.
- _DB__handle_run_command_in_pager_command($obj);
+ # Empty input means repeat the last command.
+ if ($cmd eq '') {
+ $cmd = $laststep;
+ }
+ chomp($cmd); # get rid of the annoying extra newline
+ if (length($cmd) >= option_val('HistItemMinLength', 2)) {
+ push( @hist, $cmd );
+ }
+ push( @truehist, $cmd );
+ share(@hist);
+ share(@truehist);
-=head3 END OF COMMAND PARSING
+ # This is a restart point for commands that didn't arrive
+ # via direct user input. It allows us to 'redo PIPE' to
+ # re-execute command processing without reading a new command.
+ PIPE: {
+ _DB__trim_command_and_return_first_component($obj);
-Anything left in C<$cmd> at this point is a Perl expression that we want to
-evaluate. We'll always evaluate in the user's context, and fully qualify
-any variables we might want to address in the C<DB> package.
+=head3 COMMAND ALIASES
+
+The debugger can create aliases for commands (these are stored in the
+C<%alias> hash). Before a command is executed, the command loop looks it up
+in the alias hash and substitutes the contents of the alias for the command,
+completely replacing it.
=cut
- } # PIPE:
+ # See if there's an alias for the command, and set it up if so.
+ if ( $alias{$cmd_verb} ) {
- # trace an expression
- $cmd =~ s/^t\s/\$DB::trace |= 1;\n/;
+ # Squelch signal handling; we want to keep control here
+ # if something goes loco during the alias eval.
+ local $SIG{__DIE__};
+ local $SIG{__WARN__};
- # Make sure the flag that says "the debugger's running" is
- # still on, to make sure we get control again.
- $evalarg = "\$^D = \$^D | \$DB::db_stop;\n$cmd";
+ # This is a command, so we eval it in the DEBUGGER's
+ # scope! Otherwise, we can't see the special debugger
+ # variables, or get to the debugger's subs. (Well, we
+ # _could_, but why make it even more complicated?)
+ eval "\$cmd =~ $alias{$cmd_verb}";
+ if ($@) {
+ local $\ = '';
+ print $OUT "Couldn't evaluate '$cmd_verb' alias: $@";
+ next CMD;
+ }
+ _DB__trim_command_and_return_first_component($obj);
+ } ## end if ($alias{$cmd_verb})
- # Run *our* eval that executes in the caller's context.
- DB::eval();
+=head3 MAIN-LINE COMMANDS
- # Turn off the one-time-dump stuff now.
- if ($onetimeDump) {
- $onetimeDump = undef;
- $onetimedumpDepth = undef;
- }
- elsif ( $term_pid == $$ ) {
- eval { # May run under miniperl, when not available...
- STDOUT->flush();
- STDERR->flush();
- };
+All of these commands work up to and after the program being debugged has
+terminated.
- # XXX If this is the master pid, print a newline.
- print {$OUT} "\n";
- }
- } ## end while (($term || &setterm...
+=head4 C<q> - quit
+
+Quit the debugger. This entails setting the C<$fall_off_end> flag, so we don't
+try to execute further, cleaning any restart-related stuff out of the
+environment, and executing with the last value of C<$?>.
-=head3 POST-COMMAND PROCESSING
+=cut
-After each command, we check to see if the command output was piped anywhere.
-If so, we go through the necessary code to unhook the pipe and go back to
-our standard filehandles for input and output.
+ # All of these commands were remapped in perl 5.8.0;
+ # we send them off to the secondary dispatcher (see below).
+ $obj->_handle_special_char_cmd_wrapper_commands;
+ _DB__trim_command_and_return_first_component($obj);
-=cut
+ if (my $cmd_rec = $cmd_lookup{$cmd_verb}) {
+ my $type = $cmd_rec->{t};
+ my $val = $cmd_rec->{v};
+ if ($type eq 'm') {
+ $obj->$val();
+ }
+ elsif ($type eq 's') {
+ $val->($obj);
+ }
+ }
- continue { # CMD:
- _DB__at_end_of_every_command($obj);
- } # CMD:
+=head4 C<t> - trace [n]
-=head3 COMMAND LOOP TERMINATION
+Turn tracing on or off. Inverts the appropriate bit in C<$trace> (q.v.).
+If level is specified, set C<$trace_to_depth>.
-When commands have finished executing, we come here. If the user closed the
-input filehandle, we turn on C<$fall_off_end> to emulate a C<q> command. We
-evaluate any post-prompt items. We restore C<$@>, C<$!>, C<$^E>, C<$,>, C<$/>,
-C<$\>, and C<$^W>, and return a null list as expected by the Perl interpreter.
-The interpreter will then execute the next line and then return control to us
-again.
+=head4 C<S> - list subroutines matching/not matching a pattern
-=cut
+Walks through C<%sub>, checking to see whether or not to print the name.
- # No more commands? Quit.
- $fall_off_end = 1 unless defined $cmd; # Emulate 'q' on EOF
+=head4 C<X> - list variables in current package
- # Evaluate post-prompt commands.
- foreach $evalarg (@$post) {
- DB::eval();
- }
- } # if ($single || $signal)
+Since the C<V> command actually processes this, just change this to the
+appropriate C<V> command and fall through.
- # Put the user's globals back where you found them.
- ( $@, $!, $^E, $,, $/, $\, $^W ) = @saved;
- ();
-} ## end sub DB
+=head4 C<V> - list variables
-# Because DB::Obj is used above,
-#
-# my $obj = DB::Obj->new(
-#
-# The following package declaration must come before that,
-# or else runtime errors will occur with
-#
-# PERLDB_OPTS="autotrace nonstop"
-#
-# ( rt#116771 )
-BEGIN {
+Uses C<dumpvar.pl> to dump out the current values for selected variables.
-package DB::Obj;
+=head4 C<x> - evaluate and print an expression
-sub new {
- my $class = shift;
+Hands the expression off to C<DB::eval>, setting it up to print the value
+via C<dumpvar.pl> instead of just printing it directly.
- my $self = bless {}, $class;
+=head4 C<m> - print methods
- $self->_init(@_);
+Just uses C<DB::methods> to determine what methods are available.
- return $self;
-}
+=head4 C<f> - switch files
-sub _init {
- my ($self, $args) = @_;
+Switch to a different filename.
- %{$self} = (%$self, %$args);
+=head4 C<.> - return to last-executed line.
- return;
-}
+We set C<$incr> to -1 to indicate that the debugger shouldn't move ahead,
+and then we look up the line in the magical C<%dbline> hash.
-{
- no strict 'refs';
- foreach my $slot_name (qw(
- after explicit_stop infix pat piped position prefix selected cmd_verb
- cmd_args
- )) {
- my $slot = $slot_name;
- *{$slot} = sub {
- my $self = shift;
+=head4 C<-> - back one window
- if (@_) {
- ${ $self->{$slot} } = shift;
- }
+We change C<$start> to be one window back; if we go back past the first line,
+we set it to be the first line. We set C<$incr> to put us back at the
+currently-executing line, and then put a C<l $start +> (list one window from
+C<$start>) in C<$cmd> to be executed later.
- return ${ $self->{$slot} };
- };
+=head3 PRE-580 COMMANDS VS. NEW COMMANDS: C<a, A, b, B, h, l, L, M, o, O, P, v, w, W, E<lt>, E<lt>E<lt>, E<0x7B>, E<0x7B>E<0x7B>>
- *{"append_to_$slot"} = sub {
- my $self = shift;
- my $s = shift;
+In Perl 5.8.0, a realignment of the commands was done to fix up a number of
+problems, most notably that the default case of several commands destroying
+the user's work in setting watchpoints, actions, etc. We wanted, however, to
+retain the old commands for those who were used to using them or who preferred
+them. At this point, we check for the new commands and call C<cmd_wrapper> to
+deal with them instead of processing them in-line.
- return $self->$slot($self->$slot . $s);
- };
- }
-}
+=head4 C<y> - List lexicals in higher scope
-sub _DB_on_init__initialize_globals
-{
- my $self = shift;
+Uses C<PadWalker> to find the lexicals supplied as arguments in a scope
+above the current one and then displays then using C<dumpvar.pl>.
- # Check for whether we should be running continuously or not.
- # _After_ the perl program is compiled, $single is set to 1:
- if ( $single and not $second_time++ ) {
+=head3 COMMANDS NOT WORKING AFTER PROGRAM ENDS
- # Options say run non-stop. Run until we get an interrupt.
- if ($runnonstop) { # Disable until signal
- # If there's any call stack in place, turn off single
- # stepping into subs throughout the stack.
- for my $i (0 .. $stack_depth) {
- $stack[ $i ] &= ~1;
- }
+All of the commands below this point don't work after the program being
+debugged has ended. All of them check to see if the program has ended; this
+allows the commands to be relocated without worrying about a 'line of
+demarcation' above which commands can be entered anytime, and below which
+they can't.
- # And we are now no longer in single-step mode.
- $single = 0;
+=head4 C<n> - single step, but don't trace down into subs
- # If we simply returned at this point, we wouldn't get
- # the trace info. Fall on through.
- # return;
- } ## end if ($runnonstop)
+Done by setting C<$single> to 2, which forces subs to execute straight through
+when entered (see C<DB::sub>). We also save the C<n> command in C<$laststep>,
+so a null command knows what to re-execute.
- elsif ($ImmediateStop) {
+=head4 C<s> - single-step, entering subs
- # We are supposed to stop here; XXX probably a break.
- $ImmediateStop = 0; # We've processed it; turn it off
- $signal = 1; # Simulate an interrupt to force
- # us into the command loop
- }
- } ## end if ($single and not $second_time...
+Sets C<$single> to 1, which causes C<DB::sub> to continue tracing inside
+subs. Also saves C<s> as C<$lastcmd>.
- # If we're in single-step mode, or an interrupt (real or fake)
- # has occurred, turn off non-stop mode.
- $runnonstop = 0 if $single or $signal;
+=head4 C<c> - run continuously, setting an optional breakpoint
- return;
-}
+Most of the code for this command is taken up with locating the optional
+breakpoint, which is either a subroutine name or a line number. We set
+the appropriate one-time-break in C<@dbline> and then turn off single-stepping
+in this and all call levels above this one.
-sub _my_print_lineinfo
-{
- my ($self, $i, $incr_pos) = @_;
+=head4 C<r> - return from a subroutine
- if ($frame) {
- # Print it indented if tracing is on.
- DB::print_lineinfo( ' ' x $stack_depth,
- "$i:\t$DB::dbline[$i]" . $self->after );
- }
- else {
- DB::depth_print_lineinfo($self->explicit_stop, $incr_pos);
- }
-}
+For C<r> to work properly, the debugger has to stop execution again
+immediately after the return is executed. This is done by forcing
+single-stepping to be on in the call level above the current one. If
+we are printing return values when a C<r> is executed, set C<$doret>
+appropriately, and force us out of the command loop.
-sub _curr_line {
- return $DB::dbline[$line];
-}
+=head4 C<T> - stack trace
-sub _is_full {
- my ($self, $letter) = @_;
+Just calls C<DB::print_trace>.
- return ($DB::cmd eq $letter);
-}
+=head4 C<w> - List window around current line.
-sub _DB__grab_control
-{
- my $self = shift;
+Just calls C<DB::cmd_w>.
- # Yes, grab control.
- if ($slave_editor) {
+=head4 C<W> - watch-expression processing.
- # Tell the editor to update its position.
- $self->position("\032\032${DB::filename}:$line:0\n");
- DB::print_lineinfo($self->position());
- }
+Just calls C<DB::cmd_W>.
-=pod
+=head4 C</> - search forward for a string in the source
-Special check: if we're in package C<DB::fake>, we've gone through the
-C<END> block at least once. We set up everything so that we can continue
-to enter commands and have a valid context to be in.
+We take the argument and treat it as a pattern. If it turns out to be a
+bad one, we return the error we got from trying to C<eval> it and exit.
+If not, we create some code to do the search and C<eval> it so it can't
+mess us up.
=cut
- elsif ( $DB::package eq 'DB::fake' ) {
+ _DB__handle_forward_slash_command($obj);
- # Fallen off the end already.
- if (!$DB::term) {
- DB::setterm();
- }
+=head4 C<?> - search backward for a string in the source
- DB::print_help(<<EOP);
-Debugged program terminated. Use B<q> to quit or B<R> to restart,
-use B<o> I<inhibit_exit> to avoid stopping after program termination,
-B<h q>, B<h R> or B<h o> to get additional info.
-EOP
+Same as for C</>, except the loop runs backwards.
- # Set the DB::eval context appropriately.
- $DB::package = 'main';
- $DB::usercontext = DB::_calc_usercontext($DB::package);
- } ## end elsif ($package eq 'DB::fake')
+=cut
-=pod
+ _DB__handle_question_mark_command($obj);
-If the program hasn't finished executing, we scan forward to the
-next executable line, print that out, build the prompt from the file and line
-number information, and print that.
+=head4 C<$rc> - Recall command
+
+Manages the commands in C<@hist> (which is created if C<Term::ReadLine> reports
+that the terminal supports history). It finds the command required, puts it
+into C<$cmd>, and redoes the loop to execute it.
=cut
- else {
+ # $rc - recall command.
+ $obj->_handle_rc_recall_command;
+=head4 C<$sh$sh> - C<system()> command
+
+Calls the C<_db_system()> to handle the command. This keeps the C<STDIN> and
+C<STDOUT> from getting messed up.
+
+=cut
+
+ $obj->_handle_sh_command;
- # Still somewhere in the midst of execution. Set up the
- # debugger prompt.
- $DB::sub =~ s/\'/::/; # Swap Perl 4 package separators (') to
- # Perl 5 ones (sorry, we don't print Klingon
- #module names)
+=head4 C<$rc I<pattern> $rc> - Search command history
- $self->prefix($DB::sub =~ /::/ ? "" : ($DB::package . '::'));
- $self->append_to_prefix( "$DB::sub(${DB::filename}:" );
- $self->after( $self->_curr_line =~ /\n$/ ? '' : "\n" );
+Another command to manipulate C<@hist>: this one searches it with a pattern.
+If a command is found, it is placed in C<$cmd> and executed via C<redo>.
- # Break up the prompt if it's really long.
- if ( length($self->prefix()) > 30 ) {
- $self->position($self->prefix . "$line):\n$line:\t" . $self->_curr_line . $self->after);
- $self->prefix("");
- $self->infix(":\t");
- }
- else {
- $self->infix("):\t");
- $self->position(
- $self->prefix . $line. $self->infix
- . $self->_curr_line . $self->after
- );
- }
+=cut
- # Print current line info, indenting if necessary.
- $self->_my_print_lineinfo($line, $self->position);
+ $obj->_handle_rc_search_history_command;
- my $i;
- my $line_i = sub { return $DB::dbline[$i]; };
+=head4 C<$sh> - Invoke a shell
- # Scan forward, stopping at either the end or the next
- # unbreakable line.
- for ( $i = $line + 1 ; $i <= $DB::max && $line_i->() == 0 ; ++$i )
- { #{ vi
+Uses C<_db_system()> to invoke a shell.
- # Drop out on null statements, block closers, and comments.
- last if $line_i->() =~ /^\s*[\;\}\#\n]/;
+=cut
- # Drop out if the user interrupted us.
- last if $signal;
+=head4 C<$sh I<command>> - Force execution of a command in a shell
- # Append a newline if the line doesn't have one. Can happen
- # in eval'ed text, for instance.
- $self->after( $line_i->() =~ /\n$/ ? '' : "\n" );
+Like the above, but the command is passed to the shell. Again, we use
+C<_db_system()> to avoid problems with C<STDIN> and C<STDOUT>.
- # Next executable line.
- my $incr_pos = $self->prefix . $i . $self->infix . $line_i->()
- . $self->after;
- $self->append_to_position($incr_pos);
- $self->_my_print_lineinfo($i, $incr_pos);
- } ## end for ($i = $line + 1 ; $i...
- } ## end else [ if ($slave_editor)
+=head4 C<H> - display commands in history
- return;
-}
+Prints the contents of C<@hist> (if any).
-sub _handle_t_command {
- my $self = shift;
+=head4 C<man, doc, perldoc> - look up documentation
- my $levels = $self->cmd_args();
+Just calls C<runman()> to print the appropriate document.
- if ((!length($levels)) or ($levels !~ /\D/)) {
- $trace ^= 1;
- local $\ = '';
- $DB::trace_to_depth = $levels ? $stack_depth + $levels : 1E9;
- print {$OUT} "Trace = "
- . ( ( $trace & 1 )
- ? ( $levels ? "on (to level $DB::trace_to_depth)" : "on" )
- : "off" ) . "\n";
- next CMD;
- }
+=cut
- return;
-}
+ $obj->_handle_doc_command;
+=head4 C<p> - print
-sub _handle_S_command {
- my $self = shift;
+Builds a C<print EXPR> expression in the C<$cmd>; this will get executed at
+the bottom of the loop.
- if (my ($print_all_subs, $should_reverse, $Spatt)
- = $self->cmd_args =~ /\A((!)?(.+))?\z/) {
- # $Spatt is the pattern (if any) to use.
- # Reverse scan?
- my $Srev = defined $should_reverse;
- # No args - print all subs.
- my $Snocheck = !defined $print_all_subs;
+=head4 C<=> - define command alias
- # Need to make these sane here.
- local $\ = '';
- local $, = '';
+Manipulates C<%alias> to add or list command aliases.
- # Search through the debugger's magical hash of subs.
- # If $nocheck is true, just print the sub name.
- # Otherwise, check it against the pattern. We then use
- # the XOR trick to reverse the condition as required.
- foreach $subname ( sort( keys %sub ) ) {
- if ( $Snocheck or $Srev ^ ( $subname =~ /$Spatt/ ) ) {
- print $OUT $subname, "\n";
- }
- }
- next CMD;
- }
+=head4 C<source> - read commands from a file.
- return;
-}
+Opens a lexical filehandle and stacks it on C<@cmdfhs>; C<DB::readline> will
+pick it up.
-sub _handle_V_command_and_X_command {
- my $self = shift;
+=head4 C<enable> C<disable> - enable or disable breakpoints
- $DB::cmd =~ s/^X\b/V $DB::package/;
+This enables or disables breakpoints.
- # Bare V commands get the currently-being-debugged package
- # added.
- if ($self->_is_full('V')) {
- $DB::cmd = "V $DB::package";
- }
+=head4 C<save> - send current history to a file
- # V - show variables in package.
- if (my ($new_packname, $new_vars_str) =
- $DB::cmd =~ /\AV\b\s*(\S+)\s*(.*)/) {
+Takes the complete history, (not the shrunken version you see with C<H>),
+and saves it to the given filename, so it can be replayed using C<source>.
- # Save the currently selected filehandle and
- # force output to debugger's filehandle (dumpvar
- # just does "print" for output).
- my $savout = select($OUT);
+Note that all C<^(save|source)>'s are commented out with a view to minimise recursion.
- # Grab package name and variables to dump.
- $packname = $new_packname;
- my @vars = split( ' ', $new_vars_str );
+=head4 C<R> - restart
- # If main::dumpvar isn't here, get it.
- do 'dumpvar.pl' || die $@ unless defined &main::dumpvar;
- if ( defined &main::dumpvar ) {
+Restart the debugger session.
- # We got it. Turn off subroutine entry/exit messages
- # for the moment, along with return values.
- local $frame = 0;
- local $doret = -2;
+=head4 C<rerun> - rerun the current session
- # must detect sigpipe failures - not catching
- # then will cause the debugger to die.
- eval {
- main::dumpvar(
- $packname,
- defined $option{dumpDepth}
- ? $option{dumpDepth}
- : -1, # assume -1 unless specified
- @vars
- );
- };
+Return to any given position in the B<true>-history list
- # The die doesn't need to include the $@, because
- # it will automatically get propagated for us.
- if ($@) {
- die unless $@ =~ /dumpvar print failed/;
- }
- } ## end if (defined &main::dumpvar)
- else {
+=head4 C<|, ||> - pipe output through the pager.
- # Couldn't load dumpvar.
- print $OUT "dumpvar.pl not available.\n";
- }
+For C<|>, we save C<OUT> (the debugger's output filehandle) and C<STDOUT>
+(the program's standard output). For C<||>, we only save C<OUT>. We open a
+pipe to the pager (restoring the output filehandles if this fails). If this
+is the C<|> command, we also set up a C<SIGPIPE> handler which will simply
+set C<$signal>, sending us back into the debugger.
- # Restore the output filehandle, and go round again.
- select($savout);
- next CMD;
- }
+We then trim off the pipe symbols and C<redo> the command loop at the
+C<PIPE> label, causing us to evaluate the command in C<$cmd> without
+reading another.
- return;
-}
+=cut
-sub _handle_dash_command {
- my $self = shift;
+ # || - run command in the pager, with output to DB::OUT.
+ _DB__handle_run_command_in_pager_command($obj);
- if ($self->_is_full('-')) {
+=head3 END OF COMMAND PARSING
- # back up by a window; go to 1 if back too far.
- $start -= $incr + $window + 1;
- $start = 1 if $start <= 0;
- $incr = $window - 1;
+Anything left in C<$cmd> at this point is a Perl expression that we want to
+evaluate. We'll always evaluate in the user's context, and fully qualify
+any variables we might want to address in the C<DB> package.
- # Generate and execute a "l +" command (handled below).
- $DB::cmd = 'l ' . ($start) . '+';
- redo CMD;
- }
- return;
-}
+=cut
-sub _n_or_s_commands_generic {
- my ($self, $new_val) = @_;
- # n - next
- next CMD if DB::_DB__is_finished();
+ } # PIPE:
- # Single step, but don't enter subs.
- $single = $new_val;
+ # trace an expression
+ $cmd =~ s/^t\s/\$DB::trace |= 1;\n/;
- # Save for empty command (repeat last).
- $laststep = $DB::cmd;
- last CMD;
-}
+ # Make sure the flag that says "the debugger's running" is
+ # still on, to make sure we get control again.
+ $evalarg = "\$^D = \$^D | \$DB::db_stop;\n$cmd";
-sub _n_or_s {
- my ($self, $letter, $new_val) = @_;
+ # Run *our* eval that executes in the caller's context.
+ # The &-call is here to ascertain the mutability of @_.
+ &DB::eval;
- if ($self->_is_full($letter)) {
- $self->_n_or_s_commands_generic($new_val);
- }
- else {
- $self->_n_or_s_and_arg_commands_generic($letter, $new_val);
- }
+ # Turn off the one-time-dump stuff now.
+ if ($onetimeDump) {
+ $onetimeDump = undef;
+ $onetimedumpDepth = undef;
+ }
+ elsif ( $term_pid == $$ ) {
+ eval { # May run under miniperl, when not available...
+ STDOUT->flush();
+ STDERR->flush();
+ };
- return;
-}
+ # XXX If this is the master pid, print a newline.
+ print {$OUT} "\n";
+ }
+ } ## end while (($term || &setterm...
-sub _handle_n_command {
- my $self = shift;
+=head3 POST-COMMAND PROCESSING
- return $self->_n_or_s('n', 2);
-}
+After each command, we check to see if the command output was piped anywhere.
+If so, we go through the necessary code to unhook the pipe and go back to
+our standard filehandles for input and output.
-sub _handle_s_command {
- my $self = shift;
+=cut
- return $self->_n_or_s('s', 1);
-}
+ continue { # CMD:
+ _DB__at_end_of_every_command($obj);
+ } # CMD:
-sub _handle_r_command {
- my $self = shift;
+=head3 COMMAND LOOP TERMINATION
- # r - return from the current subroutine.
- if ($self->_is_full('r')) {
+When commands have finished executing, we come here. If the user closed the
+input filehandle, we turn on C<$fall_off_end> to emulate a C<q> command. We
+evaluate any post-prompt items. We restore C<$@>, C<$!>, C<$^E>, C<$,>, C<$/>,
+C<$\>, and C<$^W>, and return a null list as expected by the Perl interpreter.
+The interpreter will then execute the next line and then return control to us
+again.
- # Can't do anything if the program's over.
- next CMD if DB::_DB__is_finished();
+=cut
- # Turn on stack trace.
- $stack[$stack_depth] |= 1;
+ # No more commands? Quit.
+ $fall_off_end = 1 unless defined $cmd; # Emulate 'q' on EOF
- # Print return value unless the stack is empty.
- $doret = $option{PrintRet} ? $stack_depth - 1 : -2;
- last CMD;
- }
+ # Evaluate post-prompt commands.
+ foreach $evalarg (@$post) {
+ # The &-call is here to ascertain the mutability of @_.
+ &DB::eval;
+ }
+ } # if ($single || $signal)
- return;
-}
+ # Put the user's globals back where you found them.
+ ( $@, $!, $^E, $,, $/, $\, $^W ) = @saved;
+ ();
+} ## end sub DB
-sub _handle_T_command {
- my $self = shift;
+# Because DB::Obj is used above,
+#
+# my $obj = DB::Obj->new(
+#
+# The following package declaration must come before that,
+# or else runtime errors will occur with
+#
+# PERLDB_OPTS="autotrace nonstop"
+#
+# ( rt#116771 )
+BEGIN {
- if ($self->_is_full('T')) {
- DB::print_trace( $OUT, 1 ); # skip DB
- next CMD;
- }
+package DB::Obj;
- return;
-}
+sub new {
+ my $class = shift;
-sub _handle_w_command {
- my $self = shift;
+ my $self = bless {}, $class;
- DB::cmd_w( 'w', $self->cmd_args() );
- next CMD;
+ $self->_init(@_);
- return;
+ return $self;
}
-sub _handle_W_command {
- my $self = shift;
+sub _init {
+ my ($self, $args) = @_;
- if (my $arg = $self->cmd_args) {
- DB::cmd_W( 'W', $arg );
- next CMD;
- }
+ %{$self} = (%$self, %$args);
return;
}
-sub _handle_rc_recall_command {
- my $self = shift;
-
- # $rc - recall command.
- if (my ($minus, $arg) = $DB::cmd =~ m#\A$rc+\s*(-)?(\d+)?\z#) {
-
- # No arguments, take one thing off history.
- pop(@hist) if length($DB::cmd) > 1;
+{
+ no strict 'refs';
+ foreach my $slot_name (qw(
+ after explicit_stop infix pat piped position prefix selected cmd_verb
+ cmd_args
+ )) {
+ my $slot = $slot_name;
+ *{$slot} = sub {
+ my $self = shift;
- # Relative (- found)?
- # Y - index back from most recent (by 1 if bare minus)
- # N - go to that particular command slot or the last
- # thing if nothing following.
+ if (@_) {
+ ${ $self->{$slot} } = shift;
+ }
- $self->cmd_verb(
- scalar($minus ? ( $#hist - ( $arg || 1 ) ) : ( $arg || $#hist ))
- );
+ return ${ $self->{$slot} };
+ };
- # Pick out the command desired.
- $DB::cmd = $hist[$self->cmd_verb];
+ *{"append_to_$slot"} = sub {
+ my $self = shift;
+ my $s = shift;
- # Print the command to be executed and restart the loop
- # with that command in the buffer.
- print {$OUT} $DB::cmd, "\n";
- redo CMD;
+ return $self->$slot($self->$slot . $s);
+ };
}
-
- return;
}
-sub _handle_rc_search_history_command {
+sub _DB_on_init__initialize_globals
+{
my $self = shift;
- # $rc pattern $rc - find a command in the history.
- if (my ($arg) = $DB::cmd =~ /\A$rc([^$rc].*)\z/) {
-
- # Create the pattern to use.
- my $pat = "^$arg";
- $self->pat($pat);
+ # Check for whether we should be running continuously or not.
+ # _After_ the perl program is compiled, $single is set to 1:
+ if ( $single and not $second_time++ ) {
- # Toss off last entry if length is >1 (and it always is).
- pop(@hist) if length($DB::cmd) > 1;
+ # Options say run non-stop. Run until we get an interrupt.
+ if ($runnonstop) { # Disable until signal
+ # If there's any call stack in place, turn off single
+ # stepping into subs throughout the stack.
+ for my $i (0 .. $stack_depth) {
+ $stack[ $i ] &= ~1;
+ }
- my $i;
+ # And we are now no longer in single-step mode.
+ $single = 0;
- # Look backward through the history.
- SEARCH_HIST:
- for ( $i = $#hist ; $i ; --$i ) {
- # Stop if we find it.
- last SEARCH_HIST if $hist[$i] =~ /$pat/;
- }
+ # If we simply returned at this point, we wouldn't get
+ # the trace info. Fall on through.
+ # return;
+ } ## end if ($runnonstop)
- if ( !$i ) {
+ elsif ($ImmediateStop) {
- # Never found it.
- print $OUT "No such command!\n\n";
- next CMD;
+ # We are supposed to stop here; XXX probably a break.
+ $ImmediateStop = 0; # We've processed it; turn it off
+ $signal = 1; # Simulate an interrupt to force
+ # us into the command loop
}
+ } ## end if ($single and not $second_time...
- # Found it. Put it in the buffer, print it, and process it.
- $DB::cmd = $hist[$i];
- print $OUT $DB::cmd, "\n";
- redo CMD;
- }
+ # If we're in single-step mode, or an interrupt (real or fake)
+ # has occurred, turn off non-stop mode.
+ $runnonstop = 0 if $single or $signal;
return;
}
-sub _handle_H_command {
- my $self = shift;
+sub _my_print_lineinfo
+{
+ my ($self, $i, $incr_pos) = @_;
- if ($self->cmd_args =~ m#\A\*#) {
- @hist = @truehist = ();
- print $OUT "History cleansed\n";
- next CMD;
+ if ($frame) {
+ # Print it indented if tracing is on.
+ DB::print_lineinfo( ' ' x $stack_depth,
+ "$i:\t$DB::dbline[$i]" . $self->after );
}
+ else {
+ DB::depth_print_lineinfo($self->explicit_stop, $incr_pos);
+ }
+}
- if (my ($num) = $self->cmd_args =~ /\A(?:-(\d+))?/) {
-
- # Anything other than negative numbers is ignored by
- # the (incorrect) pattern, so this test does nothing.
- $end = $num ? ( $#hist - $num ) : 0;
+sub _curr_line {
+ return $DB::dbline[$line];
+}
- # Set to the minimum if less than zero.
- $hist = 0 if $hist < 0;
+sub _is_full {
+ my ($self, $letter) = @_;
- # Start at the end of the array.
- # Stay in while we're still above the ending value.
- # Tick back by one each time around the loop.
- my $i;
+ return ($DB::cmd eq $letter);
+}
- for ( $i = $#hist ; $i > $end ; $i-- ) {
+sub _DB__grab_control
+{
+ my $self = shift;
- # Print the command unless it has no arguments.
- print $OUT "$i: ", $hist[$i], "\n"
- unless $hist[$i] =~ /^.?$/;
- }
+ # Yes, grab control.
+ if ($slave_editor) {
- next CMD;
+ # Tell the editor to update its position.
+ $self->position("\032\032${DB::filename}:$line:0\n");
+ DB::print_lineinfo($self->position());
}
- return;
-}
+=pod
-sub _handle_doc_command {
- my $self = shift;
+Special check: if we're in package C<DB::fake>, we've gone through the
+C<END> block at least once. We set up everything so that we can continue
+to enter commands and have a valid context to be in.
- # man, perldoc, doc - show manual pages.
- if (my ($man_page)
- = $DB::cmd =~ /\A(?:man|(?:perl)?doc)\b(?:\s+([^(]*))?\z/) {
- DB::runman($man_page);
- next CMD;
- }
+=cut
- return;
-}
+ elsif ( $DB::package eq 'DB::fake' ) {
-sub _handle_p_command {
- my $self = shift;
+ # Fallen off the end already.
+ if (!$DB::term) {
+ DB::setterm();
+ }
- my $print_cmd = 'print {$DB::OUT} ';
- # p - print (no args): print $_.
- if ($self->_is_full('p')) {
- $DB::cmd = $print_cmd . '$_';
- }
- else {
- # p - print the given expression.
- $DB::cmd =~ s/\Ap\b/$print_cmd /;
- }
+ DB::print_help(<<EOP);
+Debugged program terminated. Use B<q> to quit or B<R> to restart,
+use B<o> I<inhibit_exit> to avoid stopping after program termination,
+B<h q>, B<h R> or B<h o> to get additional info.
+EOP
+
+ $DB::package = 'main';
+ $DB::usercontext = DB::_calc_usercontext($DB::package);
+ } ## end elsif ($package eq 'DB::fake')
- return;
-}
+=pod
-sub _handle_equal_sign_command {
- my $self = shift;
+If the program hasn't finished executing, we scan forward to the
+next executable line, print that out, build the prompt from the file and line
+number information, and print that.
- if ($DB::cmd =~ s/\A=\s*//) {
- my @keys;
- if ( length $DB::cmd == 0 ) {
+=cut
- # No args, get current aliases.
- @keys = sort keys %alias;
- }
- elsif ( my ( $k, $v ) = ( $DB::cmd =~ /^(\S+)\s+(\S.*)/ ) ) {
+ else {
- # Creating a new alias. $k is alias name, $v is
- # alias value.
- # can't use $_ or kill //g state
- for my $x ( $k, $v ) {
+ # Still somewhere in the midst of execution. Set up the
+ # debugger prompt.
+ $DB::sub =~ s/\'/::/; # Swap Perl 4 package separators (') to
+ # Perl 5 ones (sorry, we don't print Klingon
+ #module names)
- # Escape "alarm" characters.
- $x =~ s/\a/\\a/g;
- }
+ $self->prefix($DB::sub =~ /::/ ? "" : ($DB::package . '::'));
+ $self->append_to_prefix( "$DB::sub(${DB::filename}:" );
+ $self->after( $self->_curr_line =~ /\n$/ ? '' : "\n" );
- # Substitute key for value, using alarm chars
- # as separators (which is why we escaped them in
- # the command).
- $alias{$k} = "s\a$k\a$v\a";
+ # Break up the prompt if it's really long.
+ if ( length($self->prefix()) > 30 ) {
+ $self->position($self->prefix . "$line):\n$line:\t" . $self->_curr_line . $self->after);
+ $self->prefix("");
+ $self->infix(":\t");
+ }
+ else {
+ $self->infix("):\t");
+ $self->position(
+ $self->prefix . $line. $self->infix
+ . $self->_curr_line . $self->after
+ );
+ }
- # Turn off standard warn and die behavior.
- local $SIG{__DIE__};
- local $SIG{__WARN__};
+ # Print current line info, indenting if necessary.
+ $self->_my_print_lineinfo($line, $self->position);
- # Is it valid Perl?
- unless ( eval "sub { s\a$k\a$v\a }; 1" ) {
+ my $i;
+ my $line_i = sub { return $DB::dbline[$i]; };
- # Nope. Bad alias. Say so and get out.
- print $OUT "Can't alias $k to $v: $@\n";
- delete $alias{$k};
- next CMD;
- }
+ # Scan forward, stopping at either the end or the next
+ # unbreakable line.
+ for ( $i = $line + 1 ; $i <= $DB::max && $line_i->() == 0 ; ++$i )
+ { #{ vi
- # We'll only list the new one.
- @keys = ($k);
- } ## end elsif (my ($k, $v) = ($DB::cmd...
+ # Drop out on null statements, block closers, and comments.
+ last if $line_i->() =~ /^\s*[\;\}\#\n]/;
- # The argument is the alias to list.
- else {
- @keys = ($DB::cmd);
- }
+ # Drop out if the user interrupted us.
+ last if $signal;
- # List aliases.
- for my $k (@keys) {
+ # Append a newline if the line doesn't have one. Can happen
+ # in eval'ed text, for instance.
+ $self->after( $line_i->() =~ /\n$/ ? '' : "\n" );
- # Messy metaquoting: Trim the substitution code off.
- # We use control-G as the delimiter because it's not
- # likely to appear in the alias.
- if ( ( my $v = $alias{$k} ) =~ s\as\a$k\a(.*)\a$\a1\a ) {
+ # Next executable line.
+ my $incr_pos = $self->prefix . $i . $self->infix . $line_i->()
+ . $self->after;
+ $self->append_to_position($incr_pos);
+ $self->_my_print_lineinfo($i, $incr_pos);
+ } ## end for ($i = $line + 1 ; $i...
+ } ## end else [ if ($slave_editor)
- # Print the alias.
- print $OUT "$k\t= $1\n";
- }
- elsif ( defined $alias{$k} ) {
+ return;
+}
- # Couldn't trim it off; just print the alias code.
- print $OUT "$k\t$alias{$k}\n";
- }
- else {
+sub _handle_t_command {
+ my $self = shift;
- # No such, dude.
- print "No alias for $k\n";
- }
- } ## end for my $k (@keys)
+ my $levels = $self->cmd_args();
+
+ if ((!length($levels)) or ($levels !~ /\D/)) {
+ $trace ^= 1;
+ local $\ = '';
+ $DB::trace_to_depth = $levels ? $stack_depth + $levels : 1E9;
+ print {$OUT} "Trace = "
+ . ( ( $trace & 1 )
+ ? ( $levels ? "on (to level $DB::trace_to_depth)" : "on" )
+ : "off" ) . "\n";
next CMD;
}
return;
}
-sub _handle_source_command {
+
+sub _handle_S_command {
my $self = shift;
- # source - read commands from a file (or pipe!) and execute.
- if (my $sourced_fn = $self->cmd_args) {
- if ( open my $fh, $sourced_fn ) {
+ if (my ($print_all_subs, $should_reverse, $Spatt)
+ = $self->cmd_args =~ /\A((!)?(.+))?\z/) {
+ # $Spatt is the pattern (if any) to use.
+ # Reverse scan?
+ my $Srev = defined $should_reverse;
+ # No args - print all subs.
+ my $Snocheck = !defined $print_all_subs;
- # Opened OK; stick it in the list of file handles.
- push @cmdfhs, $fh;
- }
- else {
+ # Need to make these sane here.
+ local $\ = '';
+ local $, = '';
- # Couldn't open it.
- DB::_db_warn("Can't execute '$sourced_fn': $!\n");
+ # Search through the debugger's magical hash of subs.
+ # If $nocheck is true, just print the sub name.
+ # Otherwise, check it against the pattern. We then use
+ # the XOR trick to reverse the condition as required.
+ foreach $subname ( sort( keys %sub ) ) {
+ if ( $Snocheck or $Srev ^ ( $subname =~ /$Spatt/ ) ) {
+ print $OUT $subname, "\n";
+ }
}
next CMD;
}
return;
}
-sub _handle_enable_disable_commands {
+sub _handle_V_command_and_X_command {
my $self = shift;
- my $which_cmd = $self->cmd_verb;
- my $position = $self->cmd_args;
+ $DB::cmd =~ s/^X\b/V $DB::package/;
- if ($position !~ /\s/) {
- my ($fn, $line_num);
- if ($position =~ m{\A\d+\z})
- {
- $fn = $DB::filename;
- $line_num = $position;
- }
- elsif (my ($new_fn, $new_line_num)
- = $position =~ m{\A(.*):(\d+)\z}) {
- ($fn, $line_num) = ($new_fn, $new_line_num);
- }
- else
- {
- DB::_db_warn("Wrong spec for enable/disable argument.\n");
- }
+ # Bare V commands get the currently-being-debugged package
+ # added.
+ if ($self->_is_full('V')) {
+ $DB::cmd = "V $DB::package";
+ }
- if (defined($fn)) {
- if (DB::_has_breakpoint_data_ref($fn, $line_num)) {
- DB::_set_breakpoint_enabled_status($fn, $line_num,
- ($which_cmd eq 'enable' ? 1 : '')
+ # V - show variables in package.
+ if (my ($new_packname, $new_vars_str) =
+ $DB::cmd =~ /\AV\b\s*(\S+)\s*(.*)/) {
+
+ # Save the currently selected filehandle and
+ # force output to debugger's filehandle (dumpvar
+ # just does "print" for output).
+ my $savout = select($OUT);
+
+ # Grab package name and variables to dump.
+ $packname = $new_packname;
+ my @vars = split( ' ', $new_vars_str );
+
+ # If main::dumpvar isn't here, get it.
+ do 'dumpvar.pl' || die $@ unless defined &main::dumpvar;
+ if ( defined &main::dumpvar ) {
+
+ # We got it. Turn off subroutine entry/exit messages
+ # for the moment, along with return values.
+ local $frame = 0;
+ local $doret = -2;
+
+ # must detect sigpipe failures - not catching
+ # then will cause the debugger to die.
+ eval {
+ main::dumpvar(
+ $packname,
+ defined $option{dumpDepth}
+ ? $option{dumpDepth}
+ : -1, # assume -1 unless specified
+ @vars
);
+ };
+
+ # The die doesn't need to include the $@, because
+ # it will automatically get propagated for us.
+ if ($@) {
+ die unless $@ =~ /dumpvar print failed/;
}
- else {
- DB::_db_warn("No breakpoint set at ${fn}:${line_num}\n");
- }
+ } ## end if (defined &main::dumpvar)
+ else {
+
+ # Couldn't load dumpvar.
+ print $OUT "dumpvar.pl not available.\n";
}
+ # Restore the output filehandle, and go round again.
+ select($savout);
next CMD;
}
return;
}
-sub _handle_save_command {
+sub _handle_dash_command {
my $self = shift;
- if (my $new_fn = $self->cmd_args) {
- my $filename = $new_fn || '.perl5dbrc'; # default?
- if ( open my $fh, '>', $filename ) {
+ if ($self->_is_full('-')) {
- # chomp to remove extraneous newlines from source'd files
- chomp( my @truelist =
- map { m/\A\s*(save|source)/ ? "#$_" : $_ }
- @truehist );
- print {$fh} join( "\n", @truelist );
- print "commands saved in $filename\n";
- }
- else {
- DB::_db_warn("Can't save debugger commands in '$new_fn': $!\n");
- }
- next CMD;
+ # back up by a window; go to 1 if back too far.
+ $start -= $incr + $window + 1;
+ $start = 1 if $start <= 0;
+ $incr = $window - 1;
+
+ # Generate and execute a "l +" command (handled below).
+ $DB::cmd = 'l ' . ($start) . '+';
+ redo CMD;
}
+ return;
+}
+
+sub _n_or_s_commands_generic {
+ my ($self, $new_val) = @_;
+ # n - next
+ next CMD if DB::_DB__is_finished();
- return;
+ # Single step, but don't enter subs.
+ $single = $new_val;
+
+ # Save for empty command (repeat last).
+ $laststep = $DB::cmd;
+ last CMD;
}
-sub _n_or_s_and_arg_commands_generic {
+sub _n_or_s {
my ($self, $letter, $new_val) = @_;
- # s - single-step. Remember the last command was 's'.
- if ($DB::cmd =~ s#\A\Q$letter\E\s#\$DB::single = $new_val;\n#) {
- $laststep = $letter;
+ if ($self->_is_full($letter)) {
+ $self->_n_or_s_commands_generic($new_val);
+ }
+ else {
+ $self->_n_or_s_and_arg_commands_generic($letter, $new_val);
}
return;
}
-sub _handle_sh_command {
+sub _handle_n_command {
my $self = shift;
- # $sh$sh - run a shell command (if it's all ASCII).
- # Can't run shell commands with Unicode in the debugger, hmm.
- my $my_cmd = $DB::cmd;
- if ($my_cmd =~ m#\A$sh#gms) {
+ return $self->_n_or_s('n', 2);
+}
- if ($my_cmd =~ m#\G\z#cgms) {
- # Run the user's shell. If none defined, run Bourne.
- # We resume execution when the shell terminates.
- DB::_db_system( $ENV{SHELL} || "/bin/sh" );
- next CMD;
- }
- elsif ($my_cmd =~ m#\G$sh\s*(.*)#cgms) {
- # System it.
- DB::_db_system($1);
- next CMD;
- }
- elsif ($my_cmd =~ m#\G\s*(.*)#cgms) {
- DB::_db_system( $ENV{SHELL} || "/bin/sh", "-c", $1 );
- next CMD;
- }
- }
+sub _handle_s_command {
+ my $self = shift;
+
+ return $self->_n_or_s('s', 1);
}
-sub _handle_x_command {
+sub _handle_r_command {
my $self = shift;
- if ($DB::cmd =~ s#\Ax\b# #) { # Remainder gets done by DB::eval()
- $onetimeDump = 'dump'; # main::dumpvar shows the output
+ # r - return from the current subroutine.
+ if ($self->_is_full('r')) {
- # handle special "x 3 blah" syntax XXX propagate
- # doc back to special variables.
- if ( $DB::cmd =~ s#\A\s*(\d+)(?=\s)# #) {
- $onetimedumpDepth = $1;
- }
+ # Can't do anything if the program's over.
+ next CMD if DB::_DB__is_finished();
+
+ # Turn on stack trace.
+ $stack[$stack_depth] |= 1;
+
+ # Print return value unless the stack is empty.
+ $doret = $option{PrintRet} ? $stack_depth - 1 : -2;
+ last CMD;
}
return;
}
-sub _handle_q_command {
+sub _handle_T_command {
my $self = shift;
- if ($self->_is_full('q')) {
- $fall_off_end = 1;
- DB::clean_ENV();
- exit $?;
+ if ($self->_is_full('T')) {
+ DB::print_trace( $OUT, 1 ); # skip DB
+ next CMD;
}
return;
}
-sub _handle_cmd_wrapper_commands {
+sub _handle_w_command {
my $self = shift;
- DB::cmd_wrapper( $self->cmd_verb, $self->cmd_args, $line );
+ DB::cmd_w( 'w', $self->cmd_args() );
next CMD;
+
+ return;
}
-sub _handle_special_char_cmd_wrapper_commands {
+sub _handle_W_command {
my $self = shift;
- # All of these commands were remapped in perl 5.8.0;
- # we send them off to the secondary dispatcher (see below).
- if (my ($cmd_letter, $my_arg) = $DB::cmd =~ /\A([<>\{]{1,2})\s*(.*)/so) {
- DB::cmd_wrapper( $cmd_letter, $my_arg, $line );
+ if (my $arg = $self->cmd_args) {
+ DB::cmd_W( 'W', $arg );
next CMD;
}
return;
}
-} ## end DB::Obj
+sub _handle_rc_recall_command {
+ my $self = shift;
-package DB;
+ # $rc - recall command.
+ if (my ($minus, $arg) = $DB::cmd =~ m#\A$rc+\s*(-)?(\d+)?\z#) {
-# The following code may be executed now:
-# BEGIN {warn 4}
+ # No arguments, take one thing off history.
+ pop(@hist) if length($DB::cmd) > 1;
-=head2 sub
+ # Relative (- found)?
+ # Y - index back from most recent (by 1 if bare minus)
+ # N - go to that particular command slot or the last
+ # thing if nothing following.
-C<sub> is called whenever a subroutine call happens in the program being
-debugged. The variable C<$DB::sub> contains the name of the subroutine
-being called.
+ $self->cmd_verb(
+ scalar($minus ? ( $#hist - ( $arg || 1 ) ) : ( $arg || $#hist ))
+ );
-The core function of this subroutine is to actually call the sub in the proper
-context, capturing its output. This of course causes C<DB::DB> to get called
-again, repeating until the subroutine ends and returns control to C<DB::sub>
-again. Once control returns, C<DB::sub> figures out whether or not to dump the
-return value, and returns its captured copy of the return value as its own
-return value. The value then feeds back into the program being debugged as if
-C<DB::sub> hadn't been there at all.
+ # Pick out the command desired.
+ $DB::cmd = $hist[$self->cmd_verb];
-C<sub> does all the work of printing the subroutine entry and exit messages
-enabled by setting C<$frame>. It notes what sub the autoloader got called for,
-and also prints the return value if needed (for the C<r> command and if
-the 16 bit is set in C<$frame>).
+ # Print the command to be executed and restart the loop
+ # with that command in the buffer.
+ print {$OUT} $DB::cmd, "\n";
+ redo CMD;
+ }
-It also tracks the subroutine call depth by saving the current setting of
-C<$single> in the C<@stack> package global; if this exceeds the value in
-C<$deep>, C<sub> automatically turns on printing of the current depth by
-setting the C<4> bit in C<$single>. In any case, it keeps the current setting
-of stop/don't stop on entry to subs set as it currently is set.
+ return;
+}
-=head3 C<caller()> support
+sub _handle_rc_search_history_command {
+ my $self = shift;
-If C<caller()> is called from the package C<DB>, it provides some
-additional data, in the following order:
+ # $rc pattern $rc - find a command in the history.
+ if (my ($arg) = $DB::cmd =~ /\A$rc([^$rc].*)\z/) {
-=over 4
+ # Create the pattern to use.
+ my $pat = "^$arg";
+ $self->pat($pat);
-=item * C<$package>
+ # Toss off last entry if length is >1 (and it always is).
+ pop(@hist) if length($DB::cmd) > 1;
-The package name the sub was in
+ my $i;
-=item * C<$filename>
+ # Look backward through the history.
+ SEARCH_HIST:
+ for ( $i = $#hist ; $i ; --$i ) {
+ # Stop if we find it.
+ last SEARCH_HIST if $hist[$i] =~ /$pat/;
+ }
-The filename it was defined in
+ if ( !$i ) {
-=item * C<$line>
+ # Never found it.
+ print $OUT "No such command!\n\n";
+ next CMD;
+ }
-The line number it was defined on
+ # Found it. Put it in the buffer, print it, and process it.
+ $DB::cmd = $hist[$i];
+ print $OUT $DB::cmd, "\n";
+ redo CMD;
+ }
-=item * C<$subroutine>
+ return;
+}
-The subroutine name; C<(eval)> if an C<eval>().
+sub _handle_H_command {
+ my $self = shift;
-=item * C<$hasargs>
+ if ($self->cmd_args =~ m#\A\*#) {
+ @hist = @truehist = ();
+ print $OUT "History cleansed\n";
+ next CMD;
+ }
-1 if it has arguments, 0 if not
+ if (my ($num) = $self->cmd_args =~ /\A(?:-(\d+))?/) {
-=item * C<$wantarray>
+ # Anything other than negative numbers is ignored by
+ # the (incorrect) pattern, so this test does nothing.
+ $end = $num ? ( $#hist - $num ) : 0;
-1 if array context, 0 if scalar context
+ # Set to the minimum if less than zero.
+ $hist = 0 if $hist < 0;
-=item * C<$evaltext>
+ # Start at the end of the array.
+ # Stay in while we're still above the ending value.
+ # Tick back by one each time around the loop.
+ my $i;
-The C<eval>() text, if any (undefined for C<eval BLOCK>)
+ for ( $i = $#hist ; $i > $end ; $i-- ) {
+ print $OUT "$i: ", $hist[$i], "\n";
+ }
-=item * C<$is_require>
+ next CMD;
+ }
-frame was created by a C<use> or C<require> statement
+ return;
+}
-=item * C<$hints>
+sub _handle_doc_command {
+ my $self = shift;
+
+ # man, perldoc, doc - show manual pages.
+ if (my ($man_page)
+ = $DB::cmd =~ /\A(?:man|(?:perl)?doc)\b(?:\s+([^(]*))?\z/) {
+ DB::runman($man_page);
+ next CMD;
+ }
+
+ return;
+}
+
+sub _handle_p_command {
+ my $self = shift;
+
+ my $print_cmd = 'print {$DB::OUT} ';
+ # p - print (no args): print $_.
+ if ($self->_is_full('p')) {
+ $DB::cmd = $print_cmd . '$_';
+ }
+ else {
+ # p - print the given expression.
+ $DB::cmd =~ s/\Ap\b/$print_cmd /;
+ }
+
+ return;
+}
+
+sub _handle_equal_sign_command {
+ my $self = shift;
+
+ if ($DB::cmd =~ s/\A=\s*//) {
+ my @keys;
+ if ( length $DB::cmd == 0 ) {
+
+ # No args, get current aliases.
+ @keys = sort keys %alias;
+ }
+ elsif ( my ( $k, $v ) = ( $DB::cmd =~ /^(\S+)\s+(\S.*)/ ) ) {
+
+ # Creating a new alias. $k is alias name, $v is
+ # alias value.
+
+ # can't use $_ or kill //g state
+ for my $x ( $k, $v ) {
+
+ # Escape "alarm" characters.
+ $x =~ s/\a/\\a/g;
+ }
-pragma information; subject to change between versions
+ # Substitute key for value, using alarm chars
+ # as separators (which is why we escaped them in
+ # the command).
+ $alias{$k} = "s\a$k\a$v\a";
-=item * C<$bitmask>
+ # Turn off standard warn and die behavior.
+ local $SIG{__DIE__};
+ local $SIG{__WARN__};
-pragma information; subject to change between versions
+ # Is it valid Perl?
+ unless ( eval "sub { s\a$k\a$v\a }; 1" ) {
-=item * C<@DB::args>
+ # Nope. Bad alias. Say so and get out.
+ print $OUT "Can't alias $k to $v: $@\n";
+ delete $alias{$k};
+ next CMD;
+ }
-arguments with which the subroutine was invoked
+ # We'll only list the new one.
+ @keys = ($k);
+ } ## end elsif (my ($k, $v) = ($DB::cmd...
-=back
+ # The argument is the alias to list.
+ else {
+ @keys = ($DB::cmd);
+ }
-=cut
+ # List aliases.
+ for my $k (@keys) {
-use vars qw($deep);
+ # Messy metaquoting: Trim the substitution code off.
+ # We use control-G as the delimiter because it's not
+ # likely to appear in the alias.
+ if ( ( my $v = $alias{$k} ) =~ s\as\a$k\a(.*)\a$\a1\a ) {
-# We need to fully qualify the name ("DB::sub") to make "use strict;"
-# happy. -- Shlomi Fish
+ # Print the alias.
+ print $OUT "$k\t= $1\n";
+ }
+ elsif ( defined $alias{$k} ) {
-sub _indent_print_line_info {
- my ($offset, $str) = @_;
+ # Couldn't trim it off; just print the alias code.
+ print $OUT "$k\t$alias{$k}\n";
+ }
+ else {
- print_lineinfo( ' ' x ($stack_depth - $offset), $str);
+ # No such, dude.
+ print "No alias for $k\n";
+ }
+ } ## end for my $k (@keys)
+ next CMD;
+ }
return;
}
-sub _print_frame_message {
- my ($al) = @_;
+sub _handle_source_command {
+ my $self = shift;
- if ($frame) {
- if ($frame & 4) { # Extended frame entry message
- _indent_print_line_info(-1, "in ");
+ # source - read commands from a file (or pipe!) and execute.
+ if (my $sourced_fn = $self->cmd_args) {
+ if ( open my $fh, $sourced_fn ) {
- # Why -1? But it works! :-(
- # Because print_trace will call add 1 to it and then call
- # dump_trace; this results in our skipping -1+1 = 0 stack frames
- # in dump_trace.
- #
- # Now it's 0 because we extracted a function.
- print_trace( $LINEINFO, 0, 1, 1, "$sub$al" );
+ # Opened OK; stick it in the list of file handles.
+ push @cmdfhs, $fh;
}
else {
- _indent_print_line_info(-1, "entering $sub$al\n" );
+
+ # Couldn't open it.
+ DB::_db_warn("Can't execute '$sourced_fn': $!\n");
}
+ next CMD;
}
return;
}
-sub DB::sub {
- # Do not use a regex in this subroutine -> results in corrupted memory
- # See: [perl #66110]
-
- # lock ourselves under threads
- lock($DBGR);
-
- # Whether or not the autoloader was running, a scalar to put the
- # sub's return value in (if needed), and an array to put the sub's
- # return value in (if needed).
- my ( $al, $ret, @ret ) = "";
- if ($sub eq 'threads::new' && $ENV{PERL5DB_THREADED}) {
- print "creating new thread\n";
- }
-
- # If the last ten characters are '::AUTOLOAD', note we've traced
- # into AUTOLOAD for $sub.
- if ( length($sub) > 10 && substr( $sub, -10, 10 ) eq '::AUTOLOAD' ) {
- no strict 'refs';
- $al = " for $$sub" if defined $$sub;
- }
-
- # We stack the stack pointer and then increment it to protect us
- # from a situation that might unwind a whole bunch of call frames
- # at once. Localizing the stack pointer means that it will automatically
- # unwind the same amount when multiple stack frames are unwound.
- local $stack_depth = $stack_depth + 1; # Protect from non-local exits
-
- # Expand @stack.
- $#stack = $stack_depth;
-
- # Save current single-step setting.
- $stack[-1] = $single;
-
- # Turn off all flags except single-stepping.
- $single &= 1;
-
- # If we've gotten really deeply recursed, turn on the flag that will
- # make us stop with the 'deep recursion' message.
- $single |= 4 if $stack_depth == $deep;
-
- # If frame messages are on ...
+sub _handle_enable_disable_commands {
+ my $self = shift;
- _print_frame_message($al);
- # standard frame entry message
+ my $which_cmd = $self->cmd_verb;
+ my $position = $self->cmd_args;
- my $print_exit_msg = sub {
- # Check for exit trace messages...
- if ($frame & 2)
+ if ($position !~ /\s/) {
+ my ($fn, $line_num);
+ if ($position =~ m{\A\d+\z})
{
- if ($frame & 4) # Extended exit message
- {
- _indent_print_line_info(0, "out ");
- print_trace( $LINEINFO, 0, 1, 1, "$sub$al" );
- }
- else
- {
- _indent_print_line_info(0, "exited $sub$al\n" );
- }
+ $fn = $DB::filename;
+ $line_num = $position;
}
- return;
- };
-
- # Determine the sub's return type, and capture appropriately.
- if (wantarray) {
-
- # Called in array context. call sub and capture output.
- # DB::DB will recursively get control again if appropriate; we'll come
- # back here when the sub is finished.
+ elsif (my ($new_fn, $new_line_num)
+ = $position =~ m{\A(.*):(\d+)\z}) {
+ ($fn, $line_num) = ($new_fn, $new_line_num);
+ }
+ else
{
- no strict 'refs';
- @ret = &$sub;
+ DB::_db_warn("Wrong spec for enable/disable argument.\n");
}
- # Pop the single-step value back off the stack.
- $single |= $stack[ $stack_depth-- ];
-
- $print_exit_msg->();
-
- # Print the return info if we need to.
- if ( $doret eq $stack_depth or $frame & 16 ) {
+ if (defined($fn)) {
+ if (DB::_has_breakpoint_data_ref($fn, $line_num)) {
+ DB::_set_breakpoint_enabled_status($fn, $line_num,
+ ($which_cmd eq 'enable' ? 1 : '')
+ );
+ }
+ else {
+ DB::_db_warn("No breakpoint set at ${fn}:${line_num}\n");
+ }
+ }
- # Turn off output record separator.
- local $\ = '';
- my $fh = ( $doret eq $stack_depth ? $OUT : $LINEINFO );
+ next CMD;
+ }
- # Indent if we're printing because of $frame tracing.
- if ($frame & 16)
- {
- print {$fh} ' ' x $stack_depth;
- }
+ return;
+}
- # Print the return value.
- print {$fh} "list context return from $sub:\n";
- dumpit( $fh, \@ret );
+sub _handle_save_command {
+ my $self = shift;
- # And don't print it again.
- $doret = -2;
- } ## end if ($doret eq $stack_depth...
- # And we have to return the return value now.
- @ret;
- } ## end if (wantarray)
+ if (my $new_fn = $self->cmd_args) {
+ my $filename = $new_fn || '.perl5dbrc'; # default?
+ if ( open my $fh, '>', $filename ) {
- # Scalar context.
- else {
- if ( defined wantarray ) {
- no strict 'refs';
- # Save the value if it's wanted at all.
- $ret = &$sub;
+ # chomp to remove extraneous newlines from source'd files
+ chomp( my @truelist =
+ map { m/\A\s*(save|source)/ ? "#$_" : $_ }
+ @truehist );
+ print {$fh} join( "\n", @truelist );
+ print "commands saved in $filename\n";
}
else {
- no strict 'refs';
- # Void return, explicitly.
- &$sub;
- undef $ret;
+ DB::_db_warn("Can't save debugger commands in '$new_fn': $!\n");
}
+ next CMD;
+ }
- # Pop the single-step value off the stack.
- $single |= $stack[ $stack_depth-- ];
-
- # If we're doing exit messages...
- $print_exit_msg->();
-
- # If we are supposed to show the return value... same as before.
- if ( $doret eq $stack_depth or $frame & 16 and defined wantarray ) {
- local $\ = '';
- my $fh = ( $doret eq $stack_depth ? $OUT : $LINEINFO );
- print $fh ( ' ' x $stack_depth ) if $frame & 16;
- print $fh (
- defined wantarray
- ? "scalar context return from $sub: "
- : "void context return from $sub\n"
- );
- dumpit( $fh, $ret ) if defined wantarray;
- $doret = -2;
- } ## end if ($doret eq $stack_depth...
-
- # Return the appropriate scalar value.
- $ret;
- } ## end else [ if (wantarray)
-} ## end sub _sub
-
-sub lsub : lvalue {
-
- no strict 'refs';
-
- # lock ourselves under threads
- lock($DBGR);
+ return;
+}
- # Whether or not the autoloader was running, a scalar to put the
- # sub's return value in (if needed), and an array to put the sub's
- # return value in (if needed).
- my ( $al, $ret, @ret ) = "";
- if ($sub =~ /^threads::new$/ && $ENV{PERL5DB_THREADED}) {
- print "creating new thread\n";
- }
+sub _n_or_s_and_arg_commands_generic {
+ my ($self, $letter, $new_val) = @_;
- # If the last ten characters are C'::AUTOLOAD', note we've traced
- # into AUTOLOAD for $sub.
- if ( length($sub) > 10 && substr( $sub, -10, 10 ) eq '::AUTOLOAD' ) {
- $al = " for $$sub";
+ # s - single-step. Remember the last command was 's'.
+ if ($DB::cmd =~ s#\A\Q$letter\E\s#\$DB::single = $new_val;\n#) {
+ $laststep = $letter;
}
- # We stack the stack pointer and then increment it to protect us
- # from a situation that might unwind a whole bunch of call frames
- # at once. Localizing the stack pointer means that it will automatically
- # unwind the same amount when multiple stack frames are unwound.
- local $stack_depth = $stack_depth + 1; # Protect from non-local exits
+ return;
+}
- # Expand @stack.
- $#stack = $stack_depth;
+sub _handle_sh_command {
+ my $self = shift;
- # Save current single-step setting.
- $stack[-1] = $single;
+ # $sh$sh - run a shell command (if it's all ASCII).
+ # Can't run shell commands with Unicode in the debugger, hmm.
+ my $my_cmd = $DB::cmd;
+ if ($my_cmd =~ m#\A$sh#gms) {
- # Turn off all flags except single-stepping.
- $single &= 1;
+ if ($my_cmd =~ m#\G\z#cgms) {
+ # Run the user's shell. If none defined, run Bourne.
+ # We resume execution when the shell terminates.
+ DB::_db_system( $ENV{SHELL} || "/bin/sh" );
+ next CMD;
+ }
+ elsif ($my_cmd =~ m#\G$sh\s*(.*)#cgms) {
+ # System it.
+ DB::_db_system($1);
+ next CMD;
+ }
+ elsif ($my_cmd =~ m#\G\s*(.*)#cgms) {
+ DB::_db_system( $ENV{SHELL} || "/bin/sh", "-c", $1 );
+ next CMD;
+ }
+ }
+}
- # If we've gotten really deeply recursed, turn on the flag that will
- # make us stop with the 'deep recursion' message.
- $single |= 4 if $stack_depth == $deep;
+sub _handle_x_command {
+ my $self = shift;
- # If frame messages are on ...
- _print_frame_message($al);
+ if ($DB::cmd =~ s#\Ax\b# #) { # Remainder gets done by DB::eval()
+ $onetimeDump = 'dump'; # main::dumpvar shows the output
- # Pop the single-step value back off the stack.
- $single |= $stack[ $stack_depth-- ];
+ # handle special "x 3 blah" syntax XXX propagate
+ # doc back to special variables.
+ if ( $DB::cmd =~ s#\A\s*(\d+)(?=\s)# #) {
+ $onetimedumpDepth = $1;
+ }
+ }
- # call the original lvalue sub.
- &$sub;
+ return;
}
-# Abstracting common code from multiple places elsewhere:
-sub depth_print_lineinfo {
- my $always_print = shift;
+sub _handle_q_command {
+ my $self = shift;
+
+ if ($self->_is_full('q')) {
+ $fall_off_end = 1;
+ DB::clean_ENV();
+ exit $?;
+ }
- print_lineinfo( @_ ) if ($always_print or $stack_depth < $trace_to_depth);
+ return;
}
-=head1 EXTENDED COMMAND HANDLING AND THE COMMAND API
+sub _handle_cmd_wrapper_commands {
+ my $self = shift;
-In Perl 5.8.0, there was a major realignment of the commands and what they did,
-Most of the changes were to systematize the command structure and to eliminate
-commands that threw away user input without checking.
+ DB::cmd_wrapper( $self->cmd_verb, $self->cmd_args, $line );
+ next CMD;
+}
-The following sections describe the code added to make it easy to support
-multiple command sets with conflicting command names. This section is a start
-at unifying all command processing to make it simpler to develop commands.
+sub _handle_special_char_cmd_wrapper_commands {
+ my $self = shift;
-Note that all the cmd_[a-zA-Z] subroutines require the command name, a line
-number, and C<$dbline> (the current line) as arguments.
+ # All of these commands were remapped in perl 5.8.0;
+ # we send them off to the secondary dispatcher (see below).
+ if (my ($cmd_letter, $my_arg) = $DB::cmd =~ /\A([<>\{]{1,2})\s*(.*)/so) {
+ DB::cmd_wrapper( $cmd_letter, $my_arg, $line );
+ next CMD;
+ }
-Support functions in this section which have multiple modes of failure C<die>
-on error; the rest simply return a false value.
+ return;
+}
-The user-interface functions (all of the C<cmd_*> functions) just output
-error messages.
+} ## end DB::Obj
-=head2 C<%set>
+package DB;
-The C<%set> hash defines the mapping from command letter to subroutine
-name suffix.
+# The following code may be executed now:
+# BEGIN {warn 4}
-C<%set> is a two-level hash, indexed by set name and then by command name.
-Note that trying to set the CommandSet to C<foobar> simply results in the
-5.8.0 command set being used, since there's no top-level entry for C<foobar>.
+=head2 sub
-=cut
+C<sub> is called whenever a subroutine call happens in the program being
+debugged. The variable C<$DB::sub> contains the name of the subroutine
+being called.
-### The API section
+The core function of this subroutine is to actually call the sub in the proper
+context, capturing its output. This of course causes C<DB::DB> to get called
+again, repeating until the subroutine ends and returns control to C<DB::sub>
+again. Once control returns, C<DB::sub> figures out whether or not to dump the
+return value, and returns its captured copy of the return value as its own
+return value. The value then feeds back into the program being debugged as if
+C<DB::sub> hadn't been there at all.
-my %set = ( #
- 'pre580' => {
- 'a' => 'pre580_a',
- 'A' => 'pre580_null',
- 'b' => 'pre580_b',
- 'B' => 'pre580_null',
- 'd' => 'pre580_null',
- 'D' => 'pre580_D',
- 'h' => 'pre580_h',
- 'M' => 'pre580_null',
- 'O' => 'o',
- 'o' => 'pre580_null',
- 'v' => 'M',
- 'w' => 'v',
- 'W' => 'pre580_W',
- },
- 'pre590' => {
- '<' => 'pre590_prepost',
- '<<' => 'pre590_prepost',
- '>' => 'pre590_prepost',
- '>>' => 'pre590_prepost',
- '{' => 'pre590_prepost',
- '{{' => 'pre590_prepost',
- },
-);
+C<sub> does all the work of printing the subroutine entry and exit messages
+enabled by setting C<$frame>. It notes what sub the autoloader got called for,
+and also prints the return value if needed (for the C<r> command and if
+the 16 bit is set in C<$frame>).
-my %breakpoints_data;
+It also tracks the subroutine call depth by saving the current setting of
+C<$single> in the C<@stack> package global; if this exceeds the value in
+C<$deep>, C<sub> automatically turns on printing of the current depth by
+setting the C<4> bit in C<$single>. In any case, it keeps the current setting
+of stop/don't stop on entry to subs set as it currently is set.
-sub _has_breakpoint_data_ref {
- my ($filename, $line) = @_;
+=head3 C<caller()> support
- return (
- exists( $breakpoints_data{$filename} )
- and
- exists( $breakpoints_data{$filename}{$line} )
- );
-}
+If C<caller()> is called from the package C<DB>, it provides some
+additional data, in the following order:
-sub _get_breakpoint_data_ref {
- my ($filename, $line) = @_;
+=over 4
- return ($breakpoints_data{$filename}{$line} ||= +{});
-}
+=item * C<$package>
-sub _delete_breakpoint_data_ref {
- my ($filename, $line) = @_;
+The package name the sub was in
- delete($breakpoints_data{$filename}{$line});
- if (! scalar(keys( %{$breakpoints_data{$filename}} )) ) {
- delete($breakpoints_data{$filename});
- }
+=item * C<$filename>
- return;
-}
+The filename it was defined in
-sub _set_breakpoint_enabled_status {
- my ($filename, $line, $status) = @_;
+=item * C<$line>
- _get_breakpoint_data_ref($filename, $line)->{'enabled'} =
- ($status ? 1 : '')
- ;
+The line number it was defined on
- return;
-}
+=item * C<$subroutine>
-sub _enable_breakpoint_temp_enabled_status {
- my ($filename, $line) = @_;
+The subroutine name; C<(eval)> if an C<eval>().
- _get_breakpoint_data_ref($filename, $line)->{'temp_enabled'} = 1;
+=item * C<$hasargs>
- return;
-}
+1 if it has arguments, 0 if not
-sub _cancel_breakpoint_temp_enabled_status {
- my ($filename, $line) = @_;
+=item * C<$wantarray>
- my $ref = _get_breakpoint_data_ref($filename, $line);
+1 if array context, 0 if scalar context
- delete ($ref->{'temp_enabled'});
+=item * C<$evaltext>
- if (! %$ref) {
- _delete_breakpoint_data_ref($filename, $line);
- }
+The C<eval>() text, if any (undefined for C<eval BLOCK>)
- return;
-}
+=item * C<$is_require>
-sub _is_breakpoint_enabled {
- my ($filename, $line) = @_;
+frame was created by a C<use> or C<require> statement
- my $data_ref = _get_breakpoint_data_ref($filename, $line);
- return ($data_ref->{'enabled'} || $data_ref->{'temp_enabled'});
-}
+=item * C<$hints>
-=head2 C<cmd_wrapper()> (API)
+pragma information; subject to change between versions
-C<cmd_wrapper()> allows the debugger to switch command sets
-depending on the value of the C<CommandSet> option.
+=item * C<$bitmask>
-It tries to look up the command in the C<%set> package-level I<lexical>
-(which means external entities can't fiddle with it) and create the name of
-the sub to call based on the value found in the hash (if it's there). I<All>
-of the commands to be handled in a set have to be added to C<%set>; if they
-aren't found, the 5.8.0 equivalent is called (if there is one).
+pragma information; subject to change between versions
-This code uses symbolic references.
+=item * C<@DB::args>
-=cut
+arguments with which the subroutine was invoked
-sub cmd_wrapper {
- my $cmd = shift;
- my $line = shift;
- my $dblineno = shift;
+=back
- # Assemble the command subroutine's name by looking up the
- # command set and command name in %set. If we can't find it,
- # default to the older version of the command.
- my $call = 'cmd_'
- . ( $set{$CommandSet}{$cmd}
- || ( $cmd =~ /\A[<>{]+/o ? 'prepost' : $cmd ) );
+=cut
- # Call the command subroutine, call it by name.
- return __PACKAGE__->can($call)->( $cmd, $line, $dblineno );
-} ## end sub cmd_wrapper
+use vars qw($deep);
-=head3 C<cmd_a> (command)
+# We need to fully qualify the name ("DB::sub") to make "use strict;"
+# happy. -- Shlomi Fish
-The C<a> command handles pre-execution actions. These are associated with a
-particular line, so they're stored in C<%dbline>. We default to the current
-line if none is specified.
+sub _indent_print_line_info {
+ my ($offset, $str) = @_;
-=cut
+ print_lineinfo( ' ' x ($stack_depth - $offset), $str);
-sub cmd_a {
- my $cmd = shift;
- my $line = shift || ''; # [.|line] expr
- my $dbline = shift;
+ return;
+}
- # If it's dot (here), or not all digits, use the current line.
- $line =~ s/\A\./$dbline/;
+sub _print_frame_message {
+ my ($al) = @_;
- # Should be a line number followed by an expression.
- if ( my ($lineno, $expr) = $line =~ /^\s*(\d*)\s*(\S.+)/ ) {
+ if ($frame) {
+ if ($frame & 4) { # Extended frame entry message
+ _indent_print_line_info(-1, "in ");
- if (! length($lineno)) {
- $lineno = $dbline;
+ # Why -1? But it works! :-(
+ # Because print_trace will call add 1 to it and then call
+ # dump_trace; this results in our skipping -1+1 = 0 stack frames
+ # in dump_trace.
+ #
+ # Now it's 0 because we extracted a function.
+ print_trace( $LINEINFO, 0, 1, 1, "$sub$al" );
}
+ else {
+ _indent_print_line_info(-1, "entering $sub$al\n" );
+ }
+ }
- # If we have an expression ...
- if ( length $expr ) {
-
- # ... but the line isn't breakable, complain.
- if ( $dbline[$lineno] == 0 ) {
- print $OUT
- "Line $lineno($dbline[$lineno]) does not have an action?\n";
- }
- else {
+ return;
+}
- # It's executable. Record that the line has an action.
- $had_breakpoints{$filename} |= 2;
+sub DB::sub {
+ my ( $al, $ret, @ret ) = "";
- # Remove any action, temp breakpoint, etc.
- $dbline{$lineno} =~ s/\0[^\0]*//;
+ # We stack the stack pointer and then increment it to protect us
+ # from a situation that might unwind a whole bunch of call frames
+ # at once. Localizing the stack pointer means that it will automatically
+ # unwind the same amount when multiple stack frames are unwound.
+ local $stack_depth = $stack_depth + 1; # Protect from non-local exits
- # Add the action to the line.
- $dbline{$lineno} .= "\0" . action($expr);
+ {
+ # lock ourselves under threads
+ # While lock() permits recursive locks, there's two cases where it's bad
+ # that we keep a hold on the lock while we call the sub:
+ # - during cloning, Package::CLONE might be called in the context of the new
+ # thread, which will deadlock if we hold the lock across the threads::new call
+ # - for any function that waits any significant time
+ # This also deadlocks if the parent thread joins(), since holding the lock
+ # will prevent any child threads passing this point.
+ # So release the lock for the function call.
+ lock($DBGR);
- _set_breakpoint_enabled_status($filename, $lineno, 1);
- }
- } ## end if (length $expr)
- } ## end if ($line =~ /^\s*(\d*)\s*(\S.+)/)
- else {
+ # Whether or not the autoloader was running, a scalar to put the
+ # sub's return value in (if needed), and an array to put the sub's
+ # return value in (if needed).
+ if ($sub eq 'threads::new' && $ENV{PERL5DB_THREADED}) {
+ print "creating new thread\n";
+ }
- # Syntax wrong.
- print $OUT
- "Adding an action requires an optional lineno and an expression\n"
- ; # hint
- }
-} ## end sub cmd_a
+ # If the last ten characters are '::AUTOLOAD', note we've traced
+ # into AUTOLOAD for $sub.
+ if ( length($sub) > 10 && substr( $sub, -10, 10 ) eq '::AUTOLOAD' ) {
+ no strict 'refs';
+ $al = " for $$sub" if defined $$sub;
+ }
-=head3 C<cmd_A> (command)
+ # Expand @stack.
+ $#stack = $stack_depth;
-Delete actions. Similar to above, except the delete code is in a separate
-subroutine, C<delete_action>.
+ # Save current single-step setting.
+ $stack[-1] = $single;
-=cut
+ # Turn off all flags except single-stepping.
+ $single &= 1;
-sub cmd_A {
- my $cmd = shift;
- my $line = shift || '';
- my $dbline = shift;
+ # If we've gotten really deeply recursed, turn on the flag that will
+ # make us stop with the 'deep recursion' message.
+ $single |= 4 if $stack_depth == $deep;
- # Dot is this line.
- $line =~ s/^\./$dbline/;
+ # If frame messages are on ...
- # Call delete_action with a null param to delete them all.
- # The '1' forces the eval to be true. It'll be false only
- # if delete_action blows up for some reason, in which case
- # we print $@ and get out.
- if ( $line eq '*' ) {
- if (! eval { _delete_all_actions(); 1 }) {
- print {$OUT} $@;
- return;
- }
+ _print_frame_message($al);
}
- # There's a real line number. Pass it to delete_action.
- # Error trapping is as above.
- elsif ( $line =~ /^(\S.*)/ ) {
- if (! eval { delete_action($1); 1 }) {
- print {$OUT} $@;
- return;
- }
- }
+ # Determine the sub's return type, and capture appropriately.
+ if (wantarray) {
- # Swing and a miss. Bad syntax.
+ # Called in array context. call sub and capture output.
+ # DB::DB will recursively get control again if appropriate; we'll come
+ # back here when the sub is finished.
+ no strict 'refs';
+ @ret = &$sub;
+ }
+ elsif ( defined wantarray ) {
+ no strict 'refs';
+ # Save the value if it's wanted at all.
+ $ret = &$sub;
+ }
else {
- print $OUT
- "Deleting an action requires a line number, or '*' for all\n" ; # hint
+ no strict 'refs';
+ # Void return, explicitly.
+ &$sub;
+ undef $ret;
}
-} ## end sub cmd_A
-
-=head3 C<delete_action> (API)
-C<delete_action> accepts either a line number or C<undef>. If a line number
-is specified, we check for the line being executable (if it's not, it
-couldn't have had an action). If it is, we just take the action off (this
-will get any kind of an action, including breakpoints).
+ {
+ lock($DBGR);
-=cut
+ # Pop the single-step value back off the stack.
+ $single |= $stack[ $stack_depth-- ];
-sub _remove_action_from_dbline {
- my $i = shift;
+ if ($frame & 2) {
+ if ($frame & 4) { # Extended exit message
+ _indent_print_line_info(0, "out ");
+ print_trace( $LINEINFO, -1, 1, 1, "$sub$al" );
+ }
+ else {
+ _indent_print_line_info(0, "exited $sub$al\n" );
+ }
+ }
- $dbline{$i} =~ s/\0[^\0]*//; # \^a
- delete $dbline{$i} if $dbline{$i} eq '';
+ if (wantarray) {
+ # Print the return info if we need to.
+ if ( $doret eq $stack_depth or $frame & 16 ) {
- return;
-}
+ # Turn off output record separator.
+ local $\ = '';
+ my $fh = ( $doret eq $stack_depth ? $OUT : $LINEINFO );
-sub _delete_all_actions {
- print {$OUT} "Deleting all actions...\n";
+ # Indent if we're printing because of $frame tracing.
+ if ($frame & 16)
+ {
+ print {$fh} ' ' x $stack_depth;
+ }
- for my $file ( keys %had_breakpoints ) {
- local *dbline = $main::{ '_<' . $file };
- $max = $#dbline;
- my $was;
- for my $i (1 .. $max) {
- if ( defined $dbline{$i} ) {
- _remove_action_from_dbline($i);
- }
- }
+ # Print the return value.
+ print {$fh} "list context return from $sub:\n";
+ dumpit( $fh, \@ret );
- unless ( $had_breakpoints{$file} &= ~2 ) {
- delete $had_breakpoints{$file};
- }
+ # And don't print it again.
+ $doret = -2;
+ } ## end if ($doret eq $stack_depth...
+ # And we have to return the return value now.
+ @ret;
+ } ## end if (wantarray)
+ # Scalar context.
+ else {
+ # If we are supposed to show the return value... same as before.
+ if ( $doret eq $stack_depth or $frame & 16 and defined wantarray ) {
+ local $\ = '';
+ my $fh = ( $doret eq $stack_depth ? $OUT : $LINEINFO );
+ print $fh ( ' ' x $stack_depth ) if $frame & 16;
+ print $fh (
+ defined wantarray
+ ? "scalar context return from $sub: "
+ : "void context return from $sub\n"
+ );
+ dumpit( $fh, $ret ) if defined wantarray;
+ $doret = -2;
+ } ## end if ($doret eq $stack_depth...
+
+ # Return the appropriate scalar value.
+ $ret;
+ } ## end else [ if (wantarray)
}
+} ## end sub _sub
- return;
-}
+sub lsub : lvalue {
-sub delete_action {
- my $i = shift;
+ # We stack the stack pointer and then increment it to protect us
+ # from a situation that might unwind a whole bunch of call frames
+ # at once. Localizing the stack pointer means that it will automatically
+ # unwind the same amount when multiple stack frames are unwound.
+ local $stack_depth = $stack_depth + 1; # Protect from non-local exits
- if ( defined($i) ) {
- # Can there be one?
- die "Line $i has no action .\n" if $dbline[$i] == 0;
+ # Expand @stack.
+ $#stack = $stack_depth;
- # Nuke whatever's there.
- _remove_action_from_dbline($i);
- }
- else {
- _delete_all_actions();
+ # Save current single-step setting.
+ $stack[-1] = $single;
+
+ # Turn off all flags except single-stepping.
+ # Use local so the single-step value is popped back off the
+ # stack for us.
+ local $single = $single & 1;
+
+ no strict 'refs';
+ {
+ # lock ourselves under threads
+ lock($DBGR);
+
+ # Whether or not the autoloader was running, a scalar to put the
+ # sub's return value in (if needed), and an array to put the sub's
+ # return value in (if needed).
+ my ( $al, $ret, @ret ) = "";
+ if ($sub =~ /^threads::new$/ && $ENV{PERL5DB_THREADED}) {
+ print "creating new thread\n";
+ }
+
+ # If the last ten characters are C'::AUTOLOAD', note we've traced
+ # into AUTOLOAD for $sub.
+ if ( length($sub) > 10 && substr( $sub, -10, 10 ) eq '::AUTOLOAD' ) {
+ $al = " for $$sub";
+ }
+
+ # If we've gotten really deeply recursed, turn on the flag that will
+ # make us stop with the 'deep recursion' message.
+ $single |= 4 if $stack_depth == $deep;
+
+ # If frame messages are on ...
+ _print_frame_message($al);
}
-}
-=head3 C<cmd_b> (command)
+ # call the original lvalue sub.
+ &$sub;
+}
-Set breakpoints. Since breakpoints can be set in so many places, in so many
-ways, conditionally or not, the breakpoint code is kind of complex. Mostly,
-we try to parse the command type, and then shuttle it off to an appropriate
-subroutine to actually do the work of setting the breakpoint in the right
-place.
+# Abstracting common code from multiple places elsewhere:
+sub depth_print_lineinfo {
+ my $always_print = shift;
-=cut
+ print_lineinfo( @_ ) if ($always_print or $stack_depth < $trace_to_depth);
+}
-sub cmd_b {
- my $cmd = shift;
- my $line = shift; # [.|line] [cond]
- my $dbline = shift;
+=head1 EXTENDED COMMAND HANDLING AND THE COMMAND API
- my $default_cond = sub {
- my $cond = shift;
- return length($cond) ? $cond : '1';
- };
+In Perl 5.8.0, there was a major realignment of the commands and what they did,
+Most of the changes were to systematize the command structure and to eliminate
+commands that threw away user input without checking.
- # Make . the current line number if it's there..
- $line =~ s/^\.(\s|\z)/$dbline$1/;
+The following sections describe the code added to make it easy to support
+multiple command sets with conflicting command names. This section is a start
+at unifying all command processing to make it simpler to develop commands.
- # No line number, no condition. Simple break on current line.
- if ( $line =~ /^\s*$/ ) {
- cmd_b_line( $dbline, 1 );
- }
+Note that all the cmd_[a-zA-Z] subroutines require the command name, a line
+number, and C<$dbline> (the current line) as arguments.
- # Break on load for a file.
- elsif ( my ($file) = $line =~ /^load\b\s*(.*)/ ) {
- $file =~ s/\s+\z//;
- cmd_b_load($file);
- }
+Support functions in this section which have multiple modes of failure C<die>
+on error; the rest simply return a false value.
- # b compile|postpone <some sub> [<condition>]
- # The interpreter actually traps this one for us; we just put the
- # necessary condition in the %postponed hash.
- elsif ( my ($action, $subname, $cond)
- = $line =~ /^(postpone|compile)\b\s*([':A-Za-z_][':\w]*)\s*(.*)/ ) {
+The user-interface functions (all of the C<cmd_*> functions) just output
+error messages.
- # De-Perl4-ify the name - ' separators to ::.
- $subname =~ s/'/::/g;
+=head2 C<%set>
- # Qualify it into the current package unless it's already qualified.
- $subname = "${package}::" . $subname unless $subname =~ /::/;
+The C<%set> hash defines the mapping from command letter to subroutine
+name suffix.
- # Add main if it starts with ::.
- $subname = "main" . $subname if substr( $subname, 0, 2 ) eq "::";
+C<%set> is a two-level hash, indexed by set name and then by command name.
+Note that trying to set the CommandSet to C<foobar> simply results in the
+5.8.0 command set being used, since there's no top-level entry for C<foobar>.
- # Save the break type for this sub.
- $postponed{$subname} = (($action eq 'postpone')
- ? ( "break +0 if " . $default_cond->($cond) )
- : "compile");
- } ## end elsif ($line =~ ...
- # b <filename>:<line> [<condition>]
- elsif (my ($filename, $line_num, $cond)
- = $line =~ /\A(\S+[^:]):(\d+)\s*(.*)/ms) {
- cmd_b_filename_line(
- $filename,
- $line_num,
- (length($cond) ? $cond : '1'),
- );
- }
- # b <sub name> [<condition>]
- elsif ( my ($new_subname, $new_cond) =
- $line =~ /^([':A-Za-z_][':\w]*(?:\[.*\])?)\s*(.*)/ ) {
+=cut
- #
- $subname = $new_subname;
- cmd_b_sub( $subname, $default_cond->($new_cond) );
- }
+### The API section
- # b <line> [<condition>].
- elsif ( my ($line_n, $cond) = $line =~ /^(\d*)\s*(.*)/ ) {
+my %set = ( #
+ 'pre580' => {
+ 'a' => 'pre580_a',
+ 'A' => 'pre580_null',
+ 'b' => 'pre580_b',
+ 'B' => 'pre580_null',
+ 'd' => 'pre580_null',
+ 'D' => 'pre580_D',
+ 'h' => 'pre580_h',
+ 'M' => 'pre580_null',
+ 'O' => 'o',
+ 'o' => 'pre580_null',
+ 'v' => 'M',
+ 'w' => 'v',
+ 'W' => 'pre580_W',
+ },
+ 'pre590' => {
+ '<' => 'pre590_prepost',
+ '<<' => 'pre590_prepost',
+ '>' => 'pre590_prepost',
+ '>>' => 'pre590_prepost',
+ '{' => 'pre590_prepost',
+ '{{' => 'pre590_prepost',
+ },
+);
- # Capture the line. If none, it's the current line.
- $line = $line_n || $dbline;
+my %breakpoints_data;
- # Break on line.
- cmd_b_line( $line, $default_cond->($cond) );
- }
+sub _has_breakpoint_data_ref {
+ my ($filename, $line) = @_;
- # Line didn't make sense.
- else {
- print "confused by line($line)?\n";
- }
+ return (
+ exists( $breakpoints_data{$filename} )
+ and
+ exists( $breakpoints_data{$filename}{$line} )
+ );
+}
- return;
-} ## end sub cmd_b
+sub _get_breakpoint_data_ref {
+ my ($filename, $line) = @_;
-=head3 C<break_on_load> (API)
+ return ($breakpoints_data{$filename}{$line} ||= +{});
+}
-We want to break when this file is loaded. Mark this file in the
-C<%break_on_load> hash, and note that it has a breakpoint in
-C<%had_breakpoints>.
+sub _delete_breakpoint_data_ref {
+ my ($filename, $line) = @_;
-=cut
+ delete($breakpoints_data{$filename}{$line});
+ if (! scalar(keys( %{$breakpoints_data{$filename}} )) ) {
+ delete($breakpoints_data{$filename});
+ }
-sub break_on_load {
- my $file = shift;
- $break_on_load{$file} = 1;
- $had_breakpoints{$file} |= 1;
+ return;
}
-=head3 C<report_break_on_load> (API)
-
-Gives us an array of filenames that are set to break on load. Note that
-only files with break-on-load are in here, so simply showing the keys
-suffices.
+sub _set_breakpoint_enabled_status {
+ my ($filename, $line, $status) = @_;
-=cut
+ _get_breakpoint_data_ref($filename, $line)->{'enabled'} =
+ ($status ? 1 : '')
+ ;
-sub report_break_on_load {
- sort keys %break_on_load;
+ return;
}
-=head3 C<cmd_b_load> (command)
+sub _enable_breakpoint_temp_enabled_status {
+ my ($filename, $line) = @_;
-We take the file passed in and try to find it in C<%INC> (which maps modules
-to files they came from). We mark those files for break-on-load via
-C<break_on_load> and then report that it was done.
+ _get_breakpoint_data_ref($filename, $line)->{'temp_enabled'} = 1;
-=cut
+ return;
+}
-sub cmd_b_load {
- my $file = shift;
- my @files;
+sub _cancel_breakpoint_temp_enabled_status {
+ my ($filename, $line) = @_;
- # This is a block because that way we can use a redo inside it
- # even without there being any looping structure at all outside it.
- {
+ my $ref = _get_breakpoint_data_ref($filename, $line);
- # Save short name and full path if found.
- push @files, $file;
- push @files, $::INC{$file} if $::INC{$file};
+ delete ($ref->{'temp_enabled'});
- # Tack on .pm and do it again unless there was a '.' in the name
- # already.
- $file .= '.pm', redo unless $file =~ /\./;
+ if (! %$ref) {
+ _delete_breakpoint_data_ref($filename, $line);
}
- # Do the real work here.
- break_on_load($_) for @files;
-
- # All the files that have break-on-load breakpoints.
- @files = report_break_on_load;
+ return;
+}
- # Normalize for the purposes of our printing this.
- local $\ = '';
- local $" = ' ';
- print $OUT "Will stop on load of '@files'.\n";
-} ## end sub cmd_b_load
+sub _is_breakpoint_enabled {
+ my ($filename, $line) = @_;
-=head3 C<$filename_error> (API package global)
+ my $data_ref = _get_breakpoint_data_ref($filename, $line);
+ return ($data_ref->{'enabled'} || $data_ref->{'temp_enabled'});
+}
-Several of the functions we need to implement in the API need to work both
-on the current file and on other files. We don't want to duplicate code, so
-C<$filename_error> is used to contain the name of the file that's being
-worked on (if it's not the current one).
+=head2 C<cmd_wrapper()> (API)
-We can now build functions in pairs: the basic function works on the current
-file, and uses C<$filename_error> as part of its error message. Since this is
-initialized to C<"">, no filename will appear when we are working on the
-current file.
+C<cmd_wrapper()> allows the debugger to switch command sets
+depending on the value of the C<CommandSet> option.
-The second function is a wrapper which does the following:
+It tries to look up the command in the C<%set> package-level I<lexical>
+(which means external entities can't fiddle with it) and create the name of
+the sub to call based on the value found in the hash (if it's there). I<All>
+of the commands to be handled in a set have to be added to C<%set>; if they
+aren't found, the 5.8.0 equivalent is called (if there is one).
-=over 4
+This code uses symbolic references.
-=item *
+=cut
-Localizes C<$filename_error> and sets it to the name of the file to be processed.
+sub cmd_wrapper {
+ my $cmd = shift;
+ my $line = shift;
+ my $dblineno = shift;
-=item *
+ # Assemble the command subroutine's name by looking up the
+ # command set and command name in %set. If we can't find it,
+ # default to the older version of the command.
+ my $call = 'cmd_'
+ . ( $set{$CommandSet}{$cmd}
+ || ( $cmd =~ /\A[<>{]+/o ? 'prepost' : $cmd ) );
-Localizes the C<*dbline> glob and reassigns it to point to the file we want to process.
+ # Call the command subroutine, call it by name.
+ return __PACKAGE__->can($call)->( $cmd, $line, $dblineno );
+} ## end sub cmd_wrapper
-=item *
+=head3 C<cmd_a> (command)
-Calls the first function.
+The C<a> command handles pre-execution actions. These are associated with a
+particular line, so they're stored in C<%dbline>. We default to the current
+line if none is specified.
-The first function works on the I<current> file (i.e., the one we changed to),
-and prints C<$filename_error> in the error message (the name of the other file)
-if it needs to. When the functions return, C<*dbline> is restored to point
-to the actual current file (the one we're executing in) and
-C<$filename_error> is restored to C<"">. This restores everything to
-the way it was before the second function was called at all.
+=cut
-See the comments in C<breakable_line> and C<breakable_line_in_file> for more
-details.
+sub cmd_a {
+ my $cmd = shift;
+ my $line = shift || ''; # [.|line] expr
+ my $dbline = shift;
-=back
+ # If it's dot (here), or not all digits, use the current line.
+ $line =~ s/\A\./$dbline/;
-=cut
+ # Should be a line number followed by an expression.
+ if ( my ($lineno, $expr) = $line =~ /^\s*(\d*)\s*(\S.+)/ ) {
-use vars qw($filename_error);
-$filename_error = '';
+ if (! length($lineno)) {
+ $lineno = $dbline;
+ }
-=head3 breakable_line(from, to) (API)
+ # If we have an expression ...
+ if ( length $expr ) {
-The subroutine decides whether or not a line in the current file is breakable.
-It walks through C<@dbline> within the range of lines specified, looking for
-the first line that is breakable.
+ # ... but the line isn't breakable, complain.
+ if ( $dbline[$lineno] == 0 ) {
+ print $OUT
+ "Line $lineno($dbline[$lineno]) does not have an action?\n";
+ }
+ else {
-If C<$to> is greater than C<$from>, the search moves forwards, finding the
-first line I<after> C<$to> that's breakable, if there is one.
+ # It's executable. Record that the line has an action.
+ $had_breakpoints{$filename} |= 2;
-If C<$from> is greater than C<$to>, the search goes I<backwards>, finding the
-first line I<before> C<$to> that's breakable, if there is one.
+ # Remove any action, temp breakpoint, etc.
+ $dbline{$lineno} =~ s/\0[^\0]*//;
-=cut
+ # Add the action to the line.
+ $dbline{$lineno} .= "\0" . action($expr);
-sub breakable_line {
+ _set_breakpoint_enabled_status($filename, $lineno, 1);
+ }
+ } ## end if (length $expr)
+ } ## end if ($line =~ /^\s*(\d*)\s*(\S.+)/)
+ else {
- my ( $from, $to ) = @_;
+ # Syntax wrong.
+ print $OUT
+ "Adding an action requires an optional lineno and an expression\n"
+ ; # hint
+ }
+} ## end sub cmd_a
- # $i is the start point. (Where are the FORTRAN programs of yesteryear?)
- my $i = $from;
+=head3 C<cmd_A> (command)
- # If there are at least 2 arguments, we're trying to search a range.
- if ( @_ >= 2 ) {
+Delete actions. Similar to above, except the delete code is in a separate
+subroutine, C<delete_action>.
- # $delta is positive for a forward search, negative for a backward one.
- my $delta = $from < $to ? +1 : -1;
+=cut
- # Keep us from running off the ends of the file.
- my $limit = $delta > 0 ? $#dbline : 1;
+sub cmd_A {
+ my $cmd = shift;
+ my $line = shift || '';
+ my $dbline = shift;
- # Clever test. If you're a mathematician, it's obvious why this
- # test works. If not:
- # If $delta is positive (going forward), $limit will be $#dbline.
- # If $to is less than $limit, ($limit - $to) will be positive, times
- # $delta of 1 (positive), so the result is > 0 and we should use $to
- # as the stopping point.
- #
- # If $to is greater than $limit, ($limit - $to) is negative,
- # times $delta of 1 (positive), so the result is < 0 and we should
- # use $limit ($#dbline) as the stopping point.
- #
- # If $delta is negative (going backward), $limit will be 1.
- # If $to is zero, ($limit - $to) will be 1, times $delta of -1
- # (negative) so the result is > 0, and we use $to as the stopping
- # point.
- #
- # If $to is less than zero, ($limit - $to) will be positive,
- # times $delta of -1 (negative), so the result is not > 0, and
- # we use $limit (1) as the stopping point.
- #
- # If $to is 1, ($limit - $to) will zero, times $delta of -1
- # (negative), still giving zero; the result is not > 0, and
- # we use $limit (1) as the stopping point.
- #
- # if $to is >1, ($limit - $to) will be negative, times $delta of -1
- # (negative), giving a positive (>0) value, so we'll set $limit to
- # $to.
+ # Dot is this line.
+ $line =~ s/^\./$dbline/;
- $limit = $to if ( $limit - $to ) * $delta > 0;
+ # Call delete_action with a null param to delete them all.
+ # The '1' forces the eval to be true. It'll be false only
+ # if delete_action blows up for some reason, in which case
+ # we print $@ and get out.
+ if ( $line eq '*' ) {
+ if (! eval { _delete_all_actions(); 1 }) {
+ print {$OUT} $@;
+ return;
+ }
+ }
- # The real search loop.
- # $i starts at $from (the point we want to start searching from).
- # We move through @dbline in the appropriate direction (determined
- # by $delta: either -1 (back) or +1 (ahead).
- # We stay in as long as we haven't hit an executable line
- # ($dbline[$i] == 0 means not executable) and we haven't reached
- # the limit yet (test similar to the above).
- $i += $delta while $dbline[$i] == 0 and ( $limit - $i ) * $delta > 0;
+ # There's a real line number. Pass it to delete_action.
+ # Error trapping is as above.
+ elsif ( $line =~ /^(\S.*)/ ) {
+ if (! eval { delete_action($1); 1 }) {
+ print {$OUT} $@;
+ return;
+ }
+ }
- } ## end if (@_ >= 2)
+ # Swing and a miss. Bad syntax.
+ else {
+ print $OUT
+ "Deleting an action requires a line number, or '*' for all\n" ; # hint
+ }
+} ## end sub cmd_A
- # If $i points to a line that is executable, return that.
- return $i unless $dbline[$i] == 0;
+=head3 C<delete_action> (API)
- # Format the message and print it: no breakable lines in range.
- my ( $pl, $upto ) = ( '', '' );
- ( $pl, $upto ) = ( 's', "..$to" ) if @_ >= 2 and $from != $to;
+C<delete_action> accepts either a line number or C<undef>. If a line number
+is specified, we check for the line being executable (if it's not, it
+couldn't have had an action). If it is, we just take the action off (this
+will get any kind of an action, including breakpoints).
- # If there's a filename in filename_error, we'll see it.
- # If not, not.
- die "Line$pl $from$upto$filename_error not breakable\n";
-} ## end sub breakable_line
+=cut
-=head3 breakable_line_in_filename(file, from, to) (API)
+sub _remove_action_from_dbline {
+ my $i = shift;
-Like C<breakable_line>, but look in another file.
+ $dbline{$i} =~ s/\0[^\0]*//; # \^a
+ delete $dbline{$i} if $dbline{$i} eq '';
-=cut
+ return;
+}
-sub breakable_line_in_filename {
+sub _delete_all_actions {
+ print {$OUT} "Deleting all actions...\n";
- # Capture the file name.
- my ($f) = shift;
+ for my $file ( keys %had_breakpoints ) {
+ local *dbline = $main::{ '_<' . $file };
+ $max = $#dbline;
+ my $was;
+ for my $i (1 .. $max) {
+ if ( defined $dbline{$i} ) {
+ _remove_action_from_dbline($i);
+ }
+ }
- # Swap the magic line array over there temporarily.
- local *dbline = $main::{ '_<' . $f };
+ unless ( $had_breakpoints{$file} &= ~2 ) {
+ delete $had_breakpoints{$file};
+ }
+ }
- # If there's an error, it's in this other file.
- local $filename_error = " of '$f'";
+ return;
+}
- # Find the breakable line.
- breakable_line(@_);
+sub delete_action {
+ my $i = shift;
- # *dbline and $filename_error get restored when this block ends.
+ if ( defined($i) ) {
+ # Can there be one?
+ die "Line $i has no action .\n" if $dbline[$i] == 0;
-} ## end sub breakable_line_in_filename
+ # Nuke whatever's there.
+ _remove_action_from_dbline($i);
+ }
+ else {
+ _delete_all_actions();
+ }
+}
-=head3 break_on_line(lineno, [condition]) (API)
+=head3 C<cmd_b> (command)
-Adds a breakpoint with the specified condition (or 1 if no condition was
-specified) to the specified line. Dies if it can't.
+Set breakpoints. Since breakpoints can be set in so many places, in so many
+ways, conditionally or not, the breakpoint code is kind of complex. Mostly,
+we try to parse the command type, and then shuttle it off to an appropriate
+subroutine to actually do the work of setting the breakpoint in the right
+place.
=cut
-sub break_on_line {
- my $i = shift;
- my $cond = @_ ? shift(@_) : 1;
-
- my $inii = $i;
- my $after = '';
- my $pl = '';
+sub cmd_b {
+ my $cmd = shift;
+ my $line = shift; # [.|line] [cond]
+ my $dbline = shift;
- # Woops, not a breakable line. $filename_error allows us to say
- # if it was in a different file.
- die "Line $i$filename_error not breakable.\n" if $dbline[$i] == 0;
+ my $default_cond = sub {
+ my $cond = shift;
+ return length($cond) ? $cond : '1';
+ };
- # Mark this file as having breakpoints in it.
- $had_breakpoints{$filename} |= 1;
+ # Make . the current line number if it's there..
+ $line =~ s/^\.(\s|\z)/$dbline$1/;
- # If there is an action or condition here already ...
- if ( $dbline{$i} ) {
+ # No line number, no condition. Simple break on current line.
+ if ( $line =~ /^\s*$/ ) {
+ cmd_b_line( $dbline, 1 );
+ }
- # ... swap this condition for the existing one.
- $dbline{$i} =~ s/^[^\0]*/$cond/;
+ # Break on load for a file.
+ elsif ( my ($file) = $line =~ /^load\b\s*(.*)/ ) {
+ $file =~ s/\s+\z//;
+ cmd_b_load($file);
}
- else {
- # Nothing here - just add the condition.
- $dbline{$i} = $cond;
+ # b compile|postpone <some sub> [<condition>]
+ # The interpreter actually traps this one for us; we just put the
+ # necessary condition in the %postponed hash.
+ elsif ( my ($action, $subname, $cond)
+ = $line =~ /^(postpone|compile)\b\s*([':A-Za-z_][':\w]*)\s*(.*)/ ) {
- _set_breakpoint_enabled_status($filename, $i, 1);
+ # De-Perl4-ify the name - ' separators to ::.
+ $subname =~ s/'/::/g;
+
+ # Qualify it into the current package unless it's already qualified.
+ $subname = "${package}::" . $subname unless $subname =~ /::/;
+
+ # Add main if it starts with ::.
+ $subname = "main" . $subname if substr( $subname, 0, 2 ) eq "::";
+
+ # Save the break type for this sub.
+ $postponed{$subname} = (($action eq 'postpone')
+ ? ( "break +0 if " . $default_cond->($cond) )
+ : "compile");
+ } ## end elsif ($line =~ ...
+ # b <filename>:<line> [<condition>]
+ elsif (my ($filename, $line_num, $cond)
+ = $line =~ /\A(\S+[^:]):(\d+)\s*(.*)/ms) {
+ cmd_b_filename_line(
+ $filename,
+ $line_num,
+ (length($cond) ? $cond : '1'),
+ );
}
+ # b <sub name> [<condition>]
+ elsif ( my ($new_subname, $new_cond) =
+ $line =~ /^([':A-Za-z_][':\w]*(?:\[.*\])?)\s*(.*)/ ) {
- return;
-} ## end sub break_on_line
+ #
+ $subname = $new_subname;
+ cmd_b_sub( $subname, $default_cond->($new_cond) );
+ }
-=head3 cmd_b_line(line, [condition]) (command)
+ # b <line> [<condition>].
+ elsif ( my ($line_n, $cond) = $line =~ /^(\d*)\s*(.*)/ ) {
-Wrapper for C<break_on_line>. Prints the failure message if it
-doesn't work.
+ # Capture the line. If none, it's the current line.
+ $line = $line_n || $dbline;
-=cut
+ # Break on line.
+ cmd_b_line( $line, $default_cond->($cond) );
+ }
-sub cmd_b_line {
- if (not eval { break_on_line(@_); 1 }) {
- local $\ = '';
- print $OUT $@ and return;
+ # Line didn't make sense.
+ else {
+ print "confused by line($line)?\n";
}
return;
-} ## end sub cmd_b_line
+} ## end sub cmd_b
-=head3 cmd_b_filename_line(line, [condition]) (command)
+=head3 C<break_on_load> (API)
-Wrapper for C<break_on_filename_line>. Prints the failure message if it
-doesn't work.
+We want to break when this file is loaded. Mark this file in the
+C<%break_on_load> hash, and note that it has a breakpoint in
+C<%had_breakpoints>.
=cut
-sub cmd_b_filename_line {
- if (not eval { break_on_filename_line(@_); 1 }) {
- local $\ = '';
- print $OUT $@ and return;
- }
-
- return;
+sub break_on_load {
+ my $file = shift;
+ $break_on_load{$file} = 1;
+ $had_breakpoints{$file} |= 1;
}
-=head3 break_on_filename_line(file, line, [condition]) (API)
+=head3 C<report_break_on_load> (API)
-Switches to the file specified and then calls C<break_on_line> to set
-the breakpoint.
+Gives us an array of filenames that are set to break on load. Note that
+only files with break-on-load are in here, so simply showing the keys
+suffices.
=cut
-sub break_on_filename_line {
- my $f = shift;
- my $i = shift;
- my $cond = @_ ? shift(@_) : 1;
+sub report_break_on_load {
+ sort keys %break_on_load;
+}
- # Switch the magical hash temporarily.
- local *dbline = $main::{ '_<' . $f };
+=head3 C<cmd_b_load> (command)
- # Localize the variables that break_on_line uses to make its message.
- local $filename_error = " of '$f'";
- local $filename = $f;
+We take the file passed in and try to find it in C<%INC> (which maps modules
+to files they came from). We mark those files for break-on-load via
+C<break_on_load> and then report that it was done.
- # Add the breakpoint.
- break_on_line( $i, $cond );
+=cut
- return;
-} ## end sub break_on_filename_line
+sub cmd_b_load {
+ my $file = shift;
+ my @files;
-=head3 break_on_filename_line_range(file, from, to, [condition]) (API)
+ # This is a block because that way we can use a redo inside it
+ # even without there being any looping structure at all outside it.
+ {
-Switch to another file, search the range of lines specified for an
-executable one, and put a breakpoint on the first one you find.
+ # Save short name and full path if found.
+ push @files, $file;
+ push @files, $::INC{$file} if $::INC{$file};
-=cut
+ # Tack on .pm and do it again unless there was a '.' in the name
+ # already.
+ $file .= '.pm', redo unless $file =~ /\./;
+ }
-sub break_on_filename_line_range {
- my $f = shift;
- my $from = shift;
- my $to = shift;
- my $cond = @_ ? shift(@_) : 1;
+ # Do the real work here.
+ break_on_load($_) for @files;
- # Find a breakable line if there is one.
- my $i = breakable_line_in_filename( $f, $from, $to );
+ # All the files that have break-on-load breakpoints.
+ @files = report_break_on_load;
- # Add the breakpoint.
- break_on_filename_line( $f, $i, $cond );
+ # Normalize for the purposes of our printing this.
+ local $\ = '';
+ local $" = ' ';
+ print $OUT "Will stop on load of '@files'.\n";
+} ## end sub cmd_b_load
- return;
-} ## end sub break_on_filename_line_range
+=head3 C<$filename_error> (API package global)
-=head3 subroutine_filename_lines(subname, [condition]) (API)
+Several of the functions we need to implement in the API need to work both
+on the current file and on other files. We don't want to duplicate code, so
+C<$filename_error> is used to contain the name of the file that's being
+worked on (if it's not the current one).
-Search for a subroutine within a given file. The condition is ignored.
-Uses C<find_sub> to locate the desired subroutine.
+We can now build functions in pairs: the basic function works on the current
+file, and uses C<$filename_error> as part of its error message. Since this is
+initialized to C<"">, no filename will appear when we are working on the
+current file.
-=cut
+The second function is a wrapper which does the following:
-sub subroutine_filename_lines {
- my ( $subname ) = @_;
+=over 4
- # Returned value from find_sub() is fullpathname:startline-endline.
- # The match creates the list (fullpathname, start, end).
- return (find_sub($subname) =~ /^(.*):(\d+)-(\d+)$/);
-} ## end sub subroutine_filename_lines
+=item *
-=head3 break_subroutine(subname) (API)
+Localizes C<$filename_error> and sets it to the name of the file to be processed.
-Places a break on the first line possible in the specified subroutine. Uses
-C<subroutine_filename_lines> to find the subroutine, and
-C<break_on_filename_line_range> to place the break.
+=item *
-=cut
+Localizes the C<*dbline> glob and reassigns it to point to the file we want to process.
-sub break_subroutine {
- my $subname = shift;
+=item *
- # Get filename, start, and end.
- my ( $file, $s, $e ) = subroutine_filename_lines($subname)
- or die "Subroutine $subname not found.\n";
+Calls the first function.
+The first function works on the I<current> file (i.e., the one we changed to),
+and prints C<$filename_error> in the error message (the name of the other file)
+if it needs to. When the functions return, C<*dbline> is restored to point
+to the actual current file (the one we're executing in) and
+C<$filename_error> is restored to C<"">. This restores everything to
+the way it was before the second function was called at all.
- # Null condition changes to '1' (always true).
- my $cond = @_ ? shift(@_) : 1;
+See the comments in C<breakable_line> and C<breakable_line_in_file> for more
+details.
- # Put a break the first place possible in the range of lines
- # that make up this subroutine.
- break_on_filename_line_range( $file, $s, $e, $cond );
+=back
- return;
-} ## end sub break_subroutine
+=cut
-=head3 cmd_b_sub(subname, [condition]) (command)
+use vars qw($filename_error);
+$filename_error = '';
-We take the incoming subroutine name and fully-qualify it as best we can.
+=head3 breakable_line(from, to) (API)
-=over 4
+The subroutine decides whether or not a line in the current file is breakable.
+It walks through C<@dbline> within the range of lines specified, looking for
+the first line that is breakable.
-=item 1. If it's already fully-qualified, leave it alone.
+If C<$to> is greater than C<$from>, the search moves forwards, finding the
+first line I<after> C<$to> that's breakable, if there is one.
-=item 2. Try putting it in the current package.
+If C<$from> is greater than C<$to>, the search goes I<backwards>, finding the
+first line I<before> C<$to> that's breakable, if there is one.
-=item 3. If it's not there, try putting it in CORE::GLOBAL if it exists there.
+=cut
-=item 4. If it starts with '::', put it in 'main::'.
+sub breakable_line {
-=back
+ my ( $from, $to ) = @_;
-After all this cleanup, we call C<break_subroutine> to try to set the
-breakpoint.
+ # $i is the start point. (Where are the FORTRAN programs of yesteryear?)
+ my $i = $from;
-=cut
+ # If there are at least 2 arguments, we're trying to search a range.
+ if ( @_ >= 2 ) {
-sub cmd_b_sub {
- my $subname = shift;
- my $cond = @_ ? shift : 1;
+ # $delta is positive for a forward search, negative for a backward one.
+ my $delta = $from < $to ? +1 : -1;
- # If the subname isn't a code reference, qualify it so that
- # break_subroutine() will work right.
- if ( ref($subname) ne 'CODE' ) {
+ # Keep us from running off the ends of the file.
+ my $limit = $delta > 0 ? $#dbline : 1;
- # Not Perl 4.
- $subname =~ s/'/::/g;
- my $s = $subname;
+ # Clever test. If you're a mathematician, it's obvious why this
+ # test works. If not:
+ # If $delta is positive (going forward), $limit will be $#dbline.
+ # If $to is less than $limit, ($limit - $to) will be positive, times
+ # $delta of 1 (positive), so the result is > 0 and we should use $to
+ # as the stopping point.
+ #
+ # If $to is greater than $limit, ($limit - $to) is negative,
+ # times $delta of 1 (positive), so the result is < 0 and we should
+ # use $limit ($#dbline) as the stopping point.
+ #
+ # If $delta is negative (going backward), $limit will be 1.
+ # If $to is zero, ($limit - $to) will be 1, times $delta of -1
+ # (negative) so the result is > 0, and we use $to as the stopping
+ # point.
+ #
+ # If $to is less than zero, ($limit - $to) will be positive,
+ # times $delta of -1 (negative), so the result is not > 0, and
+ # we use $limit (1) as the stopping point.
+ #
+ # If $to is 1, ($limit - $to) will zero, times $delta of -1
+ # (negative), still giving zero; the result is not > 0, and
+ # we use $limit (1) as the stopping point.
+ #
+ # if $to is >1, ($limit - $to) will be negative, times $delta of -1
+ # (negative), giving a positive (>0) value, so we'll set $limit to
+ # $to.
- # Put it in this package unless it's already qualified.
- if ($subname !~ /::/)
- {
- $subname = $package . '::' . $subname;
- };
+ $limit = $to if ( $limit - $to ) * $delta > 0;
- # Requalify it into CORE::GLOBAL if qualifying it into this
- # package resulted in its not being defined, but only do so
- # if it really is in CORE::GLOBAL.
- my $core_name = "CORE::GLOBAL::$s";
- if ((!defined(&$subname))
- and ($s !~ /::/)
- and (defined &{$core_name}))
- {
- $subname = $core_name;
- }
+ # The real search loop.
+ # $i starts at $from (the point we want to start searching from).
+ # We move through @dbline in the appropriate direction (determined
+ # by $delta: either -1 (back) or +1 (ahead).
+ # We stay in as long as we haven't hit an executable line
+ # ($dbline[$i] == 0 means not executable) and we haven't reached
+ # the limit yet (test similar to the above).
+ $i += $delta while $dbline[$i] == 0 and ( $limit - $i ) * $delta > 0;
- # Put it in package 'main' if it has a leading ::.
- if ($subname =~ /\A::/)
- {
- $subname = "main" . $subname;
- }
- } ## end if ( ref($subname) ne 'CODE' ) {
+ } ## end if (@_ >= 2)
- # Try to set the breakpoint.
- if (not eval { break_subroutine( $subname, $cond ); 1 }) {
- local $\ = '';
- print {$OUT} $@;
- return;
- }
+ # If $i points to a line that is executable, return that.
+ return $i unless $dbline[$i] == 0;
- return;
-} ## end sub cmd_b_sub
+ # Format the message and print it: no breakable lines in range.
+ my ( $pl, $upto ) = ( '', '' );
+ ( $pl, $upto ) = ( 's', "..$to" ) if @_ >= 2 and $from != $to;
-=head3 C<cmd_B> - delete breakpoint(s) (command)
+ # If there's a filename in filename_error, we'll see it.
+ # If not, not.
+ die "Line$pl $from$upto$filename_error not breakable\n";
+} ## end sub breakable_line
-The command mostly parses the command line and tries to turn the argument
-into a line spec. If it can't, it uses the current line. It then calls
-C<delete_breakpoint> to actually do the work.
+=head3 breakable_line_in_filename(file, from, to) (API)
-If C<*> is specified, C<cmd_B> calls C<delete_breakpoint> with no arguments,
-thereby deleting all the breakpoints.
+Like C<breakable_line>, but look in another file.
=cut
-sub cmd_B {
- my $cmd = shift;
+sub breakable_line_in_filename {
- # No line spec? Use dbline.
- # If there is one, use it if it's non-zero, or wipe it out if it is.
- my $line = ( $_[0] =~ /\A\./ ) ? $dbline : (shift || '');
- my $dbline = shift;
+ # Capture the file name.
+ my ($f) = shift;
- # If the line was dot, make the line the current one.
- $line =~ s/^\./$dbline/;
+ # Swap the magic line array over there temporarily.
+ local *dbline = $main::{ '_<' . $f };
- # If it's * we're deleting all the breakpoints.
- if ( $line eq '*' ) {
- if (not eval { delete_breakpoint(); 1 }) {
- print {$OUT} $@;
- }
- }
+ # If there's an error, it's in this other file.
+ local $filename_error = " of '$f'";
- # If there is a line spec, delete the breakpoint on that line.
- elsif ( $line =~ /\A(\S.*)/ ) {
- if (not eval { delete_breakpoint( $line || $dbline ); 1 }) {
- local $\ = '';
- print {$OUT} $@;
- }
- } ## end elsif ($line =~ /^(\S.*)/)
+ # Find the breakable line.
+ breakable_line(@_);
- # No line spec.
- else {
- print {$OUT}
- "Deleting a breakpoint requires a line number, or '*' for all\n"
- ; # hint
- }
+ # *dbline and $filename_error get restored when this block ends.
- return;
-} ## end sub cmd_B
+} ## end sub breakable_line_in_filename
-=head3 delete_breakpoint([line]) (API)
+=head3 break_on_line(lineno, [condition]) (API)
-This actually does the work of deleting either a single breakpoint, or all
-of them.
+Adds a breakpoint with the specified condition (or 1 if no condition was
+specified) to the specified line. Dies if it can't.
-For a single line, we look for it in C<@dbline>. If it's nonbreakable, we
-just drop out with a message saying so. If it is, we remove the condition
-part of the 'condition\0action' that says there's a breakpoint here. If,
-after we've done that, there's nothing left, we delete the corresponding
-line in C<%dbline> to signal that no action needs to be taken for this line.
+=cut
-For all breakpoints, we iterate through the keys of C<%had_breakpoints>,
-which lists all currently-loaded files which have breakpoints. We then look
-at each line in each of these files, temporarily switching the C<%dbline>
-and C<@dbline> structures to point to the files in question, and do what
-we did in the single line case: delete the condition in C<@dbline>, and
-delete the key in C<%dbline> if nothing's left.
+sub break_on_line {
+ my $i = shift;
+ my $cond = @_ ? shift(@_) : 1;
-We then wholesale delete C<%postponed>, C<%postponed_file>, and
-C<%break_on_load>, because these structures contain breakpoints for files
-and code that haven't been loaded yet. We can just kill these off because there
-are no magical debugger structures associated with them.
+ my $inii = $i;
+ my $after = '';
+ my $pl = '';
+
+ # Woops, not a breakable line. $filename_error allows us to say
+ # if it was in a different file.
+ die "Line $i$filename_error not breakable.\n" if $dbline[$i] == 0;
+
+ # Mark this file as having breakpoints in it.
+ $had_breakpoints{$filename} |= 1;
+
+ # If there is an action or condition here already ...
+ if ( $dbline{$i} ) {
-=cut
+ # ... swap this condition for the existing one.
+ $dbline{$i} =~ s/^[^\0]*/$cond/;
+ }
+ else {
-sub _remove_breakpoint_entry {
- my ($fn, $i) = @_;
+ # Nothing here - just add the condition.
+ $dbline{$i} = $cond;
- delete $dbline{$i};
- _delete_breakpoint_data_ref($fn, $i);
+ _set_breakpoint_enabled_status($filename, $i, 1);
+ }
return;
-}
+} ## end sub break_on_line
-sub _delete_all_breakpoints {
- print {$OUT} "Deleting all breakpoints...\n";
+=head3 cmd_b_line(line, [condition]) (command)
- # %had_breakpoints lists every file that had at least one
- # breakpoint in it.
- for my $fn ( keys %had_breakpoints ) {
+Wrapper for C<break_on_line>. Prints the failure message if it
+doesn't work.
- # Switch to the desired file temporarily.
- local *dbline = $main::{ '_<' . $fn };
+=cut
- $max = $#dbline;
+sub cmd_b_line {
+ if (not eval { break_on_line(@_); 1 }) {
+ local $\ = '';
+ print $OUT $@ and return;
+ }
- # For all lines in this file ...
- for my $i (1 .. $max) {
+ return;
+} ## end sub cmd_b_line
- # If there's a breakpoint or action on this line ...
- if ( defined $dbline{$i} ) {
+=head3 cmd_b_filename_line(line, [condition]) (command)
- # ... remove the breakpoint.
- $dbline{$i} =~ s/\A[^\0]+//;
- if ( $dbline{$i} =~ s/\A\0?\z// ) {
- # Remove the entry altogether if no action is there.
- _remove_breakpoint_entry($fn, $i);
- }
- } ## end if (defined $dbline{$i...
- } ## end for $i (1 .. $max)
+Wrapper for C<break_on_filename_line>. Prints the failure message if it
+doesn't work.
- # If, after we turn off the "there were breakpoints in this file"
- # bit, the entry in %had_breakpoints for this file is zero,
- # we should remove this file from the hash.
- if ( not $had_breakpoints{$fn} &= (~1) ) {
- delete $had_breakpoints{$fn};
- }
- } ## end for my $fn (keys %had_breakpoints)
+=cut
- # Kill off all the other breakpoints that are waiting for files that
- # haven't been loaded yet.
- undef %postponed;
- undef %postponed_file;
- undef %break_on_load;
+sub cmd_b_filename_line {
+ if (not eval { break_on_filename_line(@_); 1 }) {
+ local $\ = '';
+ print $OUT $@ and return;
+ }
return;
}
-sub _delete_breakpoint_from_line {
- my ($i) = @_;
+=head3 break_on_filename_line(file, line, [condition]) (API)
- # Woops. This line wasn't breakable at all.
- die "Line $i not breakable.\n" if $dbline[$i] == 0;
+Switches to the file specified and then calls C<break_on_line> to set
+the breakpoint.
- # Kill the condition, but leave any action.
- $dbline{$i} =~ s/\A[^\0]*//;
+=cut
- # Remove the entry entirely if there's no action left.
- if ($dbline{$i} eq '') {
- _remove_breakpoint_entry($filename, $i);
- }
+sub break_on_filename_line {
+ my $f = shift;
+ my $i = shift;
+ my $cond = @_ ? shift(@_) : 1;
- return;
-}
+ # Switch the magical hash temporarily.
+ local *dbline = $main::{ '_<' . $f };
-sub delete_breakpoint {
- my $i = shift;
+ # Localize the variables that break_on_line uses to make its message.
+ local $filename_error = " of '$f'";
+ local $filename = $f;
- # If we got a line, delete just that one.
- if ( defined($i) ) {
- _delete_breakpoint_from_line($i);
- }
- # No line; delete them all.
- else {
- _delete_all_breakpoints();
- }
+ # Add the breakpoint.
+ break_on_line( $i, $cond );
return;
-}
+} ## end sub break_on_filename_line
-=head3 cmd_stop (command)
+=head3 break_on_filename_line_range(file, from, to, [condition]) (API)
-This is meant to be part of the new command API, but it isn't called or used
-anywhere else in the debugger. XXX It is probably meant for use in development
-of new commands.
+Switch to another file, search the range of lines specified for an
+executable one, and put a breakpoint on the first one you find.
=cut
-sub cmd_stop { # As on ^C, but not signal-safy.
- $signal = 1;
-}
+sub break_on_filename_line_range {
+ my $f = shift;
+ my $from = shift;
+ my $to = shift;
+ my $cond = @_ ? shift(@_) : 1;
-=head3 C<cmd_e> - threads
+ # Find a breakable line if there is one.
+ my $i = breakable_line_in_filename( $f, $from, $to );
-Display the current thread id:
+ # Add the breakpoint.
+ break_on_filename_line( $f, $i, $cond );
- e
+ return;
+} ## end sub break_on_filename_line_range
-This could be how (when implemented) to send commands to this thread id (e cmd)
-or that thread id (e tid cmd).
+=head3 subroutine_filename_lines(subname, [condition]) (API)
-=cut
+Search for a subroutine within a given file. The condition is ignored.
+Uses C<find_sub> to locate the desired subroutine.
-sub cmd_e {
- my $cmd = shift;
- my $line = shift;
- unless (exists($INC{'threads.pm'})) {
- print "threads not loaded($ENV{PERL5DB_THREADED})
- please run the debugger with PERL5DB_THREADED=1 set in the environment\n";
- } else {
- my $tid = threads->tid;
- print "thread id: $tid\n";
- }
-} ## end sub cmd_e
+=cut
-=head3 C<cmd_E> - list of thread ids
+sub subroutine_filename_lines {
+ my ( $subname ) = @_;
-Display the list of available thread ids:
+ # Returned value from find_sub() is fullpathname:startline-endline.
+ # The match creates the list (fullpathname, start, end).
+ return (find_sub($subname) =~ /^(.*):(\d+)-(\d+)$/);
+} ## end sub subroutine_filename_lines
- E
+=head3 break_subroutine(subname) (API)
-This could be used (when implemented) to send commands to all threads (E cmd).
+Places a break on the first line possible in the specified subroutine. Uses
+C<subroutine_filename_lines> to find the subroutine, and
+C<break_on_filename_line_range> to place the break.
=cut
-sub cmd_E {
- my $cmd = shift;
- my $line = shift;
- unless (exists($INC{'threads.pm'})) {
- print "threads not loaded($ENV{PERL5DB_THREADED})
- please run the debugger with PERL5DB_THREADED=1 set in the environment\n";
- } else {
- my $tid = threads->tid;
- print "thread ids: ".join(', ',
- map { ($tid == $_->tid ? '<'.$_->tid.'>' : $_->tid) } threads->list
- )."\n";
- }
-} ## end sub cmd_E
+sub break_subroutine {
+ my $subname = shift;
-=head3 C<cmd_h> - help command (command)
+ # Get filename, start, and end.
+ my ( $file, $s, $e ) = subroutine_filename_lines($subname)
+ or die "Subroutine $subname not found.\n";
-Does the work of either
+
+ # Null condition changes to '1' (always true).
+ my $cond = @_ ? shift(@_) : 1;
+
+ # Put a break the first place possible in the range of lines
+ # that make up this subroutine.
+ break_on_filename_line_range( $file, $s, $e, $cond );
+
+ return;
+} ## end sub break_subroutine
+
+=head3 cmd_b_sub(subname, [condition]) (command)
+
+We take the incoming subroutine name and fully-qualify it as best we can.
=over 4
-=item *
+=item 1. If it's already fully-qualified, leave it alone.
-Showing all the debugger help
+=item 2. Try putting it in the current package.
-=item *
+=item 3. If it's not there, try putting it in CORE::GLOBAL if it exists there.
-Showing help for a specific command
+=item 4. If it starts with '::', put it in 'main::'.
=back
-=cut
-
-use vars qw($help);
-use vars qw($summary);
+After all this cleanup, we call C<break_subroutine> to try to set the
+breakpoint.
-sub cmd_h {
- my $cmd = shift;
+=cut
- # If we have no operand, assume null.
- my $line = shift || '';
+sub cmd_b_sub {
+ my $subname = shift;
+ my $cond = @_ ? shift : 1;
- # 'h h'. Print the long-format help.
- if ( $line =~ /\Ah\s*\z/ ) {
- print_help($help);
- }
+ # If the subname isn't a code reference, qualify it so that
+ # break_subroutine() will work right.
+ if ( ref($subname) ne 'CODE' ) {
- # 'h <something>'. Search for the command and print only its help.
- elsif ( my ($asked) = $line =~ /\A(\S.*)\z/ ) {
+ # Not Perl 4.
+ $subname =~ s/'/::/g;
+ my $s = $subname;
- # support long commands; otherwise bogus errors
- # happen when you ask for h on <CR> for example
- my $qasked = quotemeta($asked); # for searching; we don't
- # want to use it as a pattern.
- # XXX: finds CR but not <CR>
+ # Put it in this package unless it's already qualified.
+ if ($subname !~ /::/)
+ {
+ $subname = $package . '::' . $subname;
+ };
- # Search the help string for the command.
- if (
- $help =~ /^ # Start of a line
- <? # Optional '<'
- (?:[IB]<) # Optional markup
- $qasked # The requested command
- /mx
- )
+ # Requalify it into CORE::GLOBAL if qualifying it into this
+ # package resulted in its not being defined, but only do so
+ # if it really is in CORE::GLOBAL.
+ my $core_name = "CORE::GLOBAL::$s";
+ if ((!defined(&$subname))
+ and ($s !~ /::/)
+ and (defined &{$core_name}))
{
-
- # It's there; pull it out and print it.
- while (
- $help =~ /^
- (<? # Optional '<'
- (?:[IB]<) # Optional markup
- $qasked # The command
- ([\s\S]*?) # Description line(s)
- \n) # End of last description line
- (?!\s) # Next line not starting with
- # whitespace
- /mgx
- )
- {
- print_help($1);
- }
+ $subname = $core_name;
}
- # Not found; not a debugger command.
- else {
- print_help("B<$asked> is not a debugger command.\n");
+ # Put it in package 'main' if it has a leading ::.
+ if ($subname =~ /\A::/)
+ {
+ $subname = "main" . $subname;
}
- } ## end elsif ($line =~ /^(\S.*)$/)
+ } ## end if ( ref($subname) ne 'CODE' ) {
- # 'h' - print the summary help.
- else {
- print_help($summary);
+ # Try to set the breakpoint.
+ if (not eval { break_subroutine( $subname, $cond ); 1 }) {
+ local $\ = '';
+ print {$OUT} $@;
+ return;
}
-} ## end sub cmd_h
-=head3 C<cmd_i> - inheritance display
+ return;
+} ## end sub cmd_b_sub
-Display the (nested) parentage of the module or object given.
+=head3 C<cmd_B> - delete breakpoint(s) (command)
-=cut
+The command mostly parses the command line and tries to turn the argument
+into a line spec. If it can't, it uses the current line. It then calls
+C<delete_breakpoint> to actually do the work.
-sub cmd_i {
- my $cmd = shift;
- my $line = shift;
- foreach my $isa ( split( /\s+/, $line ) ) {
- $evalarg = $isa;
- ($isa) = DB::eval();
- no strict 'refs';
- print join(
- ', ',
- map {
- "$_"
- . (
- defined( ${"$_\::VERSION"} )
- ? ' ' . ${"$_\::VERSION"}
- : undef )
- } @{mro::get_linear_isa(ref($isa) || $isa)}
- );
- print "\n";
- }
-} ## end sub cmd_i
+If C<*> is specified, C<cmd_B> calls C<delete_breakpoint> with no arguments,
+thereby deleting all the breakpoints.
-=head3 C<cmd_l> - list lines (command)
+=cut
-Most of the command is taken up with transforming all the different line
-specification syntaxes into 'start-stop'. After that is done, the command
-runs a loop over C<@dbline> for the specified range of lines. It handles
-the printing of each line and any markers (C<==E<gt>> for current line,
-C<b> for break on this line, C<a> for action on this line, C<:> for this
-line breakable).
+sub cmd_B {
+ my $cmd = shift;
-We save the last line listed in the C<$start> global for further listing
-later.
+ # No line spec? Use dbline.
+ # If there is one, use it if it's non-zero, or wipe it out if it is.
+ my $line = ( $_[0] =~ /\A\./ ) ? $dbline : (shift || '');
+ my $dbline = shift;
-=cut
+ # If the line was dot, make the line the current one.
+ $line =~ s/^\./$dbline/;
-sub _min {
- my $min = shift;
- foreach my $v (@_) {
- if ($min > $v) {
- $min = $v;
+ # If it's * we're deleting all the breakpoints.
+ if ( $line eq '*' ) {
+ if (not eval { delete_breakpoint(); 1 }) {
+ print {$OUT} $@;
}
}
- return $min;
-}
-sub _max {
- my $max = shift;
- foreach my $v (@_) {
- if ($max < $v) {
- $max = $v;
+ # If there is a line spec, delete the breakpoint on that line.
+ elsif ( $line =~ /\A(\S.*)/ ) {
+ if (not eval { delete_breakpoint( $line || $dbline ); 1 }) {
+ local $\ = '';
+ print {$OUT} $@;
}
+ } ## end elsif ($line =~ /^(\S.*)/)
+
+ # No line spec.
+ else {
+ print {$OUT}
+ "Deleting a breakpoint requires a line number, or '*' for all\n"
+ ; # hint
}
- return $max;
-}
-sub _minify_to_max {
- my $ref = shift;
+ return;
+} ## end sub cmd_B
- $$ref = _min($$ref, $max);
+=head3 delete_breakpoint([line]) (API)
- return;
-}
+This actually does the work of deleting either a single breakpoint, or all
+of them.
-sub _cmd_l_handle_var_name {
- my $var_name = shift;
+For a single line, we look for it in C<@dbline>. If it's nonbreakable, we
+just drop out with a message saying so. If it is, we remove the condition
+part of the 'condition\0action' that says there's a breakpoint here. If,
+after we've done that, there's nothing left, we delete the corresponding
+line in C<%dbline> to signal that no action needs to be taken for this line.
- $evalarg = $var_name;
+For all breakpoints, we iterate through the keys of C<%had_breakpoints>,
+which lists all currently-loaded files which have breakpoints. We then look
+at each line in each of these files, temporarily switching the C<%dbline>
+and C<@dbline> structures to point to the files in question, and do what
+we did in the single line case: delete the condition in C<@dbline>, and
+delete the key in C<%dbline> if nothing's left.
- my ($s) = DB::eval();
+We then wholesale delete C<%postponed>, C<%postponed_file>, and
+C<%break_on_load>, because these structures contain breakpoints for files
+and code that haven't been loaded yet. We can just kill these off because there
+are no magical debugger structures associated with them.
- # Ooops. Bad scalar.
- if ($@) {
- print {$OUT} "Error: $@\n";
- next CMD;
- }
+=cut
- # Good scalar. If it's a reference, find what it points to.
- $s = CvGV_name($s);
- print {$OUT} "Interpreted as: $1 $s\n";
- $line = "$1 $s";
+sub _remove_breakpoint_entry {
+ my ($fn, $i) = @_;
- # Call self recursively to really do the command.
- return _cmd_l_main( $s );
+ delete $dbline{$i};
+ _delete_breakpoint_data_ref($fn, $i);
+
+ return;
}
-sub _cmd_l_handle_subname {
+sub _delete_all_breakpoints {
+ print {$OUT} "Deleting all breakpoints...\n";
- my $s = $subname;
+ # %had_breakpoints lists every file that had at least one
+ # breakpoint in it.
+ for my $fn ( keys %had_breakpoints ) {
- # De-Perl4.
- $subname =~ s/\'/::/;
+ # Switch to the desired file temporarily.
+ local *dbline = $main::{ '_<' . $fn };
- # Put it in this package unless it starts with ::.
- $subname = $package . "::" . $subname unless $subname =~ /::/;
+ $max = $#dbline;
- # Put it in CORE::GLOBAL if t doesn't start with :: and
- # it doesn't live in this package and it lives in CORE::GLOBAL.
- $subname = "CORE::GLOBAL::$s"
- if not defined &$subname
- and $s !~ /::/
- and defined &{"CORE::GLOBAL::$s"};
+ # For all lines in this file ...
+ for my $i (1 .. $max) {
- # Put leading '::' names into 'main::'.
- $subname = "main" . $subname if substr( $subname, 0, 2 ) eq "::";
+ # If there's a breakpoint or action on this line ...
+ if ( defined $dbline{$i} ) {
- # Get name:start-stop from find_sub, and break this up at
- # colons.
- my @pieces = split( /:/, find_sub($subname) || $sub{$subname} );
+ # ... remove the breakpoint.
+ $dbline{$i} =~ s/\A[^\0]+//;
+ if ( $dbline{$i} =~ s/\A\0?\z// ) {
+ # Remove the entry altogether if no action is there.
+ _remove_breakpoint_entry($fn, $i);
+ }
+ } ## end if (defined $dbline{$i...
+ } ## end for $i (1 .. $max)
- # Pull off start-stop.
- my $subrange = pop @pieces;
+ # If, after we turn off the "there were breakpoints in this file"
+ # bit, the entry in %had_breakpoints for this file is zero,
+ # we should remove this file from the hash.
+ if ( not $had_breakpoints{$fn} &= (~1) ) {
+ delete $had_breakpoints{$fn};
+ }
+ } ## end for my $fn (keys %had_breakpoints)
- # If the name contained colons, the split broke it up.
- # Put it back together.
- $file = join( ':', @pieces );
+ # Kill off all the other breakpoints that are waiting for files that
+ # haven't been loaded yet.
+ undef %postponed;
+ undef %postponed_file;
+ undef %break_on_load;
- # If we're not in that file, switch over to it.
- if ( $file ne $filename ) {
- if (! $slave_editor) {
- print {$OUT} "Switching to file '$file'.\n";
- }
+ return;
+}
- # Switch debugger's magic structures.
- *dbline = $main::{ '_<' . $file };
- $max = $#dbline;
- $filename = $file;
- } ## end if ($file ne $filename)
+sub _delete_breakpoint_from_line {
+ my ($i) = @_;
- # Subrange is 'start-stop'. If this is less than a window full,
- # swap it to 'start+', which will list a window from the start point.
- if ($subrange) {
- if ( eval($subrange) < -$window ) {
- $subrange =~ s/-.*/+/;
- }
+ # Woops. This line wasn't breakable at all.
+ die "Line $i not breakable.\n" if $dbline[$i] == 0;
- # Call self recursively to list the range.
- return _cmd_l_main( $subrange );
- } ## end if ($subrange)
+ # Kill the condition, but leave any action.
+ $dbline{$i} =~ s/\A[^\0]*//;
- # Couldn't find it.
+ # Remove the entry entirely if there's no action left.
+ if ($dbline{$i} eq '') {
+ _remove_breakpoint_entry($filename, $i);
+ }
+
+ return;
+}
+
+sub delete_breakpoint {
+ my $i = shift;
+
+ # If we got a line, delete just that one.
+ if ( defined($i) ) {
+ _delete_breakpoint_from_line($i);
+ }
+ # No line; delete them all.
else {
- print {$OUT} "Subroutine $subname not found.\n";
- return;
+ _delete_all_breakpoints();
}
-}
-
-sub _cmd_l_empty {
- # Compute new range to list.
- $incr = $window - 1;
- # Recurse to do it.
- return _cmd_l_main( $start . '-' . ( $start + $incr ) );
+ return;
}
-sub _cmd_l_plus {
- my ($new_start, $new_incr) = @_;
+=head3 cmd_stop (command)
- # Don't reset start for 'l +nnn'.
- $start = $new_start if $new_start;
+This is meant to be part of the new command API, but it isn't called or used
+anywhere else in the debugger. XXX It is probably meant for use in development
+of new commands.
- # Increment for list. Use window size if not specified.
- # (Allows 'l +' to work.)
- $incr = $new_incr || ($window - 1);
+=cut
- # Create a line range we'll understand, and recurse to do it.
- return _cmd_l_main( $start . '-' . ( $start + $incr ) );
+sub cmd_stop { # As on ^C, but not signal-safy.
+ $signal = 1;
}
-sub _cmd_l_calc_initial_end_and_i {
- my ($spec, $start_match, $end_match) = @_;
+=head3 C<cmd_e> - threads
- # Determine end point; use end of file if not specified.
- my $end = ( !defined $start_match ) ? $max :
- ( $end_match ? $end_match : $start_match );
+Display the current thread id:
- # Go on to the end, and then stop.
- _minify_to_max(\$end);
+ e
- # Determine start line.
- my $i = $start_match;
+This could be how (when implemented) to send commands to this thread id (e cmd)
+or that thread id (e tid cmd).
- if ($i eq '.') {
- $i = $spec;
+=cut
+
+sub cmd_e {
+ my $cmd = shift;
+ my $line = shift;
+ unless (exists($INC{'threads.pm'})) {
+ print "threads not loaded($ENV{PERL5DB_THREADED})
+ please run the debugger with PERL5DB_THREADED=1 set in the environment\n";
+ } else {
+ my $tid = threads->tid;
+ print "thread id: $tid\n";
}
+} ## end sub cmd_e
- $i = _max($i, 1);
+=head3 C<cmd_E> - list of thread ids
- $incr = $end - $i;
+Display the list of available thread ids:
- return ($end, $i);
-}
+ E
-sub _cmd_l_range {
- my ($spec, $current_line, $start_match, $end_match) = @_;
+This could be used (when implemented) to send commands to all threads (E cmd).
- my ($end, $i) =
- _cmd_l_calc_initial_end_and_i($spec, $start_match, $end_match);
+=cut
- # If we're running under a slave editor, force it to show the lines.
- if ($slave_editor) {
- print {$OUT} "\032\032$filename:$i:0\n";
- $i = $end;
+sub cmd_E {
+ my $cmd = shift;
+ my $line = shift;
+ unless (exists($INC{'threads.pm'})) {
+ print "threads not loaded($ENV{PERL5DB_THREADED})
+ please run the debugger with PERL5DB_THREADED=1 set in the environment\n";
+ } else {
+ my $tid = threads->tid;
+ print "thread ids: ".join(', ',
+ map { ($tid == $_->tid ? '<'.$_->tid.'>' : $_->tid) } threads->list
+ )."\n";
}
- # We're doing it ourselves. We want to show the line and special
- # markers for:
- # - the current line in execution
- # - whether a line is breakable or not
- # - whether a line has a break or not
- # - whether a line has an action or not
- else {
- I_TO_END:
- for ( ; $i <= $end ; $i++ ) {
+} ## end sub cmd_E
- # Check for breakpoints and actions.
- my ( $stop, $action );
- if ($dbline{$i}) {
- ( $stop, $action ) = split( /\0/, $dbline{$i} );
- }
+=head3 C<cmd_h> - help command (command)
- # ==> if this is the current line in execution,
- # : if it's breakable.
- my $arrow =
- ( $i == $current_line and $filename eq $filename_ini )
- ? '==>'
- : ( $dbline[$i] + 0 ? ':' : ' ' );
+Does the work of either
- # Add break and action indicators.
- $arrow .= 'b' if $stop;
- $arrow .= 'a' if $action;
+=over 4
- # Print the line.
- print {$OUT} "$i$arrow\t", $dbline[$i];
+=item *
- # Move on to the next line. Drop out on an interrupt.
- if ($signal) {
- $i++;
- last I_TO_END;
- }
- } ## end for (; $i <= $end ; $i++)
+Showing all the debugger help
- # Line the prompt up; print a newline if the last line listed
- # didn't have a newline.
- if ($dbline[ $i - 1 ] !~ /\n\z/) {
- print {$OUT} "\n";
- }
- } ## end else [ if ($slave_editor)
+=item *
- # Save the point we last listed to in case another relative 'l'
- # command is desired. Don't let it run off the end.
- $start = $i;
- _minify_to_max(\$start);
+Showing help for a specific command
- return;
-}
+=back
-sub _cmd_l_main {
- my $spec = shift;
+=cut
- # If this is '-something', delete any spaces after the dash.
- $spec =~ s/\A-\s*\z/-/;
+use vars qw($help);
+use vars qw($summary);
- # If the line is '$something', assume this is a scalar containing a
- # line number.
- # Set up for DB::eval() - evaluate in *user* context.
- if ( my ($var_name) = $spec =~ /\A(\$.*)/s ) {
- return _cmd_l_handle_var_name($var_name);
- }
- # l name. Try to find a sub by that name.
- elsif ( ($subname) = $spec =~ /\A([\':A-Za-z_][\':\w]*(?:\[.*\])?)/s ) {
- return _cmd_l_handle_subname();
- }
- # Bare 'l' command.
- elsif ( $spec !~ /\S/ ) {
- return _cmd_l_empty();
- }
- # l [start]+number_of_lines
- elsif ( my ($new_start, $new_incr) = $spec =~ /\A(\d*)\+(\d*)\z/ ) {
- return _cmd_l_plus($new_start, $new_incr);
- }
- # l start-stop or l start,stop
- elsif (my ($s, $e) = $spec =~ /^(?:(-?[\d\$\.]+)(?:[-,]([\d\$\.]+))?)?/ ) {
- return _cmd_l_range($spec, $line, $s, $e);
+sub cmd_h {
+ my $cmd = shift;
+
+ # If we have no operand, assume null.
+ my $line = shift || '';
+
+ # 'h h'. Print the long-format help.
+ if ( $line =~ /\Ah\s*\z/ ) {
+ print_help($help);
}
- return;
-} ## end sub cmd_l
+ # 'h <something>'. Search for the command and print only its help.
+ elsif ( my ($asked) = $line =~ /\A(\S.*)\z/ ) {
-sub cmd_l {
- my (undef, $line) = @_;
+ # support long commands; otherwise bogus errors
+ # happen when you ask for h on <CR> for example
+ my $qasked = quotemeta($asked); # for searching; we don't
+ # want to use it as a pattern.
+ # XXX: finds CR but not <CR>
- return _cmd_l_main($line);
-}
+ # Search the help string for the command.
+ if (
+ $help =~ /^ # Start of a line
+ <? # Optional '<'
+ (?:[IB]<) # Optional markup
+ $qasked # The requested command
+ /mx
+ )
+ {
+
+ # It's there; pull it out and print it.
+ while (
+ $help =~ /^
+ (<? # Optional '<'
+ (?:[IB]<) # Optional markup
+ $qasked # The command
+ ([\s\S]*?) # Description line(s)
+ \n) # End of last description line
+ (?!\s) # Next line not starting with
+ # whitespace
+ /mgx
+ )
+ {
+ print_help($1);
+ }
+ }
+
+ # Not found; not a debugger command.
+ else {
+ print_help("B<$asked> is not a debugger command.\n");
+ }
+ } ## end elsif ($line =~ /^(\S.*)$/)
+
+ # 'h' - print the summary help.
+ else {
+ print_help($summary);
+ }
+} ## end sub cmd_h
=head3 C<cmd_L> - list breakpoints, actions, and watch expressions (command)
# in the user's context. This version can handle expressions which
# return a list value.
$evalarg = $expr;
- my ($val) = join( ' ', DB::eval() );
+ # The &-call is here to ascertain the mutability of @_.
+ my ($val) = join( ' ', &DB::eval);
$val = ( defined $val ) ? "'$val'" : 'undef';
# Save the current value of the expression.
resetterm(1) if $LINEINFO eq $OUT and $term_pid != $$;
local $\ = '';
local $, = '';
- print $LINEINFO @_;
+ # $LINEINFO may be undef if $noTTY is set or some other issue.
+ if ($LINEINFO)
+ {
+ print {$LINEINFO} @_;
+ }
} ## end sub print_lineinfo
=head2 C<postponed_sub>
s/(.*)/'$1'/s
unless /^(?: -?[\d.]+ | \*[\w:]* )$/x;
- # Turn high-bit characters into meta-whatever.
- s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
-
- # Turn control characters into ^-whatever.
- s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
+ # Turn high-bit characters into meta-whatever, and controls into like
+ # '^D'.
+ require 'meta_notation.pm';
+ $_ = _meta_notation($_) if /[[:^print:]]/a;
return $_;
}
$i++
)
{
-
- # Go through the arguments and save them for later.
- my $save_args = _dump_trace_calc_save_args($nothard);
+ # if the sub has args ($h true), make an anonymous array of the
+ # dumped args.
+ my $args = $h ? _dump_trace_calc_save_args($nothard) : undef;
# If context is true, this is array (@)context.
# If context is false, this is scalar ($) context.
# happen' trap.)
$context = $context ? '@' : ( defined $context ? "\$" : '.' );
- # if the sub has args ($h true), make an anonymous array of the
- # dumped args.
- $args = $h ? $save_args : undef;
-
# remove trailing newline-whitespace-semicolon-end of line sequence
# from the eval text, if any.
$e =~ s/\n\s*\;\s*\Z// if $e;
# We save, change, then restore STDIN and STDOUT to avoid fork() since
# some non-Unix systems can do system() but have problems with fork().
- open( SAVEIN, "<&STDIN" ) || db_warn("Can't save STDIN");
- open( SAVEOUT, ">&STDOUT" ) || db_warn("Can't save STDOUT");
- open( STDIN, "<&IN" ) || db_warn("Can't redirect STDIN");
- open( STDOUT, ">&OUT" ) || db_warn("Can't redirect STDOUT");
+ open( SAVEIN, "<&STDIN" ) || _db_warn("Can't save STDIN");
+ open( SAVEOUT, ">&STDOUT" ) || _db_warn("Can't save STDOUT");
+ open( STDIN, "<&IN" ) || _db_warn("Can't redirect STDIN");
+ open( STDOUT, ">&OUT" ) || _db_warn("Can't redirect STDOUT");
# XXX: using csh or tcsh destroys sigint retvals!
system(@_);
- open( STDIN, "<&SAVEIN" ) || db_warn("Can't restore STDIN");
- open( STDOUT, ">&SAVEOUT" ) || db_warn("Can't restore STDOUT");
+ open( STDIN, "<&SAVEIN" ) || _db_warn("Can't restore STDIN");
+ open( STDOUT, ">&SAVEOUT" ) || _db_warn("Can't restore STDOUT");
close(SAVEIN);
close(SAVEOUT);
# most of the $? crud was coping with broken cshisms
if ( $? >> 8 ) {
- db_warn( "(Command exited ", ( $? >> 8 ), ")\n" );
+ _db_warn( "(Command exited ", ( $? >> 8 ), ")\n" );
}
elsif ($?) {
- db_warn(
+ _db_warn(
"(Command died of SIG#",
( $? & 127 ),
( ( $? & 128 ) ? " -- core dumped" : "" ),
if ($tty) {
my ( $i, $o ) = split $tty, /,/;
$o = $i unless defined $o;
- open( IN, "<$i" ) or die "Cannot open TTY '$i' for read: $!";
- open( OUT, ">$o" ) or die "Cannot open TTY '$o' for write: $!";
+ open( IN, '<', $i ) or die "Cannot open TTY '$i' for read: $!";
+ open( OUT, '>', $o ) or die "Cannot open TTY '$o' for write: $!";
$IN = \*IN;
$OUT = \*OUT;
_autoflush($OUT);
return $tty;
}
+=head3 C<tmux_get_fork_TTY>
+
+Creates a split window for subprocesses when a process running under the
+perl debugger in Tmux forks.
+
+=cut
+
+sub tmux_get_fork_TTY {
+ return unless $ENV{TMUX};
+
+ my $pipe;
+
+ my $status = open $pipe, '-|', 'tmux', 'split-window',
+ '-P', '-F', '#{pane_tty}', 'sleep 100000';
+
+ if ( !$status ) {
+ return;
+ }
+
+ my $tty = <$pipe>;
+ close $pipe;
+
+ if ( $tty ) {
+ chomp $tty;
+
+ if ( !defined $term ) {
+ require Term::ReadLine;
+ if ( !$rl ) {
+ $term = Term::ReadLine::Stub->new( 'perldb', $IN, $OUT );
+ }
+ else {
+ $term = Term::ReadLine->new( 'perldb', $IN, $OUT );
+ }
+ }
+ }
+
+ return $tty;
+}
+
=head2 C<create_IN_OUT($flags)>
Create a new pair of filehandles, pointing to a new TTY. If impossible,
# Add it to the terminal history (if possible).
$term->AddHistory($got)
- if length($got) > 1
+ if length($got) >= option_val("HistItemMinLength", 2)
and defined $term->Features->{addHistory};
return $got;
} ## end if (@typeahead)
Set_list packages up items to be stored in a set of environment variables
(VAR_n, containing the number of items, and VAR_0, VAR_1, etc., containing
the values). Values outside the standard ASCII charset are stored by encoding
-then as hexadecimal values.
+them as hexadecimal values.
=cut
for my $i ( 0 .. $#list ) {
$val = $list[$i];
$val =~ s/\\/\\\\/g;
- $val =~ s/([\0-\37\177\200-\377])/"\\0x" . unpack('H2',$1)/eg;
+ no warnings 'experimental::regex_sets';
+ $val =~ s/ ( (?[ [\000-\xFF] & [:^print:] ]) ) /
+ "\\0x" . unpack('H2',$1)/xaeg;
$ENV{"${stem}_$i"} = $val;
} ## end for $i (0 .. $#list)
} ## end sub set_list
}
# Open file onto the debugger's filehandles, if you can.
- open IN, $in or die "cannot open '$in' for read: $!";
- open OUT, ">$out" or die "cannot open '$out' for write: $!";
+ open IN, '<', $in or die "cannot open '$in' for read: $!";
+ open OUT, '>', $out or die "cannot open '$out' for write: $!";
# Swap to the new filehandles.
reset_IN_OUT( \*IN, \*OUT );
# wide. If it's wider than that, an extra space will be added.
$help_str =~ s{
^ # only matters at start of line
- ( \040{4} | \t )* # some subcommands are indented
+ ( \ {4} | \t )* # some subcommands are indented
( < ? # so <CR> works
[BI] < [^\t\n] + ) # find an eeevil ornament
( \t+ ) # original separation, discarded
=cut
-my %_is_in_pods = (map { $_ => 1 }
- qw(
- 5004delta
- 5005delta
- 561delta
- 56delta
- 570delta
- 571delta
- 572delta
- 573delta
- 58delta
- 581delta
- 582delta
- 583delta
- 584delta
- 590delta
- 591delta
- 592delta
- aix
- amiga
- apio
- api
- artistic
- book
- boot
- bot
- bs2000
- call
- ce
- cheat
- clib
- cn
- compile
- cygwin
- data
- dbmfilter
- debguts
- debtut
- debug
- delta
- dgux
- diag
- doc
- dos
- dsc
- ebcdic
- embed
- faq1
- faq2
- faq3
- faq4
- faq5
- faq6
- faq7
- faq8
- faq9
- faq
- filter
- fork
- form
- freebsd
- func
- gpl
- guts
- hack
- hist
- hpux
- hurd
- intern
- intro
- iol
- ipc
- irix
- jp
- ko
- lexwarn
- locale
- lol
- macos
- macosx
- modinstall
- modlib
- mod
- modstyle
- netware
- newmod
- number
- obj
- opentut
- op
- os2
- os390
- os400
- packtut
- plan9
- pod
- podspec
- port
- qnx
- ref
- reftut
- re
- requick
- reref
- retut
- run
- sec
- solaris
- style
- sub
- syn
- thrtut
- tie
- toc
- todo
- tooc
- toot
- trap
- tru64
- tw
- unicode
- uniintro
- util
- uts
- var
- vms
- vos
- win32
- xs
- xstut
- )
-);
-
sub runman {
my $page = shift;
unless ($page) {
$page = 'perl' if lc($page) eq 'help';
require Config;
- my $man1dir = $Config::Config{'man1dir'};
- my $man3dir = $Config::Config{'man3dir'};
+ my $man1dir = $Config::Config{man1direxp};
+ my $man3dir = $Config::Config{man3direxp};
for ( $man1dir, $man3dir ) { s#/[^/]*\z## if /\S/ }
my $manpath = '';
$manpath .= "$man1dir:" if $man1dir =~ /\S/;
chop $manpath if $manpath;
# harmless if missing, I figure
- my $oldpath = $ENV{MANPATH};
- $ENV{MANPATH} = $manpath if $manpath;
+ local $ENV{MANPATH} = $manpath if $manpath;
my $nopathopt = $^O =~ /dunno what goes here/;
if (
CORE::system(
)
{
unless ( $page =~ /^perl\w/ ) {
-# do it this way because its easier to slurp in to keep up to date - clunky though.
- if (exists($_is_in_pods{$page})) {
+ # Previously the debugger contained a list which it slurped in,
+ # listing the known "perl" manpages. However, it was out of date,
+ # with errors both of omission and inclusion. This approach is
+ # considerably less complex. The failure mode on a butchered
+ # install is simply that the user has to run man or perldoc
+ # "manually" with the full manpage name.
+
+ # There is a list of $^O values in installperl to determine whether
+ # the directory is 'pods' or 'pod'. However, we can avoid tight
+ # coupling to that by simply checking the "non-standard" 'pods'
+ # first.
+ my $pods = "$Config::Config{privlibexp}/pods";
+ $pods = "$Config::Config{privlibexp}/pod"
+ unless -d $pods;
+ if (-f "$pods/perl$page.pod") {
CORE::system( $doccmd,
( ( $manpath && !$nopathopt ) ? ( "-M", $manpath ) : () ),
"perl$page" );
}
}
} ## end if (CORE::system($doccmd...
- if ( defined $oldpath ) {
- $ENV{MANPATH} = $manpath;
- }
- else {
- delete $ENV{MANPATH};
- }
} ## end sub runman
#use Carp; # This did break, left for debugging
# This defines the point at which you get the 'deep recursion'
# warning. It MUST be defined or the debugger will not load.
- $deep = 100;
+ $deep = 1000;
# Number of lines around the current one that are shown in the
# 'w' command.
$db_stop = 0; # Compiler warning ...
$db_stop = 1 << 30; # ... because this is only used in an eval() later.
- # This variable records how many levels we're nested in debugging. Used
+ # This variable records how many levels we're nested in debugging.
# Used in the debugger prompt, and in determining whether it's all over or
# not.
$level = 0; # Level of recursive debugging
=cut
- if (not $text =~ /::/ and eval { require PadWalker } ) {
+ if (not $text =~ /::/ and eval {
+ local @INC = @INC;
+ pop @INC if $INC[-1] eq '.';
+ require PadWalker } ) {
my $level = 1;
while (1) {
my @info = caller($level);
=cut
push @out, map "$prefix$_", grep /^\Q$text/,
- ( grep /^_?[a-zA-Z]/, keys %$pack ),
+ ( grep /^_?[a-zA-Z]/, do { no strict 'refs'; keys %$pack } ),
( $pack eq '::' ? () : ( grep /::$/, keys %:: ) );
=item *
# Get the current value of the expression.
# Doesn't handle expressions returning list values!
$evalarg = $1;
- my ($val) = DB::eval();
+ # The &-call is here to ascertain the mutability of @_.
+ my ($val) = &DB::eval;
$val = ( defined $val ) ? "'$val'" : 'undef';
# Save it.