Module: Trepanning
- Includes:
- RemoteCommunication
- Defined in:
- app/iseq.rb,
app/run.rb,
app/file.rb,
app/client.rb
Overview
Copyright © 2011 Rocky Bernstein <[email protected]> Things related to RubyVM::InstructionSequence’s
Class Method Summary collapse
-
.debug_program(dbgr, program_to_debug) ⇒ Object
Given a Ruby interpreter and program we are to debug, debug it.
- .ruby_syntax_errors(prog_script) ⇒ Object
- .start_client(options) ⇒ Object
-
.whence_file(prog_script) ⇒ Object
Do a shell-like path lookup for prog_script and return the results.
Instance Method Summary collapse
- #file_match_pat(filename) ⇒ Object
- #filter_scripts(dirname) ⇒ Object
-
#find_iseq_with_line_from_iseq(iseq, lineno, go_up = true) ⇒ Object
Returns a RubyVM::Instruction for the specified line.
- #find_iseq_with_line_from_iseq2(iseq, lineno, go_up, seen) ⇒ Object
- #find_iseqs(iseqs_hash, name) ⇒ Object
- #find_iseqs_with_lineno(filename, lineno) ⇒ Object
- #find_scripts(filename) ⇒ Object
Class Method Details
.debug_program(dbgr, program_to_debug) ⇒ Object
Given a Ruby interpreter and program we are to debug, debug it. The caller must ensure that ARGV is set up to remove any debugger arguments or things that the debugged program isn’t supposed to see. FIXME: Should we make ARGV an explicit parameter?
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'app/run.rb', line 12 def debug_program(dbgr, program_to_debug) # Make sure Ruby script syntax checks okay. # Otherwise we get a load message that looks like trepanning has # a problem. output = ruby_syntax_errors(program_to_debug) if output puts output exit $?.exitstatus end dbgr.trace_filter << self.method(:debug_program) dbgr.trace_filter << Kernel.method(:load) old_dollar_0 = $0 # Without the dance below to set $0, setting it to a signifcantly # longer value will truncate it in some OS's. See # http://www.ruby-forum.com/topic/187083 $progname = program_to_debug alias $0 $progname dollar_0_tracker = lambda {|val| $program_name = val} trace_var(:$0, dollar_0_tracker) dbgr.debugger(:hide_stack=>true) do dbgr.core.processor.hidelevels[Thread.current] = RubyVM::Frame.current.stack_size + 1 begin Kernel::load program_to_debug rescue Interrupt end end # The dance we have to undo to restore $0 and undo the mess created # above. $0 = old_dollar_0 untrace_var(:$0, dollar_0_tracker) rescue if dbgr.settings[:post_mortem] frame = RubyVM::Frame.current.prev(0) dbgr.core.step_count = 0 # Make event processor stop dbgr.core.processor.settings[:debugstack] = 0 # Make event processor stop dbgr.core.event_processor('post-mortem', frame, $!) else raise end end |
.ruby_syntax_errors(prog_script) ⇒ Object
76 77 78 79 80 81 82 |
# File 'app/run.rb', line 76 def ruby_syntax_errors(prog_script) output = `#{RbConfig.ruby} -c #{prog_script.inspect} 2>&1` if $?.exitstatus != 0 and RUBY_PLATFORM !~ /mswin/ return output end return nil end |
.start_client(options) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'app/client.rb', line 9 def start_client() puts "Client option given" user_opts = {} %w(readline).each do |opt| user_opts[opt.to_sym] = [opt.to_sym] end dbgr = Trepan.new(:client => true, :cmdfiles => [], :initial_dir => [:chdir], :nx => true, :host => [:host], :port => [:port], :user_opts => user_opts ) intf = dbgr.intf[-1] intf.write_remote(SYNC, 'FIXME: add useful info') while true begin control_code, line = intf.read_remote rescue EOFError, Errno::EPIPE puts "Remote debugged process closed connection" break end # p [control_code, line] case control_code when PRINT # FIXME: don't know why server sometimes adds a gratuituous space. # the space is added somewhere inside TCPSocket.print line = line[0..-2] if line.end_with?("\n ") print line when CONFIRM_TRUE response = intf.confirm(line, true) intf.write_remote(CONFIRM_REPLY, response ? 'Y' : 'N') when CONFIRM_FALSE response = intf.confirm(line, true) intf.write_remote(CONFIRM_REPLY, response ? 'Y' : 'N') when PROMPT # Printing of prompt has been handled already by PRINT. begin command = intf.read_command(line) rescue EOFError puts "user-side EOF. Quitting..." break end begin intf.write_remote(COMMAND, command) rescue Errno::EPIPE puts "Remote debugged process died" break end when QUIT break when RESTART break else $stderr.puts "** Unknown control code: #{control_code}" end end end |
.whence_file(prog_script) ⇒ Object
Do a shell-like path lookup for prog_script and return the results. If we can’t find anything return prog_script.
62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'app/run.rb', line 62 def whence_file(prog_script) if prog_script.start_with?(File::SEPARATOR) || prog_script.start_with?('.') # Don't search since this name has path is explicitly absolute or # relative. return prog_script end for dirname in ENV['PATH'].split(File::PATH_SEPARATOR) do prog_script_try = File.join(dirname, prog_script) return prog_script_try if File.readable?(prog_script_try) end # Failure return prog_script end |
Instance Method Details
#file_match_pat(filename) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'app/file.rb', line 13 def file_match_pat(filename) prefix = if filename[0..0] == File::SEPARATOR # An absolute filename has to match at the beginning and # the end. '^' else # An nonabsolute filename has to match either at the # beginning of the file name or have a path separator before # the supplied part, e.g. "file.rb" does not match "myfile.rb" # but matches "my/file.rb" '(?:^|[/])' end "#{prefix}#{Regexp.escape(filename)}$" end |
#filter_scripts(dirname) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'app/file.rb', line 29 def filter_scripts(dirname) match_block = Proc.new{|filename, iseq| filename =~ /^#{dirname}/} scripts = SCRIPT_ISEQS__.select(&match_block) SCRIPT_ISEQS__.delete_if(&match_block) match_block = Proc.new{|iseq| iseq.source_container[1] =~ /^#{dirname}/ } rejected = {} # SCRIPT_ISEQS__ is updated automatically. Dup copy is to make # sure we we aren't iterating over something that some other # process, thread or hook is filling. script_iseqs = SCRIPT_ISEQS__.dup script_iseqs.each do |name, iseqs| ary = iseqs.select(&match_block) rejected[name] = ary unless ary.empty? iseqs.delete_if(&match_block) end return [scripts, rejected] end |
#find_iseq_with_line_from_iseq(iseq, lineno, go_up = true) ⇒ Object
Returns a RubyVM::Instruction for the specified line. We search the current instruction sequence iseq
and then up the parent scope. If we hit the top and we can’t find line
that way, then we reverse the search from the top and search down. This will add all siblings of ancestors of meth
. Similar to rbx-trepanning method “find_method_with_line”.
12 13 14 |
# File 'app/iseq.rb', line 12 def find_iseq_with_line_from_iseq(iseq, lineno, go_up=true) find_iseq_with_line_from_iseq2(iseq, lineno, go_up, {}) end |
#find_iseq_with_line_from_iseq2(iseq, lineno, go_up, seen) ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'app/iseq.rb', line 16 def find_iseq_with_line_from_iseq2(iseq, lineno, go_up, seen) return iseq if iseq.offsetlines.values.flatten.uniq.member?(lineno) seen[iseq] = true prev_iseq = iseq while prev_iseq = prev_iseq.parent iseq = prev_iseq return iseq if iseq.offsetlines.values.flatten.uniq.member?(lineno) end if go_up # At top and not found so now go down.. iseq.child_iseqs.each do |child_iseq| next if seen[child_iseq] # we tried before # puts "#{child_iseq.name}, #{child_iseq}" result = find_iseq_with_line_from_iseq2(child_iseq, lineno, false, seen) return result if result end return nil end |
#find_iseqs(iseqs_hash, name) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'app/file.rb', line 49 def find_iseqs(iseqs_hash, name) iseq_name, filename = name.split(/@/) return [] unless iseqs_hash.member?(iseq_name) iseqs = iseqs_hash[iseq_name] # FIXME: filter out debugger iseqs if filename filename_pat = file_match_pat(filename) iseqs.select{|iseq| iseq.source_container[1] =~ /#{filename_pat}/} else return iseqs end end |
#find_iseqs_with_lineno(filename, lineno) ⇒ Object
62 63 64 65 66 67 68 69 70 71 |
# File 'app/file.rb', line 62 def find_iseqs_with_lineno(filename, lineno) files = find_scripts(filename) files.each do |file| SCRIPT_ISEQS__[file].each do |iseq| found_iseq = find_iseq_with_line_from_iseq(iseq, lineno, true) return found_iseq if found_iseq end end return nil end |
#find_scripts(filename) ⇒ Object
73 74 75 76 |
# File 'app/file.rb', line 73 def find_scripts(filename) filename_pat = file_match_pat(filename) return SCRIPT_LINES__.keys.grep(/#{filename_pat}/) end |