public
Last active

rubinius variable tracer - traces set_local to print variables value as they are assigned

  • Download Gist
rbx_var_trace.rb
Ruby
1 2 3 4 5 6 7 8 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
require 'rubygems'
require 'pry'
require 'rubinius/debugger'
 
# monkeypatch to stop the debugger setting a breakpoint at the start
class Rubinius::Debugger
def start_noninteractive(offset=0)
spinup_thread
Thread.current.set_debugger_thread @thread
self
end
end
 
rd = Rubinius::Debugger.new
rd.start_noninteractive
 
# this is what we do when we hit a breakpoint on a set_local
class SetlocalHit
def initialize(variable)
@var = variable
end
# don't memoize binding as we may hit this breakpoint several times!
def binding
Binding.setup(
@location.variables,
@location.method,
@location.static_scope)
end
# breakpoint is hit!
def hit!(location)
@location = location
# puts "BreakpointHit:#{location.inspect}"
#puts @var.nil?
STDERR.puts "#{location.line}: #{@var} = #{eval(@var, binding)}" rescue nil
end
end
 
module Rubinius
class CompiledMethod
class Instruction
#make this accessible - @comment is how we get the variable name that the :set_local
# instruction refers to.
attr_accessor :comment
def initialize(inst, cm, ip)
@instruction = inst[0]
@args = inst[1..-1]
@comment = nil
 
#binding.pry if @instruction.opcode == :set_local
@args.each_index do |i|
case @instruction.args[i]
when :literal
@args[i] = cm.literals[@args[i]]
when :local
# TODO: Blocks should be able to retrieve local names as well,
# but need access to method corresponding to home context
if cm.local_names # and !cm.is_block?
@comment = cm.local_names[args[i]].to_s
end
end
end
 
@cm = cm
@ip = ip
end
end
# parse a compiled method, adding breakpoints on all set_locals
def decoder!
require 'compiler/iseq'
 
decoder = Rubinius::InstructionDecoder.new(iseq)
stream = decoder.decode(false)
ip = 0
 
stream.each do |inst|
instruct = Instruction.new(inst, self, ip)
#STDERR.puts instruct #for debug
# move to next instruction before we insert breakpoint
ip += instruct.size
if(instruct.opcode == :set_local)
self.set_breakpoint(ip, SetlocalHit.new(instruct.comment))
end
if(instruct.opcode == :create_block)
#puts "in block - we need to decode!"
instruct.args.first.decoder!
end
end
nil
end
end
end
 
string =<<EOS
(0..3).each do |x|
y = x
end
test = 3; yammer = 8
newtest = test
test += 2
rar = "man"
 
puts test
 
EOS
 
cm = Rubinius::Compiler.compile_string(string) # compile
cm.decoder! # add breakpoints at set_local
script = cm.create_script # turn compiled method into script
Rubinius.run_script script.compiled_method # run script

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.