Skip to content

Commit

Permalink
perl5db.pl: b subname and c subname break on first executable line
Browse files Browse the repository at this point in the history
This currently doesn't try to handle "b postpone subname" since that
internally has an offset function that doesn't really work
with this implementation.

This is a partial fix for Perl#799
  • Loading branch information
tonycoz committed Jul 4, 2024
1 parent 07523af commit 8c516c3
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 20 deletions.
86 changes: 66 additions & 20 deletions lib/perl5db.pl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -8849,7 +8895,7 @@ =head2 C<find_sub>
Tries to use C<%sub> first; if it can't find it there, it tries building a
reference to the subroutine and uses C<CvGV_name_or_bust> 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
Expand Down
59 changes: 59 additions & 0 deletions lib/perl5db.t
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<b problem> will set a break point here.\n";
}; # The break point _should_ be set here.
warn "This line will run even if you enter <c problem>.\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 <c problem>\./,
"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 <c problem>\./,
"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 {
Expand Down

0 comments on commit 8c516c3

Please sign in to comment.