# -*- coding: utf-8 -*- # Copyright (C) 2011, 2012 Rocky Bernstein use strict; use warnings; use rlib '../../..'; use Devel::Trepan::Position; package Devel::Trepan::CmdProcessor; use English qw( -no_match_vars ); use constant SINGLE_STEPPING_EVENT => 1; use constant NEXT_STEPPING_EVENT => 2; use constant DEEP_RECURSION_EVENT => 4; use constant RETURN_EVENT => 32; # attr_accessor :stop_condition # String or nil. When not nil # # this has to eval non-nil # # in order to stop. # attr_accessor :stop_events # Set or nil. If not nil, only # # events in this set will be # # considered for stopping. This is # # like core.step_events (which # # could be used instead), but it is # # a set of event names rather than # # a bitmask and it is intended to # # be more temporarily changed via # # "step>" or "step!" commands. # attr_accessor :to_method sub continue($$) { my ($self, $args) = @_; $self->{skip_count} = -1; if ($self->{settings}{traceprint}) { $self->step(); return; } if (scalar @{$args} != 1) { # Form is: "continue" # my $(line_number, $condition, $negate) = # $self->breakpoint_position($self->{proc}{cmd_argstr}, 0); # return unless iseq && vm_offset; # $bp = $self->.breakpoint_offset($condition, $negate, 1); #return unless bp; $self->{leave_cmd_loop} = $self->{dbgr}->cont($args->[1]); } else { $self->{leave_cmd_loop} = $self->{dbgr}->cont; }; if ($self->{leave_cmd_loop}) { $self->{DB_running} = 1; $self->{DB_single} = 0; } } # sub quit(cmd='quit') # { # @next_level = 32000; # I'm guessing the stack size can't ever # # reach this # @next_thread = undef; # @core.skip_count = -1; # No more event stepping # @leave_cmd_loop = 1; # Break out of the processor command loop. # @settings[:autoirb] = 0; # @cmdloop_prehooks.delete_by_name('autoirb'); # @commands['quit'].run([cmd]); # } sub parse_next_step_suffix($$) { my ($self, $step_cmd) = @_; my $opts = {}; my $sigil = substr($step_cmd, -1); if ('-' eq $sigil) { $opts->{different_pos} = 0; } elsif ('+' eq $sigil) { $opts->{different_pos} = 1; } elsif ('=' eq $sigil) { $opts->{different_pos} = $self->{settings}{different}; # when ('!') { $opts->{stop_events} = {'raise' => 1} }; # when ('<') { $opts->{stop_events} = {'return' => 1}; } # when ('>') { # if (length($step_cmd) > 1 && substr($step_cmd, -2, 1) eq '<') { # $opts->{stop_events} = {'return' => 1 }; # } else { # $opts->{stop_events} = {'call' => 1; } # } # } } else { $opts->{different_pos} = $self->{settings}{different}; } return $opts; } # Does whatever setup needs to be done to set to ignore stepping # to the finish of the current method. sub finish($$) { my ($self, $level_count) = @_; $self->{leave_cmd_loop} = 1; $self->{dbgr}->finish($level_count); $self->{DB_running} = 1; $self->{skip_count} = -1; } sub next($$) { my ($self, $opts) = @_; $self->{different_pos} = $opts->{different_pos}; $self->{leave_cmd_loop} = 1; # NEXT_STEPPING_EVENT is sometimes broken. # $self->{DB_single} = NEXT_STEPPING_EVENT; $self->{next_level} = $self->{stack_size}; $self->{DB_single} = SINGLE_STEPPING_EVENT; $self->{DB_running} = 1; } sub step($$) { my ($self, $opts) = @_; $self->{different_pos} = $opts->{different_pos}; $self->{leave_cmd_loop} = 1; $self->{DB_single} = SINGLE_STEPPING_EVENT; $self->{next_level} = 30000; # Virtually infinite $self->{DB_running} = 1; } sub running_initialize($) { my $self = shift; $self->{stop_condition} = undef; $self->{stop_events} = undef; $self->{to_method} = undef; $self->{last_pos} = TrepanPosition->new(pkg => '', filename => '', line =>'', event=>''); } # Should we not stop here? # Some reasons for skipping: # - step count was given. # - We want to make sure we stop on a different line # - We want to stop only when some condition is reached (step util ...). sub is_stepping_skip($) { my $self = shift; if ($self->{skip_count} < 0) { return 1; } elsif ($self->{skip_count} > 0) { $self->{skip_count} --; return 1 } if ($self->{settings}{'debugskip'}) { $self->msg("diff: $self->{different_pos}, event : $self->{event}"); $self->msg("skip_count : $self->{skip_count}"); } my $frame = $self->{frame}; my $new_pos = TrepanPosition->new(pkg => $frame->{pkg}, filename => $frame->{file}, line => $frame->{line}, event => $self->{event}); my $skip_val = 0; # If the last stop was a breakpoint, don't stop again if we are at # the same location with a line event. my $last_pos = $self->{last_pos}; # $skip_val ||= ($last_pos->event eq 'brkpt' && $self->{event} eq 'line'); if ($self->{settings}{'debugskip'}) { $self->msg("skip: $skip_val, last: $self->{last_pos}->inspect(), " . "new: $new_pos->inspect()"); } # @last_pos[2] = new_pos[2] if 'nostack' eq $self->{different_pos}; my $condition_met = 1; # if (! $skip_val) { # if (@stop_condition) { # puts 'stop_cond' if @settings[:'debugskip']; # debug_eval_no_errmsg(@stop_condition); # } elsif (@to_method) { # puts "method #{@frame.method} #{@to_method}" if # $self->{setting}{'debugskip'}; # @frame.method == @to_method; # } else { # puts 'uncond' if $self->{settings}{'debugskip'}; # 1; # }; # $self->msg("condition_met: #{condition_met}, last: $self->{last_pos}, " . # "new: $new_pos->inspect(), different #{@different_pos.inspect}") if # $self->{settings}{'debugskip'}; $skip_val = (($last_pos && $last_pos->eq($new_pos) && !!$self->{different_pos}) || !$condition_met); $self->{last_pos} = $new_pos; unless ($skip_val) { # Set up the default values for the next time we consider # skipping. $self->{different_pos} = $self->{settings}{different}; } return $skip_val; } sub restart_args($$) { my $self = shift; my @flags = (); # If warn was on before, turn it on again. no warnings 'once'; push @flags, '-w' if $DB::ini_warn; # Rebuild the -I flags that were on the initial # command line. for (@DB::ini_INC) { push @flags, '-I', $_; } # Turn on taint if it was on before. push @flags, '-T' if ${^TAINT}; # Arrange for setting the old INC: # Save the current @init_INC in the environment. DB::set_list( "PERLDB_INC", @DB::ini_INC ); ( $EXECUTABLE_NAME, @flags, '-d:Trepan', $DB::ini_dollar0, @{$self->{dbgr}{exec_strs}}, @DB::ini_ARGV ); } scalar "Just one part of the larger Devel::Trepan::CmdProcessor";