# Debugger for Perl 5.00x; perl5db.pl patch level:
-$VERSION = 1.03;
+$VERSION = 1.05;
$header = "perl5db.pl version $VERSION";
# Enhanced by ilya@math.ohio-state.edu (Ilya Zakharevich)
# LineInfo - file or pipe to print line number info to. If it is a
# pipe, a short "emacs like" message is used.
#
+# RemotePort - host:port to connect to on remote host for remote debugging.
+#
# Example $rcfile: (delete leading hashes!)
#
# &parse_options("NonStop=1 LineInfo=db.out");
TTY noTTY ReadLine NonStop LineInfo maxTraceLen
recallCommand ShellBang pager tkRunning ornaments
signalLevel warnLevel dieLevel inhibit_exit
- ImmediateStop);
+ ImmediateStop bareStringify
+ RemotePort);
%optionVars = (
hashDepth => \$dumpvar::hashDepth,
undefPrint => \$dumpvar::printUndef,
globPrint => \$dumpvar::globPrint,
UsageOnly => \$dumpvar::usageOnly,
+ bareStringify => \$dumpvar::bareStringify,
frame => \$frame,
AutoTrace => \$trace,
inhibit_exit => \$inhibit_exit,
maxTraceLen => \$maxtrace,
ImmediateStop => \$ImmediateStop,
+ RemotePort => \$remoteport,
);
%optionAction = (
dieLevel => \&dieLevel,
tkRunning => \&tkRunning,
ornaments => \&ornaments,
+ RemotePort => \&RemotePort,
);
%optionRequire = (
warnLevel($warnLevel);
dieLevel($dieLevel);
signalLevel($signalLevel);
-&pager(defined($ENV{PAGER}) ? $ENV{PAGER} : "|more") unless defined $pager;
+&pager((defined($ENV{PAGER})
+ ? $ENV{PAGER}
+ : ($^O eq 'os2'
+ ? 'cmd /c more'
+ : 'more'))) unless defined $pager;
&recallCommand("!") unless defined $prc;
&shellBang("!") unless defined $psh;
$maxtrace = 400 unless defined $maxtrace;
#require Term::ReadLine;
- if (-e "/dev/tty") {
+ if ($^O eq 'cygwin') {
+ # /dev/tty is binary. use stdin for textmode
+ undef $console;
+ } elsif (-e "/dev/tty") {
$console = "/dev/tty";
} elsif ($^O eq 'dos' or -e "con" or $^O eq 'MSWin32') {
$console = "con";
$console = undef;
}
+ if ($^O eq 'epoc') {
+ $console = undef;
+ }
+
$console = $tty if defined $tty;
- if (defined $console) {
- open(IN,"+<$console") || open(IN,"<$console") || open(IN,"<&STDIN");
- open(OUT,"+>$console") || open(OUT,">$console") || open(OUT,">&STDERR")
- || open(OUT,">&STDOUT"); # so we don't dongle stdout
- } else {
- open(IN,"<&STDIN");
- open(OUT,">&STDERR") || open(OUT,">&STDOUT"); # so we don't dongle stdout
- $console = 'STDIN/OUT';
+ if (defined $remoteport) {
+ require IO::Socket;
+ $OUT = new IO::Socket::INET( Timeout => '10',
+ PeerAddr => $remoteport,
+ Proto => 'tcp',
+ );
+ if (!$OUT) { die "Could not create socket to connect to remote host."; }
+ $IN = $OUT;
}
- # so open("|more") can read from STDOUT and so we don't dingle stdin
- $IN = \*IN;
+ else {
+ if (defined $console) {
+ open(IN,"+<$console") || open(IN,"<$console") || open(IN,"<&STDIN");
+ open(OUT,"+>$console") || open(OUT,">$console") || open(OUT,">&STDERR")
+ || open(OUT,">&STDOUT"); # so we don't dongle stdout
+ } else {
+ 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;
+ $OUT = \*OUT;
+ }
select($OUT);
$| = 1; # for DB::OUT
select(STDOUT);
print $OUT ("Emacs support ",
$emacs ? "enabled" : "available",
".\n");
- print $OUT "\nEnter h or `h h' for help.\n\n";
+ print $OUT "\nEnter h or `h h' for help, run `perldoc perldebug' for more help.\n\n";
}
}
# _After_ the perl program is compiled, $single is set to 1:
if ($single and not $second_time++) {
if ($runnonstop) { # Disable until signal
- for ($i=0; $i <= $#stack; ) {
+ for ($i=0; $i <= $stack_depth; ) {
$stack[$i++] &= ~1;
}
$single = 0;
if ($trace & 2) {
for (my $n = 0; $n <= $#to_watch; $n++) {
$evalarg = $to_watch[$n];
+ local $onetimeDump; # Do not output results
my ($val) = &eval; # Fix context (&eval is doing array)?
$val = ( (defined $val) ? "'$val'" : 'undef' );
if ($val ne $old_watch[$n]) {
$was_signal = $signal;
$signal = 0;
if ($single || ($trace & 1) || $was_signal) {
- $term || &setterm;
if ($emacs) {
$position = "\032\032$filename:$line:0\n";
print $LINEINFO $position;
} elsif ($package eq 'DB::fake') {
+ $term || &setterm;
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
$package = 'main';
- $usercontext = '($@, $!, $,, $/, $\, $^W) = @saved;' .
+ $usercontext = '($@, $!, $^E, $,, $/, $\, $^W) = @saved;' .
"package $package;"; # this won't let them modify, alas
} else {
$sub =~ s/\'/::/;
$position = "$prefix$line$infix$dbline[$line]$after";
}
if ($frame) {
- print $LINEINFO ' ' x $#stack, "$line:\t$dbline[$line]$after";
+ print $LINEINFO ' ' x $stack_depth, "$line:\t$dbline[$line]$after";
} else {
print $LINEINFO $position;
}
$incr_pos = "$prefix$i$infix$dbline[$i]$after";
$position .= $incr_pos;
if ($frame) {
- print $LINEINFO ' ' x $#stack, "$i:\t$dbline[$i]$after";
+ print $LINEINFO ' ' x $stack_depth, "$i:\t$dbline[$i]$after";
} else {
print $LINEINFO $incr_pos;
}
foreach $evalarg (@$pre) {
&eval;
}
- print $OUT $#stack . " levels deep in subroutine calls!\n"
+ print $OUT $stack_depth . " levels deep in subroutine calls!\n"
if $single & 4;
$start = $line;
$incr = -1; # for backward motion.
- @typeahead = @$pretype, @typeahead;
+ @typeahead = (@$pretype, @typeahead);
CMD:
while (($term || &setterm),
($term_pid == $$ or &resetterm),
}
};
$cmd =~ s/^l\s+-\s*$/-/;
- $cmd =~ /^l\b\s*([\':A-Za-z_][\':\w]*)/ && do {
+ $cmd =~ /^([lb])\b\s*(\$.*)/s && do {
+ $evalarg = $2;
+ my ($s) = &eval;
+ print($OUT "Error: $@\n"), next CMD if $@;
+ $s = CvGV_name($s);
+ print($OUT "Interpreted as: $1 $s\n");
+ $cmd = "$1 $s";
+ };
+ $cmd =~ /^l\b\s*([\':A-Za-z_][\':\w]*(\[.*\])?)/s && do {
$subname = $1;
$subname =~ s/\'/::/;
$subname = $package."::".$subname
unless $subname =~ /::/;
$subname = "main".$subname if substr($subname,0,2) eq "::";
- @pieces = split(/:/,find_sub($subname));
+ @pieces = split(/:/,find_sub($subname) || $sub{$subname});
$subrange = pop @pieces;
$file = join(':', @pieces);
if ($file ne $filename) {
+ print $OUT "Switching to file '$file'.\n"
+ unless $emacs;
*dbline = $main::{'_<' . $file};
$max = $#dbline;
$filename = $file;
$arrow .= 'b' if $stop;
$arrow .= 'a' if $action;
print $OUT "$i$arrow\t", $dbline[$i];
- last if $signal;
+ $i++, last if $signal;
}
+ print $OUT "\n" unless $dbline[$i-1] =~ /\n$/;
}
$start = $i; # remember in case they want more
$start = $max if $start > $max;
for ($i = 1; $i <= $max; $i++) {
if (defined $dbline{$i}) {
- print "$file:\n" unless $was++;
+ print $OUT "$file:\n" unless $was++;
print $OUT " $i:\t", $dbline[$i];
($stop,$action) = split(/\0/, $dbline{$i});
print $OUT " break if (", $stop, ")\n"
$postponed{$subname} = $break
? "break +0 if $cond" : "compile";
next CMD; };
- $cmd =~ /^b\b\s*([':A-Za-z_][':\w]*)\s*(.*)/ && do {
+ $cmd =~ /^b\b\s*([':A-Za-z_][':\w]*(?:\[.*\])?)\s*(.*)/ && do {
$subname = $1;
$cond = $2 || '1';
$subname =~ s/\'/::/;
($file,$i) = (find_sub($subname) =~ /^(.*):(.*)$/);
$i += 0;
if ($i) {
- $filename = $file;
- *dbline = $main::{'_<' . $filename};
+ local $filename = $file;
+ local *dbline = $main::{'_<' . $filename};
$had_breakpoints{$filename} = 1;
$max = $#dbline;
++$i while $dbline[$i] == 0 && $i < $max;
$cmd =~ /^c\b\s*([\w:]*)\s*$/ && do {
end_report(), next CMD if $finished and $level <= 1;
$subname = $i = $1;
+ # Probably not needed, since we finish an interactive
+ # sub-session anyway...
+ # local $filename = $filename;
+ # local *dbline = *dbline; # XXX Would this work?!
if ($i =~ /\D/) { # subroutine name
$subname = $package."::".$subname
unless $subname =~ /::/;
}
$dbline{$i} =~ s/($|\0)/;9$1/; # add one-time-only b.p.
}
- for ($i=0; $i <= $#stack; ) {
+ for ($i=0; $i <= $stack_depth; ) {
$stack[$i++] &= ~1;
}
last CMD; };
$cmd =~ /^r$/ && do {
end_report(), next CMD if $finished and $level <= 1;
- $stack[$#stack] |= 1;
- $doret = $option{PrintRet} ? $#stack - 1 : -2;
+ $stack[$stack_depth] |= 1;
+ $doret = $option{PrintRet} ? $stack_depth - 1 : -2;
last CMD; };
$cmd =~ /^R$/ && do {
print $OUT "Warning: some settings and command-line options may be lost!\n";
pop(@hist) if length($cmd) > 1;
$i = $1 ? ($#hist-($2?$2:1)) : ($2?$2:$#hist);
$cmd = $hist[$i];
- print $OUT $cmd;
+ print $OUT $cmd, "\n";
redo CMD; };
$cmd =~ /^$sh$sh\s*([\x00-\xff]*)/ && do {
&system($1);
next CMD;
}
$cmd = $hist[$i];
- print $OUT $cmd;
+ print $OUT $cmd, "\n";
redo CMD; };
$cmd =~ /^$sh$/ && do {
&system($ENV{SHELL}||"/bin/sh");
if (length($sub) > 10 && substr($sub, -10, 10) eq '::AUTOLOAD') {
$al = " for $$sub";
}
- push(@stack, $single);
+ local $stack_depth = $stack_depth + 1; # Protect from non-local exits
+ $#stack = $stack_depth;
+ $stack[-1] = $single;
$single &= 1;
- $single |= 4 if $#stack == $deep;
+ $single |= 4 if $stack_depth == $deep;
($frame & 4
- ? ( (print $LINEINFO ' ' x ($#stack - 1), "in "),
+ ? ( (print $LINEINFO ' ' x ($stack_depth - 1), "in "),
# Why -1? But it works! :-(
print_trace($LINEINFO, -1, 1, 1, "$sub$al") )
- : print $LINEINFO ' ' x ($#stack - 1), "entering $sub$al\n") if $frame;
+ : print $LINEINFO ' ' x ($stack_depth - 1), "entering $sub$al\n") if $frame;
if (wantarray) {
@ret = &$sub;
- $single |= pop(@stack);
+ $single |= $stack[$stack_depth--];
($frame & 4
- ? ( (print $LINEINFO ' ' x $#stack, "out "),
+ ? ( (print $LINEINFO ' ' x $stack_depth, "out "),
print_trace($LINEINFO, -1, 1, 1, "$sub$al") )
- : print $LINEINFO ' ' x $#stack, "exited $sub$al\n") if $frame & 2;
- if ($doret eq $#stack or $frame & 16) {
- my $fh = ($doret eq $#stack ? $OUT : $LINEINFO);
- print $fh ' ' x $#stack if $frame & 16;
+ : print $LINEINFO ' ' x $stack_depth, "exited $sub$al\n") if $frame & 2;
+ if ($doret eq $stack_depth or $frame & 16) {
+ my $fh = ($doret eq $stack_depth ? $OUT : $LINEINFO);
+ print $fh ' ' x $stack_depth if $frame & 16;
print $fh "list context return from $sub:\n";
dumpit($fh, \@ret );
$doret = -2;
} else {
&$sub; undef $ret;
};
- $single |= pop(@stack);
+ $single |= $stack[$stack_depth--];
($frame & 4
- ? ( (print $LINEINFO ' ' x $#stack, "out "),
+ ? ( (print $LINEINFO ' ' x $stack_depth, "out "),
print_trace($LINEINFO, -1, 1, 1, "$sub$al") )
- : print $LINEINFO ' ' x $#stack, "exited $sub$al\n") if $frame & 2;
- if ($doret eq $#stack or $frame & 16 and defined wantarray) {
- my $fh = ($doret eq $#stack ? $OUT : $LINEINFO);
- print $fh (' ' x $#stack) if $frame & 16;
+ : print $LINEINFO ' ' x $stack_depth, "exited $sub$al\n") if $frame & 2;
+ if ($doret eq $stack_depth or $frame & 16 and defined wantarray) {
+ 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");
# The following takes its argument via $evalarg to preserve current @_
sub eval {
- my @res;
+ local @res; # 'my' would make it visible from user code
{
- local (@stack) = @stack; # guard against recursive debugging
- my $otrace = $trace;
- my $osingle = $single;
- my $od = $^D;
+ local $otrace = $trace;
+ local $osingle = $single;
+ local $od = $^D;
@res = eval "$usercontext $evalarg;\n"; # '\n' for nice recursive debug
$trace = $otrace;
$single = $osingle;
$filename =~ s/^_<//;
$signal = 1, print $OUT "'$filename' loaded...\n"
if $break_on_load{$filename};
- print $LINEINFO ' ' x $#stack, "Package $filename.\n" if $frame;
+ print $LINEINFO ' ' x $stack_depth, "Package $filename.\n" if $frame;
return unless $postponed_file{$filename};
$had_breakpoints{$filename}++;
#%dbline = %{$postponed_file{$filename}}; # Cannot be done: unsufficient magic
sub setterm {
local $frame = 0;
local $doret = -2;
- local @stack = @stack; # Prevent growth by failing `use'.
eval { require Term::ReadLine } or die $@;
if ($notty) {
if ($tty) {
}
local $frame = 0;
local $doret = -2;
- $term->readline(@_);
+ if (ref $OUT and UNIVERSAL::isa($OUT, 'IO::Socket::INET')) {
+ print $OUT @_;
+ my $stuff;
+ $IN->recv( $stuff, 2048 );
+ $stuff;
+ }
+ else {
+ $term->readline(@_);
+ }
}
sub dump_option {
$rl;
}
+sub RemotePort {
+ if ($term) {
+ &warn("Too late to set RemotePort, enabled on next 'R'!\n") if @_;
+ }
+ $remoteport = shift if @_;
+ $remoteport;
+}
+
sub tkRunning {
if ($ {$term->Features}{tkRunning}) {
return $term->tkRunning(@_);
}
$version{$file} .= $INC{$file};
}
- do 'dumpvar.pl' unless defined &main::dumpValue;
- if (defined &main::dumpValue) {
- local $frame = 0;
- &main::dumpValue(\%version);
- } else {
- print $OUT "dumpvar.pl not available.\n";
- }
+ dumpit($OUT,\%version);
}
sub sethelp {
B<l> I<min>B<->I<max> List lines I<min> through I<max>.
B<l> I<line> List single I<line>.
B<l> I<subname> List first window of lines from subroutine.
+B<l> I<$var> List first window of lines from subroutine referenced by I<$var>.
B<l> List next window of lines.
B<-> List previous window of lines.
B<w> [I<line>] List window around I<line>.
B<.> Return to the executed line.
-B<f> I<filename> Switch to viewing I<filename>. Must be loaded.
+B<f> I<filename> Switch to viewing I<filename>. File must be already loaded.
+ I<filename> may be either the full name of the file, or a regular
+ expression matching the full file name:
+ B<f> I</home/me/foo.pl> and B<f> I<oo\\.> may access the same file.
+ Evals (with saved bodies) are considered to be filenames:
+ B<f> I<(eval 7)> and B<f> I<eval 7\\b> access the body of the 7th eval
+ (in the order of execution).
B</>I<pattern>B</> Search forwards for I<pattern>; final B</> is optional.
B<?>I<pattern>B<?> Search backwards for I<pattern>; final B<?> is optional.
B<L> List all breakpoints and actions.
I<condition> breaks if it evaluates to true, defaults to '1'.
B<b> I<subname> [I<condition>]
Set breakpoint at first line of subroutine.
+B<b> I<$var> Set breakpoint at first line of subroutine referenced by I<$var>.
B<b> B<load> I<filename> Set breakpoint on `require'ing the given file.
B<b> B<postpone> I<subname> [I<condition>]
Set breakpoint at first line of subroutine after
I<signalLevel> I<warnLevel> I<dieLevel>: level of verbosity;
I<inhibit_exit> Allows stepping off the end of the script.
I<ImmediateStop> Debugger should stop as early as possible.
+ I<RemotePort>: Remote hostname:port for remote debugging
The following options affect what happens with B<V>, B<X>, and B<x> commands:
I<arrayDepth>, I<hashDepth>: print only first N elements ('' for all);
I<compactDump>, I<veryCompact>: change style of array and hash dump;
I<DumpPackages>: dump symbol tables of packages;
I<DumpReused>: dump contents of \"reused\" addresses;
I<quote>, I<HighBit>, I<undefPrint>: change style of string dump;
+ I<bareStringify>: Do not print the overload-stringified value;
Option I<PrintRet> affects printing of return value after B<r> command,
I<frame> affects printing messages on entry and exit from subroutines.
I<AutoTrace> affects printing messages on every possible breaking point.
I<ornaments> affects screen appearance of the command line.
During startup options are initialized from \$ENV{PERLDB_OPTS}.
You can put additional initialization options I<TTY>, I<noTTY>,
- I<ReadLine>, and I<NonStop> there (or use `B<R>' after you set them).
+ I<ReadLine>, I<NonStop>, and I<RemotePort> there (or use
+ `B<R>' after you set them).
B<<> I<expr> Define Perl command to run before each prompt.
B<<<> I<expr> Add to the list of Perl commands to run before each prompt.
B<>> I<expr> Define Perl command to run after each prompt.
history, breakpoints and actions, debugger B<O>ptions
and the following command-line options: I<-w>, I<-I>, I<-e>.
B<h> [I<db_command>] Get help [on a specific debugger command], enter B<|h> to page.
+ Complete description of debugger is available in B<perldebug>
+ section of Perl documention
B<h h> Summary of debugger commands.
B<q> or B<^D> Quit. Set B<\$DB::finished = 0> to debug global destruction.
B<l> [I<ln>|I<sub>] List source code B<T> Stack trace
B<-> or B<.> List previous/current line B<s> [I<expr>] Single step [in expr]
B<w> [I<line>] List around line B<n> [I<expr>] Next, steps over subs
- B<f> I<filename> View source in file <B<CR>> Repeat last B<n> or B<s>
+ B<f> I<filename> View source in file <B<CR>/B<Enter>> Repeat last B<n> or B<s>
B</>I<pattern>B</> B<?>I<patt>B<?> Search forw/backw B<r> Return from subroutine
B<v> Show versions of modules B<c> [I<ln>|I<sub>] Continue until position
I<Debugger controls:> B<L> List break/watch/actions
B<O> [...] Set debugger options B<t> [I<expr>] Toggle trace [trace expr]
- B<<>[B<<>] or B<{>[B<{>] [I<cmd>] Do before prompt B<b> [I<ln>|I<event>] [I<cnd>] Set breakpoint
- B<>>[B<>>] [I<cmd>] Do after prompt B<b> I<sub> [I<cnd>] Set breakpoint for sub
+ B<<>[B<<>]|B<{>[B<{>]|B<>>[B<>>] [I<cmd>] Do pre/post-prompt B<b> [I<ln>|I<event>|I<sub>] [I<cnd>] Set breakpoint
B<$prc> [I<N>|I<pat>] Redo a previous command B<d> [I<ln>] or B<D> Delete a/all breakpoints
B<H> [I<-num>] Display last num commands B<a> [I<ln>] I<cmd> Do cmd before line
B<=> [I<a> I<val>] Define/list an alias B<W> I<expr> Add a watch expression
B<h> [I<db_cmd>] Get help on command B<A> or B<W> Delete all actions/watch
- B<|>[B<|>]I<dbcmd> Send output to pager B<$psh>\[B<$psh>\] I<syscmd> Run cmd in a subprocess
+ B<|>[B<|>]I<db_cmd> Send output to pager B<$psh>\[B<$psh>\] I<syscmd> Run cmd in a subprocess
B<q> or B<^D> Quit B<R> Attempt a restart
I<Data Examination:> B<expr> Execute perl code, also see: B<s>,B<n>,B<t> I<expr>
B<x>|B<m> I<expr> Evals expr in array context, dumps the result or lists methods.
B<S> [[B<!>]I<pat>] List subroutine names [not] matching pattern
B<V> [I<Pk> [I<Vars>]] List Variables in Package. Vars can be ~pattern or !pattern.
B<X> [I<Vars>] Same as \"B<V> I<current_package> [I<Vars>]\".
+I<More help for> B<db_cmd>I<:> Type B<h> I<cmd_letter> Run B<perldoc perldebug> for more help.
END_SUM
# ')}}; # Fix balance of Emacs parsing
}
$signalLevel;
}
+sub CvGV_name {
+ my $in = shift;
+ my $name = CvGV_name_or_bust($in);
+ defined $name ? $name : $in;
+}
+
+sub CvGV_name_or_bust {
+ my $in = shift;
+ return if $skipCvGV; # Backdoor to avoid problems if XS broken...
+ $in = \&$in; # Hard reference...
+ eval {require Devel::Peek; 1} or return;
+ my $gv = Devel::Peek::CvGV($in) or return;
+ *$gv{PACKAGE} . '::' . *$gv{NAME};
+}
+
sub find_sub {
my $subr = shift;
- return unless defined &$subr;
$sub{$subr} or do {
+ return unless defined &$subr;
+ my $name = CvGV_name_or_bust($subr);
+ my $data;
+ $data = $sub{$name} if defined $name;
+ return $data if defined $data;
+
+ # Old stupid way...
$subr = \&$subr; # Hard reference
my $s;
for (keys %sub) {
# @stack and $doret are needed in sub sub, which is called for DB::postponed.
# Triggers bug (?) in perl is we postpone this until runtime:
@postponed = @stack = (0);
+ $stack_depth = 0; # Localized $#stack
$doret = -2;
$frame = 0;
}