# Debugger for Perl 5.00x; perl5db.pl patch level:
-$VERSION = 1.10;
+# It is crucial that there is no lexicals in scope of `eval ""' down below
+sub eval {
+ # 'my' would make it visible from user code
+ # but so does local! --tchrist [... into @DB::res, not @res. IZ]
+ local @res;
+ {
+ local $otrace = $trace;
+ local $osingle = $single;
+ local $od = $^D;
+ { ($evalarg) = $evalarg =~ /(.*)/s; }
+ @res = eval "$usercontext $evalarg;\n"; # '\n' for nice recursive debug
+ $trace = $otrace;
+ $single = $osingle;
+ $^D = $od;
+ }
+ my $at = $@;
+ local $saved[0]; # Preserve the old value of $@
+ eval { &DB::save };
+ if ($at) {
+ print $OUT $at;
+ } elsif ($onetimeDump) {
+ if ($onetimeDump eq 'dump') {
+ local $option{dumpDepth} = $onetimedumpDepth
+ if defined $onetimedumpDepth;
+ dumpit($OUT, \@res);
+ } elsif ($onetimeDump eq 'methods') {
+ methods($res[0]) ;
+ }
+ }
+ @res;
+}
+
+# After this point it is safe to introduce lexicals
+# However, one should not overdo it: leave as much control from outside as possible
+
+$VERSION = 1.15;
$header = "perl5db.pl version $VERSION";
#
# This file is automatically included if you do perl -d.
# It's probably not useful to include this yourself.
#
+# Before venturing further into these twisty passages, it is
+# wise to read the perldebguts man page or risk the ire of dragons.
+#
# Perl supplies the values for %sub. It effectively inserts
# a &DB'DB(); in front of every place that can have a
# breakpoint. Instead of a subroutine call it calls &DB::sub with
# if caller() is called from the package DB, it provides some
# additional data.
#
-# The array @{$main::{'_<'.$filename}} is the line-by-line contents of
-# $filename.
+# The array @{$main::{'_<'.$filename}} (herein called @dbline) is the
+# line-by-line contents of $filename.
#
-# The hash %{'_<'.$filename} contains breakpoints and action (it is
-# keyed by line number), and individual entries are settable (as
-# opposed to the whole hash). Only true/false is important to the
-# interpreter, though the values used by perl5db.pl have the form
-# "$break_condition\0$action". Values are magical in numeric context.
+# The hash %{'_<'.$filename} (herein called %dbline) contains
+# breakpoints and action (it is keyed by line number), and individual
+# entries are settable (as opposed to the whole hash). Only true/false
+# is important to the interpreter, though the values used by
+# perl5db.pl have the form "$break_condition\0$action". Values are
+# magical in numeric context.
#
# The scalar ${'_<'.$filename} contains $filename.
#
# I<CreateTTY> bits control attempts to create a new TTY on events:
# 1: on fork() 2: debugger is started inside debugger
# 4: on startup
-# c) Code to auto-create a new TTY window on OS/2 (currently one one
+# c) Code to auto-create a new TTY window on OS/2 (currently one
# extra window per session - need named pipes to have more...);
# d) Simplified interface for custom createTTY functions (with a backward
# compatibility hack); now returns the TTY name to use; return of ''
# + Fixed warnings generated by "perl -dWe 42"
# + Corrected spelling errors
# + Squeezed Help (h) output into 80 columns
-
+#
+# Changes: 1.11: May 24, 2001 David Dyck <dcd@tc.fluke.com>
+# + Made "x @INC" work like it used to
+#
+# Changes: 1.12: May 24, 2001 Daniel Lewart <d-lewart@uiuc.edu>
+# + Fixed warnings generated by "O" (Show debugger options)
+# + Fixed warnings generated by "p 42" (Print expression)
+# Changes: 1.13: Jun 19, 2001 Scott.L.Miller@compaq.com
+# + Added windowSize option
+# Changes: 1.14: Oct 9, 2001 multiple
+# + Clean up after itself on VMS (Charles Lane in 12385)
+# + Adding "@ file" syntax (Peter Scott in 12014)
+# + Debug reloading selfloaded stuff (Ilya Zakharevich in 11457)
+# + $^S and other debugger fixes (Ilya Zakharevich in 11120)
+# + Forgot a my() declaration (Ilya Zakharevich in 11085)
+# Changes: 1.15: Nov 6, 2001 Michael G Schwern <schwern@pobox.com>
+# + Updated 1.14 change log
+# + Added *dbline explainatory comments
+# + Mentioning perldebguts man page
####################################################################
# Needed for the statement after exec():
# (local $^W cannot help - other packages!).
$inhibit_exit = $option{PrintRet} = 1;
-@options = qw(hashDepth arrayDepth DumpDBFiles DumpPackages DumpReused
+@options = qw(hashDepth arrayDepth dumpDepth
+ DumpDBFiles DumpPackages DumpReused
compactDump veryCompact quote HighBit undefPrint
globPrint PrintRet UsageOnly frame AutoTrace
TTY noTTY ReadLine NonStop LineInfo maxTraceLen
recallCommand ShellBang pager tkRunning ornaments
signalLevel warnLevel dieLevel inhibit_exit
ImmediateStop bareStringify CreateTTY
- RemotePort);
+ RemotePort windowSize);
%optionVars = (
hashDepth => \$dumpvar::hashDepth,
maxTraceLen => \$maxtrace,
ImmediateStop => \$ImmediateStop,
RemotePort => \$remoteport,
+ windowSize => \$window,
);
%optionAction = (
# These guys may be defined in $ENV{PERL5DB} :
$rl = 1 unless defined $rl;
-$warnLevel = 0 unless defined $warnLevel;
-$dieLevel = 0 unless defined $dieLevel;
+$warnLevel = 1 unless defined $warnLevel;
+$dieLevel = 1 unless defined $dieLevel;
$signalLevel = 1 unless defined $signalLevel;
$pre = [] unless defined $pre;
$post = [] unless defined $post;
dieLevel($dieLevel);
signalLevel($signalLevel);
-&pager(
- (defined($ENV{PAGER})
- ? $ENV{PAGER}
- : ($^O eq 'os2'
- ? 'cmd /c more'
- : 'more'))) unless defined $pager;
+pager(
+ defined $ENV{PAGER} ? $ENV{PAGER} :
+ eval { require Config } &&
+ defined $Config::Config{pager} ? $Config::Config{pager}
+ : 'more'
+ ) unless defined $pager;
setman();
&recallCommand("!") unless defined $prc;
&shellBang("!") unless defined $psh;
$console = undef;
}
+ if ($^O eq 'NetWare') {
+ $console = undef;
+ }
+
# Around a bug:
if (defined $ENV{OS2_SHELL} and ($slave_editor or $ENV{WINDOWID})) { # In OS/2
$console = undef;
);
if (!$OUT) { die "Unable to connect to remote host: $remoteport\n"; }
$IN = $OUT;
- } elsif ($CreateTTY & 4) {
- create_IN_OUT(4);
} else {
- if (defined $console) {
+ create_IN_OUT(4) if $CreateTTY & 4;
+ if ($console) {
my ($i, $o) = split /,/, $console;
$o = $i unless defined $o;
open(IN,"+<$i") || open(IN,"<$i") || open(IN,"<&STDIN");
open(OUT,"+>$o") || open(OUT,">$o") || open(OUT,">&STDERR")
|| open(OUT,">&STDOUT"); # so we don't dongle stdout
- } else {
+ } elsif (not defined $console) {
open(IN,"<&STDIN");
open(OUT,">&STDERR") || open(OUT,">&STDOUT"); # so we don't dongle stdout
$console = 'STDIN/OUT';
}
# so open("|more") can read from STDOUT and so we don't dingle stdin
- $IN = \*IN;
-
- $OUT = \*OUT;
+ $IN = \*IN, $OUT = \*OUT if $console or not defined $console;
}
- select($OUT);
+ my $previous = select($OUT);
$| = 1; # for DB::OUT
- select(STDOUT);
+ select($previous);
$LINEINFO = $OUT unless defined $LINEINFO;
$lineinfo = $console unless defined $lineinfo;
- $| = 1; # for real STDOUT
-
$header =~ s/.Header: ([^,]+),v(\s+\S+\s+\S+).*$/$1$2/;
unless ($runnonstop) {
if ($term_pid eq '-1') {
next CMD;
}
}
- $cmd =~ /^q$/ && ($fall_off_end = 1) && exit $?;
+ $cmd =~ /^q$/ && ($fall_off_end = 1) && clean_ENV() && exit $?;
$cmd =~ /^h$/ && do {
print_help($help);
next CMD; };
select ($savout);
next CMD; };
$cmd =~ s/^x\b/ / && do { # So that will be evaled
- $onetimeDump = 'dump'; };
+ $onetimeDump = 'dump';
+ # handle special "x 3 blah" syntax
+ if ($cmd =~ s/^\s*(\d+)(?=\s)/ /) {
+ $onetimedumpDepth = $1;
+ }
+ };
$cmd =~ s/^m\s+([\w:]+)\s*$/ / && do {
methods($1); next CMD};
$cmd =~ s/^m\b/ / && do { # So this will be evaled
$i = $end;
} else {
for (; $i <= $end; $i++) {
+ my ($stop,$action);
($stop,$action) = split(/\0/, $dbline{$i}) if
$dbline{$i};
$arrow = ($i==$line
for (@ini_INC) {
push @flags, '-I', $_;
}
+ push @flags, '-T' if ${^TAINT};
# Arrange for setting the old INC:
set_list("PERLDB_INC", @ini_INC);
if ($0 eq '-e') {
*dbline = $main::{'_<' . $file};
next unless %dbline or $postponed_file{$file};
(push @hard, $file), next
- if $file =~ /^\(eval \d+\)$/;
+ if $file =~ /^\(\w*eval/;
my @add;
@add = %{$postponed_file{$file}}
if $postponed_file{$file};
}
}
next CMD; };
+ $cmd =~ /^\@\s*(.*\S)/ && do {
+ if (open my $fh, $1) {
+ push @cmdfhs, $fh;
+ }
+ else {
+ &warn("Can't execute `$1': $!\n");
+ }
+ next CMD; };
$cmd =~ /^\|\|?\s*[^|]/ && do {
if ($pager =~ /^\|/) {
open(SAVEOUT,">&STDOUT") || &warn("Can't save STDOUT");
$evalarg = "\$^D = \$^D | \$DB::db_stop;\n$cmd"; &eval;
if ($onetimeDump) {
$onetimeDump = undef;
+ $onetimedumpDepth = undef;
} elsif ($term_pid == $$) {
print $OUT "\n";
}
my $pl = '';
die "Line $i$filename_error not breakable.\n" if $dbline[$i] == 0;
$had_breakpoints{$filename} |= 1;
- $dbline{$i} =~ s/^[^\0]*/$cond/ if $dbline{$i};
+ if ($dbline{$i}) { $dbline{$i} =~ s/^[^\0]*/$cond/; }
+ else { $dbline{$i} = $cond; }
}
sub cmd_b_line {
# The following takes its argument via $evalarg to preserve current @_
-sub eval {
- # 'my' would make it visible from user code
- # but so does local! --tchrist [... into @DB::res, not @res. IZ]
- local @res;
- {
- local $otrace = $trace;
- local $osingle = $single;
- local $od = $^D;
- { ($evalarg) = $evalarg =~ /(.*)/s; }
- @res = eval "$usercontext $evalarg;\n"; # '\n' for nice recursive debug
- $trace = $otrace;
- $single = $osingle;
- $^D = $od;
- }
- my $at = $@;
- local $saved[0]; # Preserve the old value of $@
- eval { &DB::save };
- if (defined($at)) {
- print $OUT $at;
- } elsif ($onetimeDump eq 'dump') {
- dumpit($OUT, \@res);
- } elsif ($onetimeDump eq 'methods') {
- methods($res[0]);
- }
- @res;
-}
-
sub postponed_sub {
my $subname = shift;
if ($postponed{$subname} =~ s/^break\s([+-]?\d+)\s+if\s//) {
do 'dumpvar.pl';
}
if (defined &main::dumpValue) {
- &main::dumpValue(shift);
+ my $v = shift;
+ my $maxdepth = shift || $option{dumpDepth};
+ $maxdepth = -1 unless defined $maxdepth; # -1 means infinite depth
+ &main::dumpValue($v, $maxdepth);
} else {
print $OUT "dumpvar.pl not available.\n";
}
return $tty;
}
-# This one resets $IN, $OUT itself
+# This example function resets $IN, $OUT itself
sub os2_get_fork_TTY {
- $^F = 40; # XXXX Fixme!
+ local $^F = 40; # XXXX Fixme!
my ($in1, $out1, $in2, $out2);
# Having -d in PERL5OPT would lead to a disaster...
local $ENV{PERL5OPT} = $ENV{PERL5OPT} if $ENV{PERL5OPT};
$ENV{PERL5OPT} =~ s/(?:^|(?<=\s))-d\b// if $ENV{PERL5OPT};
$ENV{PERL5OPT} =~ s/(?:^|(?<=\s))-d\B/-/ if $ENV{PERL5OPT};
- print $OUT "Making PERL5OPT->`$ENV{PERL5OPT}'.\n" if $ENV{PERL5OPT};
+ print $OUT "Making kid PERL5OPT->`$ENV{PERL5OPT}'.\n" if $ENV{PERL5OPT};
(my $name = $0) =~ s,^.*[/\\],,s;
- if ( pipe $in1, $out1 and pipe $in2, $out2 and
+ my @args;
+ if ( pipe $in1, $out1 and pipe $in2, $out2
# system P_SESSION will fail if there is another process
# in the same session with a "dependent" asynchronous child session.
- (($kpid = CORE::system 4, $^X, '-we', <<'ES', fileno $in1, fileno $out2, "Daughter Perl debugger $pids $name") >= 0 or warn "system P_SESSION: $!, $^E" and 0) # P_SESSION
-use Term::ReadKey;
+ and @args = ($rl, fileno $in1, fileno $out2,
+ "Daughter Perl debugger $pids $name") and
+ (($kpid = CORE::system 4, $^X, '-we', <<'ES', @args) >= 0 # P_SESSION
use OS2::Process;
-my $in = shift; # Read from here and pass through
+my ($rl, $in) = (shift, shift); # Read from $in and pass through
set_title pop;
system P_NOWAIT, $^X, '-we', <<EOS or die "Cannot start a grandkid";
open IN, '<&=$in' or die "open <&=$in: \$!";
my $out = shift;
open OUT, ">&=$out" or die "Cannot open &=$out for writing: $!";
select OUT; $| = 1;
-ReadMode 4; # Nodelay on kbd. Pipe is automatically nodelay...
-print while sysread STDIN, $_, 1<<16;
+require Term::ReadKey if $rl;
+Term::ReadKey::ReadMode(4) if $rl; # Nodelay on kbd. Pipe is automatically nodelay...
+print while sysread STDIN, $_, 1<<($rl ? 16 : 0);
ES
+ or warn "system P_SESSION: $!, $^E" and 0)
and close $in1 and close $out2 ) {
- $pidprompt = ''; # Shown anyway in titlebar
+ $pidprompt = ''; # Shown anyway in titlebar
reset_IN_OUT($in2, $out1);
$tty = '*reset*';
return ''; # Indicate that reset_IN_OUT is called
EOP
} elsif ($in ne '') {
TTY($in);
+ } else {
+ $console = ''; # Indicate no need to open-from-the-console
}
undef $fork_TTY;
}
}
local $frame = 0;
local $doret = -2;
+ while (@cmdfhs) {
+ my $line = CORE::readline($cmdfhs[-1]);
+ defined $line ? (print $OUT ">> $line" and return $line)
+ : close pop @cmdfhs;
+ }
if (ref $OUT and UNIVERSAL::isa($OUT, 'IO::Socket::INET')) {
$OUT->write(join('', @_));
my $stuff;
} else {
$val = $option{$opt};
}
+ $val = $default unless defined $val;
$val
}
# too dangerous to let intuitive usage overwrite important things
# defaultion should never be the default
my %opt_needs_val = map { ( $_ => 1 ) } qw{
- arrayDepth hashDepth LineInfo maxTraceLen ornaments
+ dumpDepth arrayDepth hashDepth LineInfo maxTraceLen ornaments windowSize
pager quote ReadLine recallCommand RemotePort ShellBang TTY
};
while (length) {
}
&warn("Too late to set TTY, enabled on next `R'!\n") if $term and @_;
# Useful if done through PERLDB_OPTS:
- $tty = shift if @_;
+ $console = $tty = shift if @_;
$tty or $console;
}
. ( $rc eq $sh ? "" : "
B<$psh> [I<cmd>] Run I<cmd> in subshell (forces \"\$SHELL -c 'cmd'\")." ) . "
See 'B<O> I<shellBang>' too.
+B<@>I<file> Execute I<file> containing debugger commands (may nest).
B<H> I<-number> Display last number commands (default all).
B<p> I<expr> Same as \"I<print {DB::OUT} expr>\" in current package.
B<|>I<dbcmd> Run debugger command, piping DB::OUT to current pager.
if ($dieLevel < 2) {
die @_ if $^S; # in eval propagate
}
- eval { require Carp } if defined $^S; # If error/warning during compilation,
+ # No need to check $^S, eval is much more robust nowadays
+ eval { require Carp }; #if defined $^S;# If error/warning during compilation,
# require may be broken.
die(@_, "\nCannot print stack trace, load with -MCarp option to see stack")
# inside DB::DB, but not in Carp).
my ($mysingle,$mytrace) = ($single,$trace);
$single = 0; $trace = 0;
- my $mess = Carp::longmess(@_);
+ my $mess = "@_";
+ {
+ package Carp; # Do not include us in the list
+ eval {
+ $mess = Carp::longmess(@_);
+ };
+ }
($single,$trace) = ($mysingle,$mytrace);
die $mess;
}
sub CvGV_name_or_bust {
my $in = shift;
return if $skipCvGV; # Backdoor to avoid problems if XS broken...
+ return unless ref $in;
$in = \&$in; # Hard reference...
eval {require Devel::Peek; 1} or return;
my $gv = Devel::Peek::CvGV($in) or return;
}
sub setman {
- $doccmd = $^O !~ /^(?:MSWin32|VMS|os2|dos|amigaos|riscos|MacOS)\z/s
+ $doccmd = $^O !~ /^(?:MSWin32|VMS|os2|dos|amigaos|riscos|MacOS|NetWare)\z/s
? "man" # O Happy Day!
: "perldoc"; # Alas, poor unfortunates
}
print $OUT "Use `q' to quit or `R' to restart. `h q' for details.\n"
}
+sub clean_ENV {
+ if (defined($ini_pids)) {
+ $ENV{PERLDB_PIDS} = $ini_pids;
+ } else {
+ delete($ENV{PERLDB_PIDS});
+ }
+}
+
END {
$finished = 1 if $inhibit_exit; # So that some keys may be disabled.
$fall_off_end = 1 unless $inhibit_exit;