Spread and use Firefox
Advertise on this blog!
-->

Subscribe to my posts via Email


Delivered by FeedBurner

I'm an Indian
Darn proud of being an Indian!

Links...

Categories

Archives

Friends



Subscribe





External Links


May 27, 2008

(Posted at 5:18 am)

Static call graphs for Ruby code

Ever since my bug list for ApnaBill.com started to go down, I was worried that what am I going to do in all the free time which will suddenly be created. So much so, I was even creating unnecessary tasks and them scratching them out upon realizing that they are just not needed - sure shot symptoms of working too lately!

While working on ApnaBill, I felt a huge urge to document my code - specially the libraries I wrote and some of the complex controllers. Surely enough the code is very well documented - infact, at times makes pun and even tells stories - but I was always missing a call graph generation tool, which when given my Rails project, can graph out all the calls each of the actions make. I am not talking about profile time graphs - but something pretty static, which just looks at your ruby code and draws a graph.

…And just why is it needed? Documentation - ofcourse! A colored and connected graph, which can take you directly to the source code is alot easier to digest than just the code - specially for those who have not written that piece of code. This can be real useful in many scenarios.

Ok, so I started my search two days ago, on Saturday night - and for all night, I was crawling web pages like a googlebot - trying to find the relevant pieces of information. I did came across some fantastic projects like RailRoad, RCov and ruby-prof - but there was nothing that could satisfy my need. I even tried Doxygen (which is still to support Ruby), rdoc and almost all the other doc tools mentioned at “Other tools” seciton on Doxygen’s website.

Continuing my {re}search, I came across ParseTree. Its a C extension which churns out Sexp representation of Ruby code. You can parse ruby code in classes or methods or strings format. The library (apart from other stuff) - basically gives you a callback like access to various parts of the Ruby language. So “process_defn” would be invoked when any “def my_method()” is encountered.

There isn’t much on the internet about how to use ParseTree but the code samples and RDoc included in the gem are a good start. And then there’s the code installed by the gem - which you can always read at leisure. Another document worth checking out is Ryan’s Dependency Analyzer - where he does a bit of explaining as well.

Okay, back to the problem in hand - How can I draw a call graph of my code without actually executing it. I don’t want to execute it because it cannot guarantee 100% path coverage. Further, my objective is to document the code rather than profile it.

After my initial attempts, I was finally able to create a hierarchical structure of the code (which is utterly basic as of now).

  1.  
  2. # Target code which is to be graphed
  3. # Save as "mini.rb" in your current directory
  4. module Junk
  5.  
  6.   class Maku
  7.  
  8.     def say_hello(str)
  9.       puts "hello #{str}"
  10.       a = 1+1
  11.       if a == 2
  12.         if a == 1
  13.           puts "Yahoo"
  14.           if a == 10
  15.             puts "Microsoft"
  16.           else
  17.             puts "Google"
  18.           end
  19.         end
  20.         pp a
  21.       else
  22.         puts "bye"
  23.       end
  24.     end
  25.   end
  26.  
  27. end

Introducing RAKA

  1.  
  2. #!/usr/bin/env ruby
  3. # RAKA - A Ruby Kode Analyzer
  4. # Save in same directory as mini.rb
  5.  
  6. require "pp"
  7. require ‘rubygems’
  8. require ‘parse_tree’
  9. require ’sexp_processor’
  10.  
  11. class Raka < SexpProcessor
  12.  
  13.   attr_accessor :tabber
  14.  
  15.   def initialize
  16.     super
  17.     @tabber = 0
  18.     self.strict = false
  19.     self.auto_shift_type = true
  20.   end
  21.  
  22.   # Just a wrapper for s method.
  23.   # Usefull in decrementing tabbing structure once processing is done.
  24.   def s(*args)
  25.     result = super(*args)
  26.     @tabber -= 1
  27.     return result
  28.   end
  29.  
  30.   def process_fcall(exp)
  31.     @tabber += 1
  32.     name = exp.shift
  33.     display "CALL #{name}"
  34.     return s(:fcall, exp.shift)
  35.   end
  36.  
  37.   def process_if(exp)
  38.     @tabber += 1
  39.     display "IF"
  40.     test = exp.shift
  41.     process(test)
  42.     left = exp.shift
  43.     process(left)
  44.     display "ELSE"
  45.     right = exp.shift
  46.     process(right)
  47.     display "END"
  48.     return s(:if, test, left, right)
  49.   end
  50.  
  51.   def process_defn(exp)
  52.     @tabber += 1
  53.     name = exp.shift
  54.     display "DEF #{name}"
  55.     args = process exp.shift
  56.     body = process exp.shift
  57.     display "END"
  58.     return s(:defn, name, args, body)
  59.   end
  60.  
  61.   private
  62.  
  63.   def display(str)
  64.     @tabber.times{ print "\t" }
  65.     puts "#{str}"
  66.   end
  67.  
  68. end
  69.  
  70. require "mini"
  71. puts "============Junk::Maku============"
  72. Raka.new.process(*ParseTree.new.parse_tree(Junk::Maku))

The result

call graph for mini.rb

Woha! As you can see, I am able to relate the code structure with the calls and if statements. If I can output this into a format which Graphviz can understand, we’ll have exactly what we want!

Too bad, its almost 6am - and I have to catch up on some sleep :( I’ll have to stop here - more on the project as things with RAKA proceed.

Comments and suggestions are more than welcome :)

nice..

Comment by SandyTheFire — May 27, 2008 @ 4:11 pm

Nice one! Can’t wait for the Gem!

Comment by Anthony — May 27, 2008 @ 8:34 pm

Leave a comment

*
To prove you're a person (not a spam script), type the security word shown in the picture.
Anti-Spam Image