Ruby Unroller
From WhyNotWiki
Aliases: Ruby execution unroller, Ruby code tracing tool, Ruby execution tracer
~/code/gemables/code_unroller/lib/code_unroller.rb
Sort of like a profiler or a "running backtrace", only this would actually output the source code that gets executed in addition to the filename, line number, and method name.
Would be great for writing "dissections" -- method-by-method, line-by-line, step-by-step guides to how some bit of code works. What calls what, in what order, etc.
Help people quickly familiarize themselves with your code and "get into the guts of it" (get their hands dirty) without having to manually print backtraces, "got here" messages, and other time-consuming manual debugging techniques and debug output...
Ideally, this would be displayed as a tree (the definition of a method would be displayed beneath each method call) and you could collapse/hide nodes/subtrees that you don't care about.
RAILS_BENCHMARKER.iterations = ARGV[0].to_i
RAILS_BENCHMARKER.url_spec = benchmarks
RAILS_BENCHMARKER.setup_test_urls(benchmark_name)
(collapsed (don't care about))
RAILS_BENCHMARKER.establish_test_session
(collapsed (don't care about))
RAILS_BENCHMARKER.warmup
(source code for RailsBenchmark#warmup would be displayed here)
You would probably often only care about understanding how stuff works starting a certain level / a certain method call / a certain line of code in the execution of the script -- so you should be able to put some command at the point where you start caring, and only code within that method, after the starting line, would be profiled/reported on/unrolled.
For instance:
def foo() something_I_really_dont_care_about Unroller.start neat_method mysterious_method interesting_method end
or
def foo()
something_I_really_dont_care_about
Unroller.unroll do
mysterious_method
end
something_I_really_dont_care_about
end
or even:
Unroller.include "MyClass#foo" Unroller.include "#foo" Unroller.include "my_class.rb" => (13..14) # line numbers
See http://brainspl.at/request_response.pdf for an excellent readable example of a dissection / trace through Rails source code.
Even more useful (except for documentation / posting on web sites) would be an interactive visual debugger that let you step through code.... does such a one exist??
After the code is unrolled, you often will want to exclude clutter/fluff code from the unrolled methods. Because you often want to focus on a particular feature/aspect of the code.
Also, if a method gets called more than once, you probably only want/need to unroll that method for the first call...
Contents |
[edit] set_trace_func(proc)
http://corelib.rubyonrails.org/classes/Kernel.html#M002058
Establishes proc as the handler for tracing, or disables tracing if the parameter is nil. proc takes up to six parameters: an event name, a filename, a line number, an object id, a binding, and the name of a class. proc is invoked whenever an event occurs. Events are: c-call (call a C-language routine), c-return (return from a C-language routine), call (call a Ruby method), class (start a class or module definition), end (finish a class or module definition), line (execute code on a new line), raise (raise an exception), and return (return from a Ruby method). Tracing is disabled within the context of proc.
class Test def test a = 1 b = 2 end end set_trace_func proc { |event, file, line, id, binding, classname| printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname } t = Test.new t.test line prog.rb:11 false c-call prog.rb:11 new Class c-call prog.rb:11 initialize Object c-return prog.rb:11 initialize Object c-return prog.rb:11 new Class line prog.rb:12 false call prog.rb:2 test Test line prog.rb:3 test Test line prog.rb:4 test Test return prog.rb:4 test Test
[edit] Used by irb/frame
...
[edit] Used by facets/binding/self/of_caller
...
[edit] Used by action_controller/rescue.rb
vendor/rails/actionpack/lib/action_controller/rescue.rb
def perform_action_with_rescue
begin
perform_action_without_rescue
rescue Exception => exception # errors from action performed
if defined?(Breakpoint) && params["BP-RETRY"]
msg = exception.backtrace.first
if md = /^(.+?):(\d+)(?::in `(.+)')?$/.match(msg) then
origin_file, origin_line = md[1], md[2].to_i
set_trace_func(lambda do |type, file, line, method, context, klass|
if file == origin_file and line == origin_line then
set_trace_func(nil)
params["BP-RETRY"] = false
callstack = caller
callstack.slice!(0) if callstack.first["rescue.rb"]
file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
message = "Exception at #{file}:#{line}#{" in `#{method}'" if method}." # `´ ( for ruby-mode)
Breakpoint.handle_breakpoint(context, message, file, line)
end
end)
retry
end
end
rescue_action(exception)
end
end
