B-Debugger

 view release on metacpan or  search on metacpan

lib/B/Debugger.pm  view on Meta::CPAN

use constant DBG_NEXT => 3;
use constant DBG_QUIT => 4;
our ($next_op, %break_op, @ops, %opnames, %opt, $last_in);

sub debugger_banner {
  our $maxo = Opcode::opcodes();
  for my $o (grep(/^-/, @_)) { $opt{$o}++; }
  print "\nB::Debugger $XS_VERSION - optree debugger. h for help\n";
}
sub debugger_help {
  print "Usage:\n";
  my @left = (
	      "n [n] next op",			#1
	      "c [n] continue (until)",		#2
	      "b <n> break at op",		#3
	      "s     step into kids",		#4
	      "sib   step to next sibling",	#5
	      "u [n] up",			#6
	      "g <n> goto",			#7
	      "h     help",			#8
	      "q     quit debugger, execute",	#9
	      );
  my @right = (
	       "l [n|x-y]     list ops",	#1
	       "d|Debug       op",		#2
	       "o|Concise     op",		#3
	       "f|Flags       op",		#4
	       "x|eval expr",			#5
	       "[sahpicg]v<n> n-th global var: sv1,",	#6
	       "pad <n>       n-th pad variable (my)",	#7
	       "",					#8
	      "exit           quit with no execution",	#9
	       );
  my $max = $#left > $#right ? $#left : $#right;
  for my $i (0 .. $max) {
    print sprintf("%-35s %s\n", $left[$i], $right[$i]);
  }
}

# numeric in the valid range 0..PL_maxo or a valid opname
sub valid_breakpoint {
  $b = shift;
  return $b if $b =~ /^\d+$/ and $op >= 0 and $op <= $maxo;
  unless (%opnames) {
    for my $opnum (0 .. $maxo ) {
      my $ppname = B::ppname($opnum); # pp_{name}
      $opnames{substr($ppname,3)} = $opnum;
    }
  }
  return exists $opnames{lc($b)} ? lc($b) : undef;
}

sub debugger_prompt {
  my $op = $_[0]; # need to manipulate it
  print "op $opidx ",$op->name,"\n"; # ?: full concise, size, flags?
  print "> ";
  my $in = readline(*STDIN);
  chomp $in;
  $in = $last_in unless $in;
  $last_in = $in;
  # $in =~ s/[:cntrl:]//g; # strip control chars, cursor keys
  if ($in =~ /^(h|help)$/) { debugger_help; return DBG_SAME; }
  elsif ($in =~ /^(q|quit)$/) { print "quit\nexecuting...\n"; return DBG_QUIT; }
  elsif ($in =~ /^exit$/) { print "exit\n"; exit; } # FIXME! Add an exit hook into INIT?
  elsif ($in =~ /^(x|eval)\s+(.+)$/) { print (eval "$2"),"\n"; return DBG_SAME; }
  elsif ($in =~ /^(n|next)$/) {
    print "..next\n" if $opt{debug};
    return DBG_NEXT;
  }
  elsif ($in =~ /^(n|next)\s+(\w+)$/) { # count
    my $count = valid_breakpoint($2);
    print "..next $count\n" if $opt{debug};
    $count = ($count and $count =~ /^\d$/);
    unless ($count) { print "invalid count \"$count\"\n"; return DBG_NEXT; }
    $break_op{$opidx + $count} = 2;
    return DBG_CONT;
  }
  elsif ($in =~ /^(b|break)\s+?(\w+)?$/) { # opidx or name?
    my $b = valid_breakpoint($2);
    unless ($b) { print "invalid breakpoint \"$b\"\n"; return DBG_SAME; }
    if (exists $break_op{$b}) { undef $break_op{$b};
				 print "breakpoint $b removed\n"; }
    else { $break_op{$b} = 1; print "breakpoint $b added\n"; }
    return DBG_SAME;
  }
  elsif ($in =~ /^(c|cont)$/) {
    return DBG_CONT;
  }
  elsif ($in =~ /^(c|cont)\s+(\w+)$/) { # arg <opidx> or next matching name?
    my $b = valid_breakpoint($2);
    print "..cont $b\n" if $opt{debug};
    unless ($b) { print "invalid breakpoint \"$b\"\n"; return DBG_SAME; }
    $break_op{$b} = 2 if $b; # 2: delete this op at the next break
    return DBG_CONT;
  }
  elsif ($in =~ /^(u|up)\s*(\w+)?$/) {
    if ($2 and valid_breakpoint($2)) {
      my $b = valid_breakpoint($2);
      unless ($b) { print "invalid opidx \"$b\"\n"; return DBG_SAME; }
      $opidx = $b;
    } else {
      $opidx--;
    }
    if (exists $ops[$opidx]) { $_[0] = $ops[$opidx]; } # rewind
    print "up to $opidx\n" if $opt{debug};
    return DBG_SAME;
  }
  elsif ($in =~ /^(g|goto)\s+(\w+)$/) { # arg <opidx>
    my $b = valid_breakpoint($2);
    unless ($b) { print "invalid breakpoint \"$b\"\n"; return DBG_SAME; }
    $break_op{$b} = 2; # 2: delete this op at the next break
    return DBG_CONT;
  }
  elsif ($in =~ /^(s|step)$/) {
    if ($op->flags & OPf_KIDS) {
      $opidx++;
      print "..step into kids: $op->first->name\n" if $opt{debug};
      return debugger_walkoptree($op->first, \&debugger_prompt, [ $op->first ])
    } else {
      print "no kids\n";
      return DBG_SAME;



( run in 3.728 seconds using v1.01-cache-2.11-cpan-cdf2f3d4e48 )