# -*- coding: utf-8 -*- # Copyright (C) 2010, 2015 Rocky Bernstein require_relative '../command' require_relative '../../app/condition' class Trepan::Command::StepCommand < Trepan::Command unless defined?(HELP) NAME = File.basename(__FILE__, '.rb') HELP = <<-HELP **#{NAME}**[**+**|**=**|**-**|**<**|**>**|**!**|**<>**] [**into**] [*event-name*...] [*count*] **#{NAME} until** *expression* **#{NAME} thread** **#{NAME} to** *method-name* **#{NAME} over** **#{NAME} out** Execute the current line, stopping at the next event. Sometimes this is called 'step into'. With an integer argument, step that many times. With an *until* expression that expression is evaluated and we stop the first time it is true. *event-name*... is list of an event name which is one on *call*, *return*, *line*, *exception* *c-call*, *c-return* or *c-exception*. If specified, only those stepping events will be considered. If no list of event names is given, then any event triggers a stop when the count is 0. There is however another way to specify an single *event-name*, by suffixing one of the symbols `<`, `>`, or `!` after the command or on an alias of that. A suffix of `+` on a command or an alias forces a move to another position, while a suffix of `-` disables this requirement. A suffix of `>'`will continue until the next call. (`finish` will run run until the return for that call.) If no suffix is given, the debugger setting 'different' determines this behavior. Examples: -------- #{NAME} # step 1 event, *any* event obeying 'set different' setting #{NAME} 1 # same as above #{NAME}+ # same but force stopping on a new line #{NAME}= # same but force stopping on a new line a new frame added #{NAME}- # same but force stopping on a new line a new frame added #{NAME} 5/5+0 # same as above #{NAME} line # step only line events #{NAME} call # step only call call events #{NAME}> # step call and c-call events #{NAME}< # step only return and c-return events #{NAME} call line # step line *and* call events #{NAME}<> # same as step call c-call return c-return #{NAME} until a > b #{NAME} over # same as 'next' #{NAME} out # same as 'finish' #{NAME} thread # step stopping only in the current thread. Is the same # as step until Thread.current.object_id == #object_id Related and similar is the `next` (step over) and `finish` (step out) commands. All of these are slower than running to a breakpoint. See also: --------- `skip`, `jump` (there's no 'hop' yet), `continue`, `return` and `finish` for other ways to progress execution. HELP ALIASES = %w(s step+ step- step< step> step<> step! s> s< s+ s- s<> s! s=) CATEGORY = 'running' NEED_STACK = true NEED_RUNNING = true SHORT_HELP = 'Step program (possibly entering called functions)' Keyword_to_related_cmd = { 'out' => 'finish', 'over' => 'next', 'into' => 'step', } end include Trepan::Condition # This method runs the command def run(args) # :nodoc opts = @proc.parse_next_step_suffix(args[0]) condition = nil if args.size == 1 # Form is: "step" which means "step 1" step_count = 0 else replace_cmd = Keyword_to_related_cmd[args[1]] if replace_cmd cmd = @proc.commands[replace_cmd] return cmd.run([replace_cmd] + args[2..-1]) elsif 'until' == args[1] try_condition = args[2..-1].join(' ') if valid_condition?(try_condition) condition = try_condition opts[:different_pos] = false step_count = 0 end elsif 'to' == args[1] if args.size != 3 errmsg('Expecting a method name after "to"') return elsif !@proc.method?(args[2]) errmsg("#{args[2]} doesn't seem to be a method name") return else opts[:to_method] = args[2] opts[:different_pos] = false step_count = 0 end elsif 'thread' == args[1] condition = "Thread.current.object_id == #{Thread.current.object_id}" opts[:different_pos] = false step_count = 0 else count_str = args[1] int_opts = { :msg_on_error => "The 'step' command argument must eval to an integer. Got: %s" % count_str, :min_value => 1 }.merge(opts) count = @proc.get_an_int(count_str, int_opts) return unless count # step 1 is core.step_count = 0 or "stop next event" step_count = count - 1 end end @proc.step(step_count, opts, condition) end end if __FILE__ == $0 require_relative '../mock' dbgr, cmd = MockDebugger::setup p cmd.run([cmd.name]) end