Skip to content

Instantly share code, notes, and snippets.

@ankopainting
Created May 9, 2012 08:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ankopainting/2643007 to your computer and use it in GitHub Desktop.
Save ankopainting/2643007 to your computer and use it in GitHub Desktop.
rubinius variable tracer - traces set_local to print variables value as they are assigned
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment