Created
May 9, 2012 08:33
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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