Ruby Unroller

From WhyNotWiki
Revision as of 08:59, 16 November 2007 by 67.130.155.233 (Talk)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search

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

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

Used by irb/frame

...

Used by facets/binding/self/of_caller

...

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
Ads
Personal tools