diff --git a/lib/perl5db.pl b/lib/perl5db.pl index c6678543c68e..914fe57858de 100644 --- a/lib/perl5db.pl +++ b/lib/perl5db.pl @@ -2033,19 +2033,12 @@ sub _DB__handle_c_command { # Qualify it to the current package unless it's # already qualified. $subname = $package . "::" . $subname - unless $subname =~ /::/; + unless $subname =~ /::/; - # find_sub will return "file:line_number" corresponding - # to where the subroutine is defined; we call find_sub, - # break up the return value, and assign it in one - # operation. - ( $file, $i ) = ( find_sub($subname) =~ /^(.*):(.*)$/ ); - - # Force the line number to be numeric. - $i = $i + 0; + my ($file, $s, $e) = do { subroutine_first_breakable_line($subname) }; # If we got a line number, we found the sub. - if ($i) { + if ($s) { # Switch all the debugger's internals around so # we're actually working with that file. @@ -2058,19 +2051,18 @@ sub _DB__handle_c_command { # Scan forward to the first executable line # after the 'sub whatever' line. $max = $#dbline; - my $_line_num = $i; - while ($dbline[$_line_num] == 0 && $_line_num< $max) + my $_line_num = $s; + while ($dbline[$_line_num] == 0 && $_line_num <= $e) { $_line_num++; } $i = $_line_num; } ## end if ($i) - - # We didn't find a sub by that name. else { - print $OUT "Subroutine $subname not found.\n"; - next CMD; + print $OUT $@; + next CMD; } + } ## end if ($subname =~ /\D/) # At this point, either the subname was all digits (an @@ -5389,6 +5381,61 @@ sub subroutine_filename_lines { return (find_sub($subname) =~ /^(.*):(\d+)-(\d+)$/); } ## end sub subroutine_filename_lines +=head2 subroutine_first_breakable(subname) + +Return the file and line number of the first breakable line in +subname. + +Throws an error message if subname is not breakable, or cannot be +found. + +=cut + +sub _first_breakable_via_B { + my ( $subname ) = @_; + + my $cv = do { + no strict "refs"; + *$subname{CODE}; + }; + ref $cv eq "CODE" + or return; + + eval { require B; 1 } + or return; + + my $bcv = B::svref_2object($cv); + + # can't break on an XSUB + $bcv->XSUB + and die "Cannot break on XSUB $subname\n"; + + my $op = $bcv->START; + unless ($op->isa("B::NULL")) { + while (!$op->isa("B::NULL") && $op->name ne "dbstate") { + $op = $op->next; + } + + unless ($op->isa("B::NULL")) { + return ( $op->file, $op->line, $op->line ); + } + } + return; +} + +sub subroutine_first_breakable_line { + my ( $subname ) = @_; + + my ($file, $s, $e) = _first_breakable_via_B($subname); + unless ($file) { + # at the very least this allows miniperl to debug + ( $file, $s, $e ) = subroutine_filename_lines($subname) + or die "Subroutine $subname not found.\n"; + } + + return ($file, $s, $e ); +} + =head3 break_subroutine(subname) (API) Places a break on the first line possible in the specified subroutine. Uses @@ -5401,9 +5448,8 @@ sub break_subroutine { my $subname = shift; # Get filename, start, and end. - my ( $file, $s, $e ) = subroutine_filename_lines($subname) - or die "Subroutine $subname not found.\n"; - + my ( $file, $s, $e ) = eval { subroutine_first_breakable_line($subname) } + or die $@; # Null condition changes to '1' (always true). my $cond = @_ ? shift(@_) : 1; @@ -8849,7 +8895,7 @@ =head2 C Tries to use C<%sub> first; if it can't find it there, it tries building a reference to the subroutine and uses C to locate it, -loading it into C<@sub> as a side effect (XXX I think). If it can't find it +loading it into C<%sub> as a side effect (XXX I think). If it can't find it this way, it brute-force searches C<%sub>, checking for identical references. =cut diff --git a/lib/perl5db.t b/lib/perl5db.t index 8cbdcf155495..783c67459dc8 100644 --- a/lib/perl5db.t +++ b/lib/perl5db.t @@ -3491,6 +3491,65 @@ EOS $wrapper->output_like(qr/\bOK\b/, "check the line is IOK"); } +{ + # https://github.com/Perl/perl5/issues/799 + my $prog = <<'EOS'; +sub problem { + $SIG{__DIE__} = sub { + die " will set a break point here.\n"; + }; # The break point _should_ be set here. + warn "This line will run even if you enter .\n"; +} +&problem; +EOS + + my $wrapper = DebugWrap->new( + { + cmds => + [ + "b problem", + "c", + "q" + ], + prog => \$prog + } + ); + $wrapper->contents_like(qr/The break point _should_/, "break at right place (b)"); + $wrapper->output_unlike(qr/This line will run even if you enter \./, + "didn't run the wrong code (b)"); + + $wrapper = DebugWrap->new( + { + cmds => + [ + "c problem", + "q" + ], + prog => \$prog + } + ); + $wrapper->contents_like(qr/The break point _should_/, "break at right place (c)"); + $wrapper->output_unlike(qr/This line will run even if you enter \./, + "didn't run the wrong code (c)"); +} + +{ + my $wrapper = DebugWrap->new( + { + cmds => + [ + "b B::svref_2object", + "q" + ], + prog => \<<'EOS' +use B; +print "Hello\n"; +EOS + } + ); + $wrapper->contents_like(qr/Cannot break on XSUB B::svref_2object/, "can't break on XSUB"); +} + done_testing(); END {