[DRE-commits] [ruby-prof] 01/14: Imported Upstream version 0.7.3

Jonas Genannt jonas at brachium-system.net
Thu Dec 19 19:42:23 UTC 2013


This is an automated email from the git hooks/post-receive script.

hggh-guest pushed a commit to branch master
in repository ruby-prof.

commit 04ca289f8ae4fe5ad3a60949ff3e8cdf6c4b72f8
Author: Jonas Genannt <jonas at brachium-system.net>
Date:   Thu Dec 19 18:29:17 2013 +0100

    Imported Upstream version 0.7.3
---
 CHANGES                              |  202 ++++
 LICENSE                              |   23 +
 README                               |  436 +++++++++
 Rakefile                             |  123 +++
 bin/ruby-prof                        |  207 +++++
 examples/flat.txt                    |   55 ++
 examples/graph.html                  |  823 +++++++++++++++++
 examples/graph.txt                   |  170 ++++
 ext/extconf.rb                       |   34 +
 ext/measure_allocations.h            |   58 ++
 ext/measure_cpu_time.h               |  152 +++
 ext/measure_gc_runs.h                |   76 ++
 ext/measure_gc_time.h                |   57 ++
 ext/measure_memory.h                 |  101 ++
 ext/measure_process_time.h           |   52 ++
 ext/measure_wall_time.h              |   53 ++
 ext/mingw/Rakefile                   |   23 +
 ext/mingw/build.rake                 |   38 +
 ext/mingw/ruby_prof.so               |  Bin 0 -> 38794 bytes
 ext/ruby_prof.c                      | 1680 ++++++++++++++++++++++++++++++++++
 ext/ruby_prof.h                      |  188 ++++
 ext/vc/ruby_prof.sln                 |   20 +
 ext/vc/ruby_prof.vcproj              |  241 +++++
 ext/version.h                        |    4 +
 lib/ruby-prof.rb                     |   48 +
 lib/ruby-prof/abstract_printer.rb    |   41 +
 lib/ruby-prof/aggregate_call_info.rb |   62 ++
 lib/ruby-prof/call_info.rb           |   47 +
 lib/ruby-prof/call_tree_printer.rb   |   84 ++
 lib/ruby-prof/flat_printer.rb        |   79 ++
 lib/ruby-prof/graph_html_printer.rb  |  256 ++++++
 lib/ruby-prof/graph_printer.rb       |  164 ++++
 lib/ruby-prof/method_info.rb         |  111 +++
 lib/ruby-prof/task.rb                |  146 +++
 lib/ruby-prof/test.rb                |  148 +++
 lib/unprof.rb                        |    8 +
 rails/environment/profile.rb         |   24 +
 rails/example/example_test.rb        |    9 +
 rails/profile_test_helper.rb         |   21 +
 test/aggregate_test.rb               |  121 +++
 test/basic_test.rb                   |  283 ++++++
 test/duplicate_names_test.rb         |   32 +
 test/exceptions_test.rb              |   15 +
 test/exclude_threads_test.rb         |   54 ++
 test/line_number_test.rb             |   73 ++
 test/measurement_test.rb             |  121 +++
 test/module_test.rb                  |   54 ++
 test/no_method_class_test.rb         |   13 +
 test/prime.rb                        |   58 ++
 test/prime_test.rb                   |   13 +
 test/printers_test.rb                |   71 ++
 test/recursive_test.rb               |  254 +++++
 test/singleton_test.rb               |   37 +
 test/stack_test.rb                   |  138 +++
 test/start_stop_test.rb              |   95 ++
 test/test_suite.rb                   |   23 +
 test/thread_test.rb                  |  159 ++++
 test/unique_call_path_test.rb        |  206 +++++
 58 files changed, 7884 insertions(+)

diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..83bcb9c
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,202 @@
+0.7.3 (2008-12-09)
+========================
+* Fixed compile error with new x86_64 code using GCC.
+
+
+0.7.2 (2008-12-08)
+========================
+* Fixed major bug in printing child methods in graph reports.
+
+* Fixes for supporting x86_64 machines (Diego Pettenò)
+
+
+0.7.1 (2008-11-28)
+========================
+* Added new AggregateCallInfo class for printers to
+  make results easier to read.  Take this call sequence
+  for example:
+
+   A   B   C
+   |   |   |
+   Z   A   A
+       |   |
+       Z   Z
+
+  By default, ruby-prof will show that Z was called by 3 separate
+  instances of A.  In an IDE that is helpful but in a text report
+  it is not since it makes the report much harder to read.
+  As a result, printers now aggregate together callers (and children),
+  matching ruby-prof's output from versions prior to 0.7.0.
+
+* Fixes for supporting x86_64 machines (Matt Sanford)
+
+
+0.7.0 (2008-11-04)
+========================
+
+Features
+--------
+* Added two new methods - RubyProf.resume and RubyProf.pause. 
+  RubyProf.resume takes an optional block, which ensures that
+  RubyProf.pause is called.  For example:
+  
+  10.times do |i|
+    RubyProf.resume do
+      # Some long process
+    end
+  end
+  
+  result = RubyProf.stop
+
+* Added support for profiling tests that use Ruby's built-in
+  unit test framework (ie, test derived from 
+  Test::Unit::TestCase).  To enable profiling simply add
+  the following line of code to your test class:
+  
+    include RubyProf::Test
+    
+  By default, profiling results are written to the current 
+  processes working directory.  To change this, or other
+  profiling options, simply modify the PROFILE_OPTIONS hash
+  table as needed.        
+
+* Used the new support for profiling test cases to revamp
+  the way that Rails profiling works.  For more information
+  please refer to RubyProf's documentation.
+
+* Keep track of call stack for each method to enable more
+  powerful profile visualizations in Integrated Development
+  Environments (Hin Boean, work supported by CodeGear).
+
+* Expose measurements to Ruby (Jeremy Kemper).
+
+* Add support for additional memory measurements modes in Ruby 1.9 (Jeremy Kemper).
+
+* Add support for Lloyd Hilaiel's Ruby patch for measuring total heap size.
+   See http://lloydforge.org/projects/ruby. (Jeremy Kemper).
+  
+  
+Fixes
+-------
+* RubyProf.profile no longer crashes if an exception is
+  thrown during a profiling run.
+
+* Measure memory in fractional kilobytes rather than rounding down (Jeremy Kemper)
+
+  
+0.6.0 (2008-02-03)
+========================
+
+ruby-prof 0.6.0 adds support for Ruby 1.9 and memory profiling.
+
+Features
+--------
+* Added support for ruby 1.9 (Shugo Maeda)
+* Added support for outputting printer results to a String, Array or IO
+  object (Michael Granger)
+* Add new memory profiling mode.  Note this mode depends on a
+  patched Ruby interpreter (Alexander Dymo)
+
+Fixes
+-------
+* Improvements to GraphHtmlPrinter including updated documentation,
+  fixes for min_time support, ability to specify templates using
+  strings or filenames, and table layout fixes (Makoto Kuwata)
+* Fixes to scaling factor for calltrees so that precision is not lost
+  due to the conversion to doubles (Sylvain Joyeux) 
+* Changed constant ALLOCATED_OBJECTS to ALLOCATIONS in the C code to
+  match the Ruby code (Sylvain Joyeux)
+* Added support for calltree printer to ruby-prof binary script (Sylvain Joyeux)
+* Fix support for the allocator measure mode to extconf.rb (Sylvain Joyeux)
+* Honor measure mode when specified on the command line (Sylvain Joyeux)
+* Sorting of methods by total time was incorrect (Dan Fitch, Charlie Savage)
+* Fix ruby-prof to work with the latest version of GEMS (Alexander Dymo)
+* Always define MEASURE_CPU_TIME and MEASURE_ALLOCATIONS in Ruby code, but
+  set their values to nil if the functionality is not available.
+   
+
+0.5.2 (2007-07-19)
+========================
+
+ruby-prof 0.5.2 is a bug fix release.
+
+Fixes
+-------
+* Include missing rails plugin
+
+
+0.5.1 (2007-07-18)
+========================
+
+ruby-prof 0.5.1 is a bug fix and performance release.
+
+Performance
+--------
+* Significantly reduced the number of thread lookups by
+  caching the last executed thread.
+
+Fixes
+-------
+* Properly escape method names in HTML reports
+* Fix use of -m and --min-percent command line switches
+* Default source file information to ruby_runtime#0 for c calls
+* Moved rails_plugin to top level so it is more obvious
+* Updated rails_plugin to write reports to the current
+  Rails log directory
+* Added additional tests
+
+
+0.5.0 (2007-07-09)
+========================
+
+Features
+--------
+* Added support for timing multi-threaded applications
+* Added support for 64 bit systems (patch from Diego 'Flameeyes' Petten)
+* Added suport for outputting data in the format used by
+  KCacheGrind (patch from Carl Shimer)
+* Add filename and line numbers to call tree information (patch from Carl Shimer)
+* Added Visual Studio 2005 project file.
+* Added replace-progname switch, als rcov.
+* Added better support for recursive methods
+* Added better support for profiling Rails applications
+
+Fixes
+-------
+* Fixes bug when the type of an attached object (singleton) is inherited
+  from T_OBJECT as opposed to being a T_OBJECT (identified by Francis Cianfrocca)
+* ruby-prof now works in IRB.
+* Fix sort order in reports.
+* Fixed rdoc compile error.
+* Fix tabs in erb template for graph html report on windows.
+
+0.4.1 (2006-06-26)
+========================
+
+Features
+--------
+* Added a RubyProf.running? method to indicate whether a profile is in progress.
+* Added tgz and zip archives to release
+
+Fixes
+-------
+* Duplicate method names are now allowed
+* The documentation has been updated to show the correct API usage is RubyProf.stop not RubyProf.end
+
+
+0.4.0 (2006-06-16)
+========================
+Features
+--------
+* added support for call graphs
+* added support for printers.  Currently there is a FlatPrinter,
+  GraphPrinter and GraphHtmlPrinter.
+* added support for recursive methods
+* added Windows support
+* now packaged as a RubyGem
+
+Fixes
+-------
+* Fixes bug where RubyProf would crash depending on the
+  way it was invoked - for example, it did not run when
+  used with Arachno Ruby's customized version of Ruby.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ebc4eef
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,23 @@
+Copyright (C) 2005  Shugo Maeda <shugo at ruby-lang.org>
+All rights reserved.
+ *
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+ *
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
\ No newline at end of file
diff --git a/README b/README
new file mode 100644
index 0000000..a5df9ce
--- /dev/null
+++ b/README
@@ -0,0 +1,436 @@
+= ruby-prof
+
+== Overview
+
+ruby-prof is a fast code profiler for Ruby.  Its features include:
+
+* Speed - it is a C extension and therefore many times faster than the standard Ruby profiler.
+* Modes - Ruby prof can measure a number of different parameters, including
+          call times, memory usage and object allocations. 
+* Reports - can generate text and cross-referenced html reports
+  - Flat Profiles - similar to the reports generated by the standard Ruby profiler
+  - Graph profiles - similar to GProf, these show how long a method runs, which methods call it and which methods it calls.
+  - Call tree profiles - outputs results in the calltree format suitable for the KCacheGrind profiling tool.
+* Threads - supports profiling multiple threads simultaneously
+* Recursive calls - supports profiling recursive method calls
+
+
+== Requirements
+
+ruby-prof requires Ruby 1.8.4 or higher.
+
+If you are running Linux or Unix you'll need a C compiler so the extension
+can be compiled when it is installed.
+
+If you are running Windows, then install the Windows specific RubyGem which
+includes an already built extension.
+
+
+== Install
+
+The easiest way to install ruby-prof is by using Ruby Gems.  To install:
+
+<tt>gem install ruby-prof</tt>
+
+If you are running Windows, make sure to install the Win32 RubyGem which 
+includes a pre-built binary. Due to a bug in ruby-gems, you cannot
+install the gem to a path that contains spaces
+(see http://rubyforge.org/tracker/?func=detail&aid=23003&group_id=126&atid=577).
+
+ruby-prof is also available as a tarred gzip archive and zip archive. 
+
+== Usage
+
+There are three ways of running ruby-prof.
+
+
+=== ruby-prof executable
+
+The first is to use ruby-prof to run the Ruby program
+you want to profile.  For more information refer to
+the ruby-prof documentation[link:files/bin/ruby-prof.html].
+
+
+=== ruby-prof API
+
+The second way is to use the ruby-prof API to profile
+particular segments of code.  
+
+  require 'ruby-prof'
+  
+  # Profile the code
+  RubyProf.start
+  ...
+  [code to profile]
+  ...
+  result = RubyProf.stop
+  
+  # Print a flat profile to text
+  printer = RubyProf::FlatPrinter.new(result)
+  printer.print(STDOUT, 0)
+  
+Alternatively, you can use a block to tell ruby-prof what
+to profile:
+
+  require 'ruby-prof'
+  
+  # Profile the code
+  result = RubyProf.profile do
+    ...
+    [code to profile]
+    ...
+  end
+  
+  # Print a graph profile to text
+  printer = RubyProf::GraphPrinter.new(result)
+  printer.print(STDOUT, 0)
+
+Starting with the 0.6.1 release, ruby-prof also supports pausing and resuming
+profiling runs.
+
+  require 'ruby-prof'
+  
+  # Profile the code
+  RubyProf.start
+  [code to profile]
+  RubyProf.pause
+  [other code]
+  RubyProf.resume
+  [code to profile]
+  result = RubyProf.stop
+  
+Note that resume will automatically call start if a profiling run
+has not yet started.  In addition, resume can also take a block:
+
+  require 'ruby-prof'
+  
+  # Profile the code
+  RubyProf.resume do
+    [code to profile]
+  end
+
+  data = RubyProf.stop
+  
+With this usage, resume will automatically call pause at the 
+end of the block.
+
+
+=== require unprof
+
+The third way of using ruby-prof is by requiring unprof.rb:
+
+  require 'unprof'
+
+This will start profiling immediately and will output the results
+using a flat profile report.
+
+This method is provided for backwards compatibility.  Using
+{ruby-prof}[link:files/bin/ruby-prof.html] provides more flexibility.
+
+
+== Profiling Tests
+
+Starting with the 0.6.1 release, ruby-prof supports profiling tests cases
+written using Ruby's built-in	unit test framework (ie, test derived from 
+Test::Unit::TestCase).  To enable profiling simply add the following line 
+of code to your test class:
+  
+  	include RubyProf::Test
+  	
+Each test method is profiled separately.  ruby-prof will run each test method
+once as a warmup and then ten additional times to gather profile data.
+Note that the profile data will *not* include the class's setup or 
+teardown methods.
+
+Separate reports are generated for each method and saved, by default, 
+in the test process's working directory.  To change this, or other profiling
+options, modify your test class's PROFILE_OPTIONS hash table. To globally 
+change test profiling options, modify RubyProf::Test::PROFILE_OPTIONS.  
+
+
+== Profiling Rails
+
+To profile a Rails application it is vital to run it using production like 
+settings (cache classes, cache view lookups, etc.).  Otherwise, Rail's
+dependency loading code will overwhelm any time spent in the application
+itself (our tests show that Rails dependency loading causes a roughly 6x
+slowdown).  The best way to do this is create a new Rails environment,
+profile.rb.
+
+So to profile Rails:
+
+1.  Create a new profile.rb environment - or simply copy the example file
+    in ruby-prof/rails/environment/profile.rb
+    
+2.  Copy the file:
+     
+      ruby-prof/rails/profile_test_helper.rb 
+    
+    To:
+    
+      your_rails_app/test/profile_test_helper.rb
+
+3.  Create a new test directory for profiling:
+
+      your_rails_app/test/profile
+    
+
+4.  Write unit, functional or integration tests specifically designed
+    to profile some part of your Rails application.  At the top
+    of each test, replace this line:
+    
+      require File.dirname(__FILE__) + '/../test_helper'
+
+    With:
+    
+      require File.dirname(__FILE__) + '/../profile_test_helper'
+
+    For example:
+
+    require File.dirname(__FILE__) + '/../profile_test_helper'
+    
+    class ExampleTest < Test::Unit::TestCase
+      include RubyProf::Test
+      fixtures ....
+      
+      def test_stuff
+        puts "Test method"
+      end
+    end   
+
+5.  Now run your tests.  Results will be written to:
+
+      your_rails_app/tmp/profile
+    
+
+== Reports
+
+ruby-prof can generate a number of different reports:
+
+* Flat Reports
+* Graph Reports
+* HTML Graph Reports
+* Call graphs
+
+Flat profiles show the overall time spent in each method.  They
+are a good of quickly identifying which methods take the most time.
+An example of a flat profile and an explanation can be found in
+{examples/flat.txt}[link:files/examples/flat_txt.html].
+
+Graph profiles also show the overall time spent in each method.
+In addition, they also show which methods call the current
+method and which methods its calls.  Thus they are good for
+understanding how methods gets called and provide insight into
+the flow of your program.  An example text graph profile
+is located at {examples/graph.txt}[link:files/examples/graph_txt.html].
+
+HTML Graph profiles are the same as graph profiles, except
+output is generated in hyper-linked HTML. Since graph profiles
+can be quite large, the embedded links make it much easier to
+navigate the results.  An example html graph profile
+is located at {examples/graph.html}[link:files/examples/graph_html.html].
+
+HTML Graph profiles are the same as graph profiles, except
+output is generated in hyper-linked HTML. Since graph profiles
+can be quite large, the embedded links make it much easier to
+navigate the results.  An example html graph profile
+is located at {examples/graph.html}[link:files/examples/graph_html.html].
+
+Call graphs output results in the calltree profile format which is used
+by KCachegrind.  Call graph support was generously donated by Carl Shimer.
+More information about the format can be found at
+the {KCachegrind}[link:http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindCalltreeFormat] site.
+
+
+== Printers
+
+Reports are created by printers.  Supported printers include:
+
+* RubyProf::FlatPrinter - Creates a flat report in text format
+* RubyProf::GraphPrinter - Creates a call graph report in text format
+* RubyProf::GraphHtmlPrinter - Creates a call graph report in HTML (separate files per thread)
+* RubyProf::CallTreePrinter - Creates a call tree report compatible with KCachegrind.
+
+To use a printer:
+
+  result = RubyProf.end
+  printer = RubyProf::GraphPrinter.new(result)
+  printer.print(STDOUT, 0)
+
+The first parameter is any writable IO object such as STDOUT or a file.
+The second parameter, which has a default value of 0, specifies 
+the minimum percentage a method must take to be printed.  Percentages
+should be specified as integers in the range 0 to 100.  For more
+information please see the documentation for the different printers.
+
+
+== Measurements
+
+Depending on the mode and platform, ruby-prof can measure various
+aspects of a Ruby program.  Supported measurements include:
+
+* process time (RubyProf::PROCESS_TIME)
+* wall time (RubyProf::WALL_TIME)
+* cpu time (RubyProf::CPU_TIME)
+* object allocations (RubyProf::ALLOCATIONS)
+* memory usage (RubyProf::MEMORY)
+* garbage collections runs (RubyProf::GC_RUNS)
+* garbage collection time (RubyProf::GC_TIME)
+
+
+Process time measures the time used by a process between any two moments.
+It is unaffected by other processes concurrently running 
+on the system. Note that Windows does not support measuring process
+times - therefore, all measurements on Windows use wall time.
+
+Wall time measures the real-world time elapsed between any two moments.
+If there are other processes concurrently running on the system
+that use significant CPU or disk time during a profiling run
+then the reported results will be too large.
+
+CPU time uses the CPU clock counter to measure time.  The returned
+values are dependent on the correctly setting the CPU's frequency.
+This mode is only supported on Pentium or PowerPC platforms.
+
+Object allocation reports show how many objects each method in
+a program allocates.  This support was added by Sylvain Joyeux
+and requires a patched Ruby interpreter.  For more information and
+the patch, please see:
+http://rubyforge.org/tracker/index.php?func=detail&aid=11497&group_id=426&atid=1700
+
+Memory usage reports show how much memory each method in a program
+uses.  This support was added by Alexander Dymo and requires a
+patched Ruby interpreter.  For more information, see:
+http://rubyforge.org/tracker/index.php?func=detail&aid=17676&group_id=1814&atid=7062
+
+Garbage collection runs report how many times Ruby's garbage collector
+is invoked during a profiling session.  This support was added by Jeremy
+Kemper and requires a patched Ruby interpreter.  For more information, see:
+http://rubyforge.org/tracker/index.php?func=detail&aid=17676&group_id=1814&atid=7062
+
+Garbage collection time reports how much time is spent in Ruby's garbage collector
+during a profiling session.  This support was added by Jeremy Kemper
+and requires a patched Ruby interpreter.  For more information, see:
+http://rubyforge.org/tracker/index.php?func=detail&aid=17676&group_id=1814&atid=7062
+
+To set the measurement:
+
+* RubyProf.measure_mode = RubyProf::PROCESS_TIME
+* RubyProf.measure_mode = RubyProf::WALL_TIME
+* RubyProf.measure_mode = RubyProf::CPU_TIME
+* RubyProf.measure_mode = RubyProf::ALLOCATIONS
+* RubyProf.measure_mode = RubyProf::MEMORY
+* RubyProf.measure_mode = RubyProf::GC_RUNS
+* RubyProf.measure_mode = RubyProf::GC_TIME
+
+The default value is RubyProf::PROCESS_TIME.
+
+You may also specify the measure_mode by using the RUBY_PROF_MEASURE_MODE
+environment variable:
+
+* export RUBY_PROF_MEASURE_MODE=process
+* export RUBY_PROF_MEASURE_MODE=wall
+* export RUBY_PROF_MEASURE_MODE=cpu
+* export RUBY_PROF_MEASURE_MODE=allocations
+* export RUBY_PROF_MEASURE_MODE=memory
+* export RUBY_PROF_MEASURE_MODE=gc_runs
+* export RUBY_PROF_MEASURE_MODE=gc_time
+  
+Note that these values have changed since ruby-prof-0.3.0.  
+
+On Linux, process time is measured using the clock method provided 
+by the C runtime library. Note that the clock method does not
+report time spent in the kernel or child processes and therefore
+does not measure time spent in methods such as Kernel.sleep method.
+If you need to measure these values, then use wall time.  Wall time
+is measured using the gettimeofday kernel method.
+
+On Windows, timings are always wall times.  If you set the clock 
+mode to PROCESS_TIME, then timing are read using the clock method
+provided by the C runtime library.  Note though, these values are
+wall times on Windows and not process times like on Linux.
+Wall time is measured using the GetLocalTime API.
+
+If you use wall time, the results will be affected by other
+processes running on your computer, network delays, disk access,
+etc.  As result, for the best results, try to make sure your
+computer is only performing your profiling run and is 
+otherwise quiescent.
+
+On both platforms, cpu time is measured using the RDTSC assembly
+function provided by the Pentium and PowerPC platforms. CPU time
+is dependent on the cpu's frequency.  On Linux, ruby-prof attempts 
+to read this value from "/proc/cpuinfo."  On Windows, you must
+specify the clock frequency.  This can be done using the
+RUBY_PROF_CPU_FREQUENCY environment variable:
+
+  export RUBY_PROF_CPU_FREQUENCY=<value>
+  
+You can also directly set the cpu frequency by calling:
+
+  RubyProf.cpu_frequency = <value> 
+
+
+== Recursive Calls
+
+Recursive calls occur when method A calls method A and cycles
+occur when method A calls method B calls method C calls method A.
+ruby-prof detects both direct recursive calls and cycles.  Both
+are indicated in reports by a dash and number following a method
+name.  For example, here is a flat profile from the test method
+RecursiveTest#test_recursive:
+
+
+%self     total     self     wait    child    calls  name
+100.00      2.00     2.00     0.00     0.00        2  Kernel#sleep
+  0.00      2.00     0.00     0.00     2.00        0  RecursiveTest#test_cycle
+  0.00      0.00     0.00     0.00     0.00        2  Fixnum#==
+  0.00      0.00     0.00     0.00     0.00        2  Fixnum#-
+  0.00      1.00     0.00     0.00     1.00        1  Object#sub_cycle-1
+  0.00      2.00     0.00     0.00     2.00        1  Object#sub_cycle
+  0.00      2.00     0.00     0.00     2.00        1  Object#cycle
+  0.00      1.00     0.00     0.00     1.00        1  Object#cycle-1
+
+Notice the presence of Object#cycle and Object#cycle-1.  The -1 means
+the method was either recursively called (directly or indirectly).
+
+However, the self time values for recursive calls should always 
+be accurate.  It is also believed that the total times are
+accurate, but these should be carefully analyzed to verify their veracity.
+
+
+== Multi-threaded Applications
+
+Unfortunately, Ruby does not provide an internal api
+for detecting thread context switches.  As a result, the
+timings ruby-prof reports for each thread may be slightly
+inaccurate.  In particular, this will happen for newly 
+spanned threads that immediately go to sleep.  For instance,
+if you use Ruby's timeout library to wait for 2 seconds,
+the 2 seconds will be assigned to the foreground thread
+and not the newly created background thread.  These errors
+can largely be avoided if the background thread performs an
+operation before going to sleeep. 
+
+
+== Performance
+
+Significant effort has been put into reducing ruby-prof's overhead
+as much as possible.  Our tests show that the overhead associated
+with profiling code varies considerably with the code being
+profiled.  Most programs will run approximately twice as slow
+while highly recursive programs (like the fibonacci series test)
+will run three times slower.
+
+
+== Windows Binary
+
+The Windows binary is built with the latest version of MinGW.  The source
+repository also includes a Microsoft VC++ 2005 solution.  If you wish to run
+a debug version of ruby-prof on Windows, then it is highly recommended
+you use VC++.
+
+
+== License
+
+See LICENSE for license information.
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..2287b61
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,123 @@
+require 'rubygems'
+require 'rake/gempackagetask'
+require 'rake/rdoctask'
+require 'rake/testtask'
+require 'date'
+
+# ------- Version ----
+# Read version from header file
+version_header = File.read('ext/version.h')
+match = version_header.match(/RUBY_PROF_VERSION\s*["](\d.+)["]/)
+raise(RuntimeError, "Could not determine RUBY_PROF_VERSION") if not match
+RUBY_PROF_VERSION = match[1]
+  
+
+# ------- Default Package ----------
+FILES = FileList[
+  'Rakefile',
+  'README',
+  'LICENSE',
+  'CHANGES',
+  'bin/*',
+  'doc/**/*',
+  'examples/*',
+  'ext/*',
+  'ext/mingw/Rakefile',
+  'ext/mingw/build.rake',
+  'ext/vc/*.sln',
+  'ext/vc/*.vcproj',
+  'lib/**/*',
+  'rails/**/*',
+  'test/*'
+]
+
+# Default GEM Specification
+default_spec = Gem::Specification.new do |spec|
+  spec.name = "ruby-prof"
+  
+  spec.homepage = "http://rubyforge.org/projects/ruby-prof/"
+  spec.summary = "Fast Ruby profiler"
+  spec.description = <<-EOF
+ruby-prof is a fast code profiler for Ruby. It is a C extension and
+therefore is many times faster than the standard Ruby profiler. It
+supports both flat and graph profiles.  For each method, graph profiles
+show how long the method ran, which methods called it and which 
+methods it called. RubyProf generate both text and html and can output
+it to standard out or to a file.
+EOF
+
+  spec.version = RUBY_PROF_VERSION
+
+  spec.author = "Shugo Maeda and Charlie Savage"
+  spec.email = "shugo at ruby-lang.org and cfis at savagexi.com"
+  spec.platform = Gem::Platform::RUBY
+  spec.require_path = "lib" 
+  spec.bindir = "bin"
+  spec.executables = ["ruby-prof"]
+  spec.extensions = ["ext/extconf.rb"]
+  spec.files = FILES.to_a
+  spec.test_files = Dir["test/test_*.rb"]
+  
+
+  spec.required_ruby_version = '>= 1.8.4'
+  spec.date = DateTime.now
+  spec.rubyforge_project = 'ruby-prof'
+  
+  # rdoc
+  spec.has_rdoc = true
+end
+
+# Rake task to build the default package
+Rake::GemPackageTask.new(default_spec) do |pkg|
+  pkg.need_tar = true
+  #pkg.need_zip = true
+end
+
+
+# ------- Windows Package ----------
+if RUBY_PLATFORM.match(/win32/)
+  binaries = (FileList['ext/mingw/*.so',
+                       'ext/mingw/*.dll*'])
+
+  # Windows specification
+  win_spec = default_spec.clone
+  win_spec.extensions = ['ext/mingw/Rakefile']
+  win_spec.platform = Gem::Platform::CURRENT
+  win_spec.files += binaries.to_a
+
+  # Rake task to build the windows package
+  Rake::GemPackageTask.new(win_spec) do |pkg|
+  end
+end
+
+# ---------  RDoc Documentation ------
+desc "Generate rdoc documentation"
+Rake::RDocTask.new("rdoc") do |rdoc|
+  rdoc.rdoc_dir = 'doc'
+  rdoc.title    = "ruby-prof"
+  # Show source inline with line numbers
+  rdoc.options << "--inline-source" << "--line-numbers"
+  # Make the readme file the start page for the generated html
+  rdoc.options << '--main' << 'README'
+  rdoc.rdoc_files.include('bin/**/*',
+                          'doc/*.rdoc',
+                          'examples/flat.txt',
+                          'examples/graph.txt',
+                          'examples/graph.html',
+                          'lib/**/*.rb',
+                          'ext/ruby_prof.c',
+                          'ext/version.h',
+                          'ext/measure_*.h',
+                          'README',
+                          'LICENSE')
+end
+
+task :default => :package
+
+desc 'Run the ruby-prof test suite'
+Rake::TestTask.new do |t|
+  t.libs += %w(lib ext test)
+  t.test_files = Dir['test/test_suite.rb']
+  t.verbose = true
+  t.warning = true
+end
\ No newline at end of file
diff --git a/bin/ruby-prof b/bin/ruby-prof
new file mode 100755
index 0000000..b3f72e0
--- /dev/null
+++ b/bin/ruby-prof
@@ -0,0 +1,207 @@
+#! /usr/bin/env ruby
+
+# == Synopsis
+#
+# Profiles a Ruby program.
+#
+# == Usage
+#
+# ruby_prof [options] <script.rb> [--] [script-options]"
+#
+# Options:
+#     -p, --printer=printer            Select a printer:
+#                                        flat - Prints a flat profile as text (default).
+#                                        graph - Prints a graph profile as text.
+#                                        graph_html - Prints a graph profile as html.
+#                                        call_tree - format for KCacheGrind
+#     -f, --file=path                  Output results to a file instead of standard out.
+#     -m, --min_percent=min_percent    The minimum percent a method must take before ',
+#                                      being included in output reports.  Should be an
+#                                      integer between 1 and 100.  0 means all methods are printed.
+#         --mode=measure_mode          Select a measurement mode:
+#                                        process - Use process time (default).
+#                                        wall - Use wall time.
+#                                        cpu - Use the CPU clock counter
+#                                              (only supported on Pentium and PowerPCs).
+#                                        allocations - Tracks object allocations
+#                                              (requires a patched Ruby interpreter).
+#                                        memory - Tracks total memory size
+#                                              (requires a patched Ruby interpreter).
+#                                        gc_runs - Tracks number of garbage collection runs
+#                                              (requires a patched Ruby interpreter).
+#                                        gc_time - Tracks time spent doing garbage collection
+#                                              (requires a patched Ruby interpreter).
+#         --replace-progname           Replace $0 when loading the .rb files.
+#         --specialized-instruction    Turn on specialized instruction.
+#     -h, --help                       Show help message
+#         --version                    Show version
+# 
+#
+# See also: {flat profiles}[link:files/examples/flat_txt.html], {graph profiles}[link:files/examples/graph_txt.html], {html graph profiles}[link:files/examples/graph_html.html]
+#
+
+require 'ostruct'
+require 'optparse'
+require 'ruby-prof'
+
+options = OpenStruct.new
+options.measure_mode = RubyProf::PROCESS_TIME
+options.printer = RubyProf::FlatPrinter
+options.min_percent = 0
+options.file = nil
+options.replace_prog_name = false
+options.specialized_instruction = false
+
+opts = OptionParser.new do |opts|
+  opts.banner = "ruby_prof #{RubyProf::VERSION}\n" +
+                "Usage: ruby_prof [options] <script.rb> [--] [script-options]"
+ 
+  opts.separator ""
+  opts.separator "Options:"
+
+    
+  opts.on('-p printer', '--printer=printer', [:flat, :graph, :graph_html, :call_tree],
+          'Select a printer:',
+          '  flat - Prints a flat profile as text (default).',
+          '  graph - Prints a graph profile as text.',
+          '  graph_html - Prints a graph profile as html.',
+          '  call_tree - format for KCacheGrind' ) do |printer|
+
+          
+    case printer
+      when :flat
+        options.printer = RubyProf::FlatPrinter
+      when :graph
+        options.printer = RubyProf::GraphPrinter
+      when :graph_html
+        options.printer = RubyProf::GraphHtmlPrinter
+      when :call_tree
+        options.printer = RubyProf::CallTreePrinter
+    end
+  end
+    
+  opts.on('-m min_percent', '--min_percent=min_percent', Float,
+          'The minimum percent a method must take before ',
+          '  being included in output reports.',
+                                        '  this option is not supported for call tree.') do |min_percent|
+    options.min_percent = min_percent
+  end
+
+  opts.on('-f path', '--file=path',
+        'Output results to a file instead of standard out.') do |file|
+    options.file = file
+  end
+    
+  opts.on('--mode=measure_mode',
+      [:process, :wall, :cpu, :allocations, :memory, :gc_runs, :gc_time],
+      'Select what ruby-prof should measure:',
+      '  process - Process time (default).',
+      '  wall - Wall time.',
+      '  cpu - CPU time (Pentium and PowerPCs only).',
+      '  allocations - Object allocations (requires patched Ruby interpreter).',
+      '  memory - Allocated memory in KB (requires patched Ruby interpreter).',
+      '  gc_runs - Number of garbage collections (requires patched Ruby interpreter).',
+      '  gc_time - Time spent in garbage collection (requires patched Ruby interpreter).') do |measure_mode|
+      
+      case measure_mode
+      when :process
+        options.measure_mode = RubyProf::PROCESS_TIME     
+      when :wall
+        options.measure_mode = RubyProf::WALL_TIME      
+      when :cpu
+        options.measure_mode = RubyProf::CPU_TIME
+      when :allocations
+        options.measure_mode = RubyProf::ALLOCATIONS
+      when :memory
+        options.measure_mode = RubyProf::MEMORY
+      when :gc_runs
+        options.measure_mode = RubyProf::GC_RUNS
+      when :gc_time
+        options.measure_mode = RubyProf::GC_TIME
+      end
+  end
+        
+  opts.on("--replace-progname", "Replace $0 when loading the .rb files.") do
+          options.replace_prog_name = true
+  end
+
+  if defined?(VM)
+    opts.on("--specialized-instruction", "Turn on specified instruction.") do
+            options.specialized_instruction = true
+    end
+  end
+    
+  opts.on_tail("-h", "--help", "Show help message") do
+      puts opts
+      exit
+  end
+  
+  opts.on_tail("-v", "--version", "Show version") do
+      puts "ruby_prof " + RubyProf::VERSION
+      exit
+  end
+end
+
+begin
+  opts.parse! ARGV
+rescue OptionParser::InvalidOption, OptionParser::InvalidArgument,
+       OptionParser::MissingArgument => e
+  puts opts
+  puts
+  puts e.message
+  exit(-1)
+end
+
+# Make sure the user specified at least one file
+if ARGV.length < 1
+  puts opts
+  puts ""
+  puts "Must specify a script to run"
+  exit(-1)
+end
+
+
+# Install at_exit handler.  It is important that we do this 
+# before loading the scripts so our at_exit handler run
+# *after* any other one that will be installed. 
+
+at_exit {
+  # Stop profiling
+  result = RubyProf.stop
+
+  # Create a printer
+  printer = options.printer.new(result)
+
+  # Get output
+  if options.file
+    File.open(options.file, 'w') do |file|
+      printer.print(file, {:min_percent => options.min_percent})
+    end
+  else
+    # Print out results 
+    printer.print(STDOUT, {:min_percent => options.min_percent})
+  end
+}
+
+# Now set measure mode
+RubyProf.measure_mode = options.measure_mode
+
+# Set VM compile option
+if defined?(VM)
+  VM::InstructionSequence.compile_option = {
+    :trace_instruction => true,
+    :specialized_instruction => options.specialized_instruction
+  }
+end
+
+# Get the script we will execute
+script = ARGV.shift
+if options.replace_prog_name
+  $0 = File.expand_path(script)
+end
+
+# Start profiling
+RubyProf.start 
+
+# Load the script
+load script
\ No newline at end of file
diff --git a/examples/flat.txt b/examples/flat.txt
new file mode 100644
index 0000000..28d9afe
--- /dev/null
+++ b/examples/flat.txt
@@ -0,0 +1,55 @@
+= Flat Profiles
+
+Flat profiles show the total amount of time spent in each method.
+As an example, here is the output from running printers_test.rb.
+
+Thread ID: 21277412
+ %self  cumulative  total     self   children  calls self/call total/call  name
+ 46.34     4.06      8.72     4.06     4.66      501     0.01     0.02     Integer#upto
+ 23.89     6.16      2.09     2.09     0.00       61     0.03     0.03     Kernel.sleep
+ 15.12     7.48      1.33     1.33     0.00   250862     0.00     0.00     Fixnum#%
+ 14.13     8.72      1.24     1.24     0.00   250862     0.00     0.00     Fixnum#==
+  0.18     8.74      0.02     0.02     0.00        1     0.02     0.02     Array#each_index
+  0.17     8.75      6.64     0.01     6.63      500     0.00     0.01     Object#is_prime
+  0.17     8.77      6.66     0.01     6.64        1     0.01     6.66     Array#select
+  0.00     8.77      0.00     0.00     0.00      501     0.00     0.00     Fixnum#-
+  0.00     8.77      0.00     0.00     0.00        1     0.00     0.00     Array#first
+  0.00     8.77      0.00     0.00     0.00        1     0.00     0.00     Array#length
+  0.00     8.77      0.00     0.00     0.00        1     0.00     0.00     Array#initialize
+  0.00     8.77      8.77     0.00     8.77        1     0.00     8.77     Object#run_primes
+  0.00     8.77      0.00     0.00     0.00        1     0.00     0.00     Integer#to_int
+  0.00     8.77      6.66     0.00     6.66        1     0.00     6.66     Object#find_primes
+  0.00     8.77      2.09     0.00     2.09        1     0.00     2.09     Object#find_largest
+  0.00     8.77      0.02     0.00     0.02        1     0.00     0.02     Object#make_random_array
+  0.00     8.77      0.00     0.00     0.00        1     0.00     0.00     Class#new
+  0.00     8.77      0.00     0.00     0.00      500     0.00     0.00     Array#[]=
+  0.00     8.77      0.00     0.00     0.00       61     0.00     0.00     Fixnum#>
+  0.00     8.77      0.00     0.00     0.00       61     0.00     0.00     Array#[]
+  0.00     8.77      8.77     0.00     8.77        1     0.00     8.77     #toplevel
+  0.00     8.77      0.00     0.00     0.00      500     0.00     0.00     Kernel.rand
+
+All values are in seconds. 
+
+The columns are:
+
+	%self        - The percentage of time spent in this method, derived from self_time/total_time
+	cumulative   - The sum of the time spent in this method and all the methods listed above it.
+	total        - The time spent in this method and its children.
+	self         - The time spent in this method.
+	children     - The time spent in this method's children.
+	calls        - The number of times this method was called.
+	self/call    - The average time spent per call in this method.
+  total/call   - The average time spent per call in this method and its children.
+  name         - The name of the method.
+
+Methods are sorted based on %self, therefore the methods that execute the longest are listed
+first.
+
+The interpretation of method names is:
+* #toplevel - The root method that calls all other methods
+* MyObject#test - An instance method "test" of the class "MyObject"
+* <Object:MyObject>#test - The <> characters indicate a singleton method on a singleton class.
+
+For example, wee can see that Integer#upto took the most time, 4.06 seconds.  An additional
+4.66 seconds were spent in its children, for a total time of 8.72 seconds.
+
diff --git a/examples/graph.html b/examples/graph.html
new file mode 100644
index 0000000..46b1f9f
--- /dev/null
+++ b/examples/graph.html
@@ -0,0 +1,823 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<style media="all" type="text/css">
+    table {
+	    border-collapse: collapse;
+	    border: 1px solid #CCC;
+	    font-family: Verdana, Arial, Helvetica, sans-serif;
+	    font-size: 9pt;
+	    line-height: normal;
+    }
+
+    th {
+	    text-align: center;
+	    border-top: 1px solid #FB7A31;
+	    border-bottom: 1px solid #FB7A31;
+	    background: #FFC;
+	    padding: 0.3em;
+	    border-left: 1px solid silver;
+    }
+
+		tr.break td {
+		  border: 0;
+	    border-top: 1px solid #FB7A31;
+			padding: 0;
+			margin: 0;
+		}
+
+    tr.method td {
+			font-weight: bold;
+    }
+
+    td {
+	    padding: 0.3em;
+    }
+
+    td:first-child {
+	    width: 190px;
+	    }
+
+    td {
+	    border-left: 1px solid #CCC;
+	    text-align: center;
+    }	
+  </style>
+</head>
+<body>
+<h1> Graph Profile</h1>
+<p>Graph profiles show how long each method runs, which methods call it
+  and which methods it calls. To understand how to read a graph profile, refer to
+   the <a href="graph.txt">documentation</a> for the text graph report. </p>
+<p>The main advantage of an HTML graph format versus the text format is the use of
+  hyperlinks to jump between methods. This makes it easier to understand the flow
+of control through a program and which methods take the most time.</p>
+<p>Below is the  output from running printers_test.rb reproduced in HTML. </p>
+<p>Profile Report</p>
+
+<table>
+  <tr>
+    <th>Thread ID</th>
+    <th>Total Time</th>
+  </tr>
+  <tr>
+    <td><a href="#21277412">21277412</a></td>
+    <td>8.766</td>
+  </tr>
+</table>
+
+<h2><a name="21277412">Thread 21277412</a></h2>
+<table>
+  <tr>
+    <th> %Total</th>
+    <th> %Self</th>
+    <th> Total</th>
+    <th> Self</th>
+    <th> Children</th>
+    <th> Calls</th>
+    <th>Name</th>
+  </tr>
+
+  <tr class="method">
+    <td> 100.00%</td>
+    <td> 0.00%</td>
+    <td> 8.77</td>
+    <td> 0.00</td>
+    <td> 8.77</td>
+    <td> 1</td>
+    <td><a name="_toplevel_21277412">#toplevel</a></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 8.77</td>
+    <td> 0.00</td>
+    <td> 8.77</td>
+    <td> 1/1</td>
+    <td><a href="#Object_run_primes_21277412">Object#run_primes</a></td>
+  </tr>
+
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 8.77</td>
+    <td> 0.00</td>
+    <td> 8.77</td>
+    <td> 1/1</td>
+    <td><a href="#_toplevel_21277412">#toplevel</a></td>
+  </tr>
+  <tr class="method">
+    <td> 100.00%</td>
+    <td> 0.00%</td>
+    <td> 8.77</td>
+    <td> 0.00</td>
+    <td> 8.77</td>
+    <td> 1</td>
+    <td><a name="Object_run_primes_21277412">Object#run_primes</a></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.02</td>
+    <td> 0.00</td>
+    <td> 0.02</td>
+    <td> 1/1</td>
+    <td><a href="#Object_make_random_array_21277412">Object#make_random_array</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 2.09</td>
+    <td> 0.00</td>
+    <td> 2.09</td>
+    <td> 1/1</td>
+    <td><a href="#Object_find_largest_21277412">Object#find_largest</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 6.66</td>
+    <td> 0.00</td>
+    <td> 6.66</td>
+    <td> 1/1</td>
+    <td><a href="#Object_find_primes_21277412">Object#find_primes</a></td>
+  </tr>
+
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 6.63</td>
+    <td> 4.06</td>
+    <td> 2.56</td>
+    <td> 500/501</td>
+    <td><a href="#Object_is_prime_21277412">Object#is_prime</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 2.09</td>
+    <td> 0.00</td>
+    <td> 2.09</td>
+    <td> 1/501</td>
+    <td><a href="#Object_find_largest_21277412">Object#find_largest</a></td>
+  </tr>
+  <tr class="method">
+    <td> 99.48%</td>
+    <td> 46.34%</td>
+    <td> 8.72</td>
+    <td> 4.06</td>
+    <td> 4.66</td>
+    <td> 501</td>
+    <td><a name="Integer_upto_21277412">Integer#upto</a></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 61/61</td>
+    <td><a href="#Array_[]_21277412">Array#[]</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 61/61</td>
+    <td><a href="#Fixnum_>_21277412">Fixnum#_</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 2.09</td>
+    <td> 2.09</td>
+    <td> 0.00</td>
+    <td> 61/61</td>
+    <td><a href="#Kernel_sleep_21277412">Kernel.sleep</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 1.24</td>
+    <td> 1.24</td>
+    <td> 0.00</td>
+    <td> 250862/250862</td>
+    <td><a href="#Fixnum____21277412">Fixnum#==</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 1.33</td>
+    <td> 1.33</td>
+    <td> 0.00</td>
+    <td> 250862/250862</td>
+    <td><a href="#Fixnum_%_21277412">Fixnum#%</a></td>
+  </tr>
+
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 6.66</td>
+    <td> 0.01</td>
+    <td> 6.64</td>
+    <td> 1/1</td>
+    <td><a href="#Object_find_primes_21277412">Object#find_primes</a></td>
+  </tr>
+  <tr class="method">
+    <td> 75.93%</td>
+    <td> 0.17%</td>
+    <td> 6.66</td>
+    <td> 0.01</td>
+    <td> 6.64</td>
+    <td> 1</td>
+    <td><a name="Array_select_21277412">Array#select</a></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 6.64</td>
+    <td> 0.01</td>
+    <td> 6.63</td>
+    <td> 500/500</td>
+    <td><a href="#Object_is_prime_21277412">Object#is_prime</a></td>
+  </tr>
+
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 6.66</td>
+    <td> 0.00</td>
+    <td> 6.66</td>
+    <td> 1/1</td>
+    <td><a href="#Object_run_primes_21277412">Object#run_primes</a></td>
+  </tr>
+  <tr class="method">
+    <td> 75.93%</td>
+    <td> 0.00%</td>
+    <td> 6.66</td>
+    <td> 0.00</td>
+    <td> 6.66</td>
+    <td> 1</td>
+    <td><a name="Object_find_primes_21277412">Object#find_primes</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 6.66</td>
+    <td> 0.01</td>
+    <td> 6.64</td>
+    <td> 1/1</td>
+    <td><a href="#Array_select_21277412">Array#select</a></td>
+  </tr>
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 6.64</td>
+    <td> 0.01</td>
+    <td> 6.63</td>
+    <td> 500/500</td>
+    <td><a href="#Array_select_21277412">Array#select</a></td>
+  </tr>
+  <tr class="method">
+    <td> 75.76%</td>
+    <td> 0.17%</td>
+    <td> 6.64</td>
+    <td> 0.01</td>
+    <td> 6.63</td>
+    <td> 500</td>
+    <td><a name="Object_is_prime_21277412">Object#is_prime</a></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 500/501</td>
+    <td><a href="#Fixnum_-_21277412">Fixnum#-</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 6.63</td>
+    <td> 4.06</td>
+    <td> 2.56</td>
+    <td> 500/501</td>
+    <td><a href="#Integer_upto_21277412">Integer#upto</a></td>
+  </tr>
+
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 2.09</td>
+    <td> 0.00</td>
+    <td> 2.09</td>
+    <td> 1/1</td>
+    <td><a href="#Object_run_primes_21277412">Object#run_primes</a></td>
+  </tr>
+  <tr class="method">
+    <td> 23.89%</td>
+    <td> 0.00%</td>
+    <td> 2.09</td>
+    <td> 0.00</td>
+    <td> 2.09</td>
+    <td> 1</td>
+    <td><a name="Object_find_largest_21277412">Object#find_largest</a></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/501</td>
+    <td><a href="#Fixnum_-_21277412">Fixnum#-</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 2.09</td>
+    <td> 0.00</td>
+    <td> 2.09</td>
+    <td> 1/501</td>
+    <td><a href="#Integer_upto_21277412">Integer#upto</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Array_first_21277412">Array#first</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Array_length_21277412">Array#length</a></td>
+  </tr>
+
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 2.09</td>
+    <td> 2.09</td>
+    <td> 0.00</td>
+    <td> 61/61</td>
+    <td><a href="#Integer_upto_21277412">Integer#upto</a></td>
+  </tr>
+  <tr class="method">
+    <td> 23.89%</td>
+    <td> 23.89%</td>
+    <td> 2.09</td>
+    <td> 2.09</td>
+    <td> 0.00</td>
+    <td> 61</td>
+    <td><a name="Kernel_sleep_21277412">Kernel.sleep</a></td>
+  </tr>
+
+
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 1.33</td>
+    <td> 1.33</td>
+    <td> 0.00</td>
+    <td> 250862/250862</td>
+    <td><a href="#Integer_upto_21277412">Integer#upto</a></td>
+  </tr>
+  <tr class="method">
+    <td> 15.12%</td>
+    <td> 15.12%</td>
+    <td> 1.33</td>
+    <td> 1.33</td>
+    <td> 0.00</td>
+    <td> 250862</td>
+    <td><a name="Fixnum_%_21277412">Fixnum#%</a></td>
+  </tr>
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 1.24</td>
+    <td> 1.24</td>
+    <td> 0.00</td>
+    <td> 250862/250862</td>
+    <td><a href="#Integer_upto_21277412">Integer#upto</a></td>
+  </tr>
+  <tr class="method">
+    <td> 14.13%</td>
+    <td> 14.13%</td>
+    <td> 1.24</td>
+    <td> 1.24</td>
+    <td> 0.00</td>
+    <td> 250862</td>
+    <td><a name="Fixnum____21277412">Fixnum#==</a></td>
+  </tr>
+  
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.02</td>
+    <td> 0.00</td>
+    <td> 0.02</td>
+    <td> 1/1</td>
+    <td><a href="#Object_run_primes_21277412">Object#run_primes</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.18%</td>
+    <td> 0.00%</td>
+    <td> 0.02</td>
+    <td> 0.00</td>
+    <td> 0.02</td>
+    <td> 1</td>
+    <td><a name="Object_make_random_array_21277412">Object#make_random_array</a></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.02</td>
+    <td> 0.02</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Array_each_index_21277412">Array#each_index</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Class_new_21277412">Class#new</a></td>
+  </tr>
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.02</td>
+    <td> 0.02</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Object_make_random_array_21277412">Object#make_random_array</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.18%</td>
+    <td> 0.18%</td>
+    <td> 0.02</td>
+    <td> 0.02</td>
+    <td> 0.00</td>
+    <td> 1</td>
+    <td><a name="Array_each_index_21277412">Array#each_index</a></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 500/500</td>
+    <td><a href="#Kernel_rand_21277412">Kernel.rand</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 500/500</td>
+    <td><a href="#Array_[]__21277412">Array#[]=</a></td>
+  </tr>
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 500/501</td>
+    <td><a href="#Object_is_prime_21277412">Object#is_prime</a></td>
+  </tr>
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/501</td>
+    <td><a href="#Object_find_largest_21277412">Object#find_largest</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.00%</td>
+    <td> 0.00%</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 501</td>
+    <td><a name="Fixnum_-_21277412">Fixnum#-</a></td>
+  </tr>
+  
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Kernel_rand_21277412">Kernel.rand</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.00%</td>
+    <td> 0.00%</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1</td>
+    <td><a name="Integer_to_int_21277412">Integer#to_int</a></td>
+  </tr>
+  
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Object_find_largest_21277412">Object#find_largest</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.00%</td>
+    <td> 0.00%</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1</td>
+    <td><a name="Array_first_21277412">Array#first</a></td>
+  </tr>
+  
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Class_new_21277412">Class#new</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.00%</td>
+    <td> 0.00%</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1</td>
+    <td><a name="Array_initialize_21277412">Array#initialize</a></td>
+  </tr>
+  
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Object_find_largest_21277412">Object#find_largest</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.00%</td>
+    <td> 0.00%</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1</td>
+    <td><a name="Array_length_21277412">Array#length</a></td>
+  </tr>
+  
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Object_make_random_array_21277412">Object#make_random_array</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.00%</td>
+    <td> 0.00%</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1</td>
+    <td><a name="Class_new_21277412">Class#new</a></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Array_initialize_21277412">Array#initialize</a></td>
+  </tr>
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 61/61</td>
+    <td><a href="#Integer_upto_21277412">Integer#upto</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.00%</td>
+    <td> 0.00%</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 61</td>
+    <td><a name="Fixnum_>_21277412">Fixnum#_</a></td>
+  </tr>
+  
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 61/61</td>
+    <td><a href="#Integer_upto_21277412">Integer#upto</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.00%</td>
+    <td> 0.00%</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 61</td>
+    <td><a name="Array_[]_21277412">Array#[]</a></td>
+  </tr>
+  
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 500/500</td>
+    <td><a href="#Array_each_index_21277412">Array#each_index</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.00%</td>
+    <td> 0.00%</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 500</td>
+    <td><a name="Array_[]__21277412">Array#[]=</a></td>
+  </tr>
+  
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 500/500</td>
+    <td><a href="#Array_each_index_21277412">Array#each_index</a></td>
+  </tr>
+  <tr class="method">
+    <td> 0.00%</td>
+    <td> 0.00%</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 500</td>
+    <td><a name="Kernel_rand_21277412">Kernel.rand</a></td>
+  </tr>
+  
+  <tr>
+    <td> </td>
+    <td> </td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 0.00</td>
+    <td> 1/1</td>
+    <td><a href="#Integer_to_int_21277412">Integer#to_int</a></td>
+  </tr>
+  
+  <tr class="break">
+    <td colspan="7"></td>
+  </tr>
+</table>
+</body>
+</html>
diff --git a/examples/graph.txt b/examples/graph.txt
new file mode 100644
index 0000000..99c071d
--- /dev/null
+++ b/examples/graph.txt
@@ -0,0 +1,170 @@
+= Graph Profiles
+
+Graph profiles show how long each method runs, which methods call it
+and which methods it calls.
+
+As an example, here is the output from running printers_test.rb:
+
+
+Thread ID: 21277412
+  %total   %self     total      self    children               calls   Name
+ --------------------------------------------------------------------------------
+ 100.00%   0.00%      8.77      0.00      8.77                   1     #toplevel
+                      8.77      0.00      8.77                 1/1     Object#run_primes
+ --------------------------------------------------------------------------------
+                      8.77      0.00      8.77                 1/1     #toplevel
+ 100.00%   0.00%      8.77      0.00      8.77                   1     Object#run_primes
+                      0.02      0.00      0.02                 1/1     Object#make_random_array
+                      2.09      0.00      2.09                 1/1     Object#find_largest
+                      6.66      0.00      6.66                 1/1     Object#find_primes
+ --------------------------------------------------------------------------------
+                      6.63      4.06      2.56             500/501     Object#is_prime
+                      2.09      0.00      2.09               1/501     Object#find_largest
+  99.48%  46.34%      8.72      4.06      4.66                 501     Integer#upto
+                      0.00      0.00      0.00               61/61     Array#[]
+                      0.00      0.00      0.00               61/61     Fixnum#>
+                      2.09      2.09      0.00               61/61     Kernel.sleep
+                      1.24      1.24      0.00       250862/250862     Fixnum#==
+                      1.33      1.33      0.00       250862/250862     Fixnum#%
+ --------------------------------------------------------------------------------
+                      6.66      0.01      6.64                 1/1     Object#find_primes
+  75.93%   0.17%      6.66      0.01      6.64                   1     Array#select
+                      6.64      0.01      6.63             500/500     Object#is_prime
+ --------------------------------------------------------------------------------
+                      6.66      0.00      6.66                 1/1     Object#run_primes
+  75.93%   0.00%      6.66      0.00      6.66                   1     Object#find_primes
+                      6.66      0.01      6.64                 1/1     Array#select
+ --------------------------------------------------------------------------------
+                      6.64      0.01      6.63             500/500     Array#select
+  75.76%   0.17%      6.64      0.01      6.63                 500     Object#is_prime
+                      0.00      0.00      0.00             500/501     Fixnum#-
+                      6.63      4.06      2.56             500/501     Integer#upto
+ --------------------------------------------------------------------------------
+                      2.09      0.00      2.09                 1/1     Object#run_primes
+  23.89%   0.00%      2.09      0.00      2.09                   1     Object#find_largest
+                      0.00      0.00      0.00               1/501     Fixnum#-
+                      2.09      0.00      2.09               1/501     Integer#upto
+                      0.00      0.00      0.00                 1/1     Array#first
+                      0.00      0.00      0.00                 1/1     Array#length
+ --------------------------------------------------------------------------------
+                      2.09      2.09      0.00               61/61     Integer#upto
+  23.89%  23.89%      2.09      2.09      0.00                  61     Kernel.sleep
+ --------------------------------------------------------------------------------
+                      1.33      1.33      0.00       250862/250862     Integer#upto
+  15.12%  15.12%      1.33      1.33      0.00              250862     Fixnum#%
+ --------------------------------------------------------------------------------
+                      1.24      1.24      0.00       250862/250862     Integer#upto
+  14.13%  14.13%      1.24      1.24      0.00              250862     Fixnum#==
+ --------------------------------------------------------------------------------
+                      0.02      0.00      0.02                 1/1     Object#run_primes
+   0.18%   0.00%      0.02      0.00      0.02                   1     Object#make_random_array
+                      0.02      0.02      0.00                 1/1     Array#each_index
+                      0.00      0.00      0.00                 1/1     Class#new
+ --------------------------------------------------------------------------------
+                      0.02      0.02      0.00                 1/1     Object#make_random_array
+   0.18%   0.18%      0.02      0.02      0.00                   1     Array#each_index
+                      0.00      0.00      0.00             500/500     Kernel.rand
+                      0.00      0.00      0.00             500/500     Array#[]=
+ --------------------------------------------------------------------------------
+                      0.00      0.00      0.00             500/501     Object#is_prime
+                      0.00      0.00      0.00               1/501     Object#find_largest
+   0.00%   0.00%      0.00      0.00      0.00                 501     Fixnum#-
+ --------------------------------------------------------------------------------
+                      0.00      0.00      0.00                 1/1     Kernel.rand
+   0.00%   0.00%      0.00      0.00      0.00                   1     Integer#to_int
+ --------------------------------------------------------------------------------
+                      0.00      0.00      0.00                 1/1     Object#find_largest
+   0.00%   0.00%      0.00      0.00      0.00                   1     Array#first
+ --------------------------------------------------------------------------------
+                      0.00      0.00      0.00                 1/1     Class#new
+   0.00%   0.00%      0.00      0.00      0.00                   1     Array#initialize
+ --------------------------------------------------------------------------------
+                      0.00      0.00      0.00                 1/1     Object#find_largest
+   0.00%   0.00%      0.00      0.00      0.00                   1     Array#length
+ --------------------------------------------------------------------------------
+                      0.00      0.00      0.00                 1/1     Object#make_random_array
+   0.00%   0.00%      0.00      0.00      0.00                   1     Class#new
+                      0.00      0.00      0.00                 1/1     Array#initialize
+ --------------------------------------------------------------------------------
+                      0.00      0.00      0.00               61/61     Integer#upto
+   0.00%   0.00%      0.00      0.00      0.00                  61     Fixnum#>
+ --------------------------------------------------------------------------------
+                      0.00      0.00      0.00               61/61     Integer#upto
+   0.00%   0.00%      0.00      0.00      0.00                  61     Array#[]
+ --------------------------------------------------------------------------------
+                      0.00      0.00      0.00             500/500     Array#each_index
+   0.00%   0.00%      0.00      0.00      0.00                 500     Array#[]=
+ --------------------------------------------------------------------------------
+                      0.00      0.00      0.00             500/500     Array#each_index
+   0.00%   0.00%      0.00      0.00      0.00                 500     Kernel.rand
+                      0.00      0.00      0.00                 1/1     Integer#to_int
+
+
+
+== Overview
+Dashed lines divide the report into entries, with one entry
+per method.  Entries are sorted by total time which is the 
+time spent in the method plus its children. 
+
+Each entry has a primary line demarked by values in the 
+%total and %self columns.  The primary line represents 
+the method being profiled.  Lines above it are the methods
+that called this method (parents) while the lines below it
+are the methods it called (children).
+
+All values are in seconds.  For the primary line, the columns represent:
+
+	%total       - The percentage of time spent in this method and its children
+	%self        - The percentage of time spent in this method
+	total        - The time spent in this method and its children.
+	self         - The time spent in this method.
+	children     - The time spent in this method's children.
+	calls        - The number of times this method was called.
+	name         - The name of the method.
+  
+The interpretation of method names is:
+* #toplevel - The root method that calls all other methods
+* MyObject#test - An instance method "test" of the class "MyObject"
+* <Object:MyObject>#test - The <> characters indicate a singleton method on a singleton class.
+
+For example, we see that 99.48% of the time was spent in Integer#upto and its children.
+Of that time, 4.06 seconds was spent in Integer#upto itself and 4.66 in its children.
+Overall, Integer#upto was called 501 times.
+
+== Parents
+In each entry, the lines above the primary line are the methods that 
+called the current method.  If the current method is a root method then
+no parents are shown.
+
+
+For parent lines, the columns represent:
+
+	total        - The time spent in the current method and it children on behalf of the parent method.
+	self         - The time spent in this method on behalf of the parent method.
+	children     - The time spent in this method's children on behalf of the parent.
+	calls        - The number of times the parent method called this child
+  
+Looking at Integer#upto again, we see that it was called 500 times from Object#is_prime
+and 1 time from find_largest.  Of the 8.72 total seconds spent in Integer#upto, 6.63
+were done for Object#is_prime and 2.09 for Object#find_largest.
+
+
+== Children
+In each entry, the lines below the primary line are the methods that 
+the current method called.  If the current method is a leaf method then
+no children are shown.
+
+For children lines, the columns represent:
+
+	total        - The time spent in the child, and its children, on behalf of the current method
+	self         - The time spent in the child on behalf of the current method.
+	children     - The time spent in the child's children (ie, granchildren) in behalf of the current method
+	calls        - The number of times the child method was called by the current method.
+
+Taking our example of Integer#upto, we see that it called five other methods - Array#[],
+Fixnum#>, Kernel.sleep, Fixnum#= and Fixnum#%.  Looking at Kernel.sleep, we see that 
+its spent 2.09 seconds working for Integer#upto and its children spent 0 time working for
+Integer#upto.  To see the overall time Kernel.sleep took we would have to look up its entry
+in the graph table.
+
+
diff --git a/ext/extconf.rb b/ext/extconf.rb
new file mode 100644
index 0000000..363ae3c
--- /dev/null
+++ b/ext/extconf.rb
@@ -0,0 +1,34 @@
+require "mkmf"
+
+if RUBY_VERSION >= "1.9"
+  if RUBY_RELEASE_DATE < "2005-03-17"
+    STDERR.print("Ruby version is too old\n")
+    exit(1)
+  end
+elsif RUBY_VERSION >= "1.8"
+  if RUBY_RELEASE_DATE < "2005-03-22"
+    STDERR.print("Ruby version is too old\n")
+    exit(1)
+  end
+else
+  STDERR.print("Ruby version is too old\n")
+  exit(1)
+end
+
+have_header("sys/times.h")
+
+# Stefan Kaes / Alexander Dymo GC patch
+have_func("rb_os_allocated_objects")
+have_func("rb_gc_allocated_size")
+have_func("rb_gc_collections")
+have_func("rb_gc_time")
+
+# Lloyd Hilaiel's heap info patch
+have_func("rb_heap_total_mem")
+have_func("rb_gc_heap_info")
+
+# Ruby 1.9 unexposed methods
+have_func("rb_gc_malloc_allocations")
+have_func("rb_gc_malloc_allocated_size")
+
+create_makefile("ruby_prof")
diff --git a/ext/measure_allocations.h b/ext/measure_allocations.h
new file mode 100644
index 0000000..a7e09f7
--- /dev/null
+++ b/ext/measure_allocations.h
@@ -0,0 +1,58 @@
+/* :nodoc: 
+ * Copyright (C) 2008  Shugo Maeda <shugo at ruby-lang.org>
+ *                     Charlie Savage <cfis at savagexi.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+
+#include <ruby.h>
+
+#if defined(HAVE_RB_OS_ALLOCATED_OBJECTS)
+#define MEASURE_ALLOCATIONS 3
+ 
+static prof_measure_t
+measure_allocations()
+{ 
+    return rb_os_allocated_objects(); 
+}
+
+static double
+convert_allocations(prof_measure_t c)
+{ 
+    return  c; 
+}
+
+/* Document-method: prof_measure_allocations
+   call-seq:
+     measure_allocations -> int
+
+Returns the total number of object allocations since Ruby started.*/
+static VALUE
+prof_measure_allocations(VALUE self)
+{
+#if defined(HAVE_LONG_LONG)
+    return ULL2NUM(rb_os_allocated_objects());
+#else
+    return ULONG2NUM(rb_os_allocated_objects());
+#endif
+}
+#endif
diff --git a/ext/measure_cpu_time.h b/ext/measure_cpu_time.h
new file mode 100644
index 0000000..df382e1
--- /dev/null
+++ b/ext/measure_cpu_time.h
@@ -0,0 +1,152 @@
+/* :nodoc: 
+ * Copyright (C) 2008  Shugo Maeda <shugo at ruby-lang.org>
+ *                     Charlie Savage <cfis at savagexi.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+
+#include <ruby.h>
+
+#if defined(_WIN32) || (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || defined(__ppc__)))
+#define MEASURE_CPU_TIME 2
+
+static unsigned long long cpu_frequency;
+
+#if defined(__GNUC__)
+
+#include <stdint.h>
+
+static prof_measure_t
+measure_cpu_time()
+{
+#if defined(__i386__) || defined(__x86_64__) 
+    uint32_t a, d;
+    __asm__ volatile("rdtsc" : "=a" (a), "=d" (d));
+    return ((uint64_t)d << 32) + a;
+#elif defined(__powerpc__) || defined(__ppc__)
+    unsigned long long x, y;
+
+    __asm__ __volatile__ ("\n\
+1:	mftbu   %1\n\
+  mftb    %L0\n\
+  mftbu   %0\n\
+  cmpw    %0,%1\n\
+  bne-    1b"
+  : "=r" (x), "=r" (y));
+    return x;
+#endif
+}
+
+#elif defined(_WIN32)
+
+static prof_measure_t
+measure_cpu_time()
+{
+    prof_measure_t cycles = 0;
+
+    __asm
+    {
+        rdtsc
+        mov DWORD PTR cycles, eax
+        mov DWORD PTR [cycles + 4], edx
+    }
+    return cycles;
+}
+
+#endif
+
+
+/* The _WIN32 check is needed for msys (and maybe cygwin?) */
+#if defined(__GNUC__) && !defined(_WIN32)
+
+unsigned long long get_cpu_frequency()
+{
+    unsigned long long x, y;
+
+    struct timespec ts;
+    ts.tv_sec = 0;
+    ts.tv_nsec = 500000000;
+    x = measure_cpu_time();
+    nanosleep(&ts, NULL);
+    y = measure_cpu_time();
+    return (y - x) * 2;
+}
+
+#elif defined(_WIN32)
+
+unsigned long long get_cpu_frequency()
+{
+    unsigned long long x, y;
+    unsigned long long frequency;
+    x = measure_cpu_time();
+
+    /* Use the windows sleep function, not Ruby's */
+    Sleep(500);
+    y = measure_cpu_time();
+    frequency = 2*(y-x);
+    return frequency;
+}
+#endif
+
+static double
+convert_cpu_time(prof_measure_t c)
+{
+    return (double) c / cpu_frequency;
+}
+
+/* Document-method: prof_measure_cpu_time
+   call-seq:
+     measure_cpu_time -> float
+
+Returns the cpu time.*/
+static VALUE
+prof_measure_cpu_time(VALUE self)
+{
+    return rb_float_new(convert_cpu_time(measure_cpu_time()));
+}
+
+/* Document-method: prof_get_cpu_frequency
+   call-seq:
+     cpu_frequency -> int
+
+Returns the cpu's frequency.  This value is needed when 
+RubyProf::measure_mode is set to CPU_TIME. */
+static VALUE
+prof_get_cpu_frequency(VALUE self)
+{
+    return ULL2NUM(cpu_frequency);
+}
+
+/* Document-method: prof_set_cpu_frequency
+   call-seq:
+     cpu_frequency=value -> void
+
+Sets the cpu's frequency.   This value is needed when 
+RubyProf::measure_mode is set to CPU_TIME. */
+static VALUE
+prof_set_cpu_frequency(VALUE self, VALUE val)
+{
+    cpu_frequency = NUM2LL(val);
+    return val;
+}
+
+#endif
diff --git a/ext/measure_gc_runs.h b/ext/measure_gc_runs.h
new file mode 100644
index 0000000..e98e325
--- /dev/null
+++ b/ext/measure_gc_runs.h
@@ -0,0 +1,76 @@
+/* :nodoc: 
+ * Copyright (C) 2008  Shugo Maeda <shugo at ruby-lang.org>
+ *                     Charlie Savage <cfis at savagexi.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+
+#if defined(HAVE_RB_GC_COLLECTIONS)
+#define MEASURE_GC_RUNS 5
+
+static prof_measure_t
+measure_gc_runs()
+{
+    return NUM2INT(rb_gc_collections());
+}
+
+static double
+convert_gc_runs(prof_measure_t c)
+{
+    return c;
+}
+
+/* Document-method: prof_measure_gc_runs
+   call-seq:
+     gc_runs -> Integer
+
+Returns the total number of garbage collections.*/
+static VALUE
+prof_measure_gc_runs(VALUE self)
+{
+    return rb_gc_collections();
+}
+
+#elif defined(HAVE_RB_GC_HEAP_INFO)
+#define MEASURE_GC_RUNS 5
+
+static prof_measure_t
+measure_gc_runs()
+{
+  VALUE h = rb_gc_heap_info();
+  return NUM2UINT(rb_hash_aref(h, rb_str_new2("num_gc_passes")));
+}
+
+static double
+convert_gc_runs(prof_measure_t c)
+{
+    return c;
+}
+
+static VALUE
+prof_measure_gc_runs(VALUE self)
+{
+  VALUE h = rb_gc_heap_info();
+  return rb_hash_aref(h, rb_str_new2("num_gc_passes"));
+}
+
+#endif
diff --git a/ext/measure_gc_time.h b/ext/measure_gc_time.h
new file mode 100644
index 0000000..ee0057b
--- /dev/null
+++ b/ext/measure_gc_time.h
@@ -0,0 +1,57 @@
+/* :nodoc: 
+ * Copyright (C) 2008  Shugo Maeda <shugo at ruby-lang.org>
+ *                     Charlie Savage <cfis at savagexi.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+
+#if defined(HAVE_RB_GC_TIME)
+#define MEASURE_GC_TIME 6
+
+static prof_measure_t
+measure_gc_time()
+{
+#if HAVE_LONG_LONG
+    return NUM2LL(rb_gc_time());
+#else
+    return NUM2LONG(rb_gc_time());
+#endif
+}
+
+static double
+convert_gc_time(prof_measure_t c)
+{
+    return (double) c / 1000000;
+}
+
+/* Document-method: prof_measure_gc_time
+   call-seq:
+     gc_time -> Integer
+
+Returns the time spent doing garbage collections in microseconds.*/
+static VALUE
+prof_measure_gc_time(VALUE self)
+{
+    return rb_gc_time();
+}
+
+#endif
diff --git a/ext/measure_memory.h b/ext/measure_memory.h
new file mode 100644
index 0000000..c613db7
--- /dev/null
+++ b/ext/measure_memory.h
@@ -0,0 +1,101 @@
+/* :nodoc: 
+ * Copyright (C) 2008  Alexander Dymo <adymo at pluron.com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+
+
+#if defined(HAVE_RB_GC_ALLOCATED_SIZE)
+#define MEASURE_MEMORY 4
+#define TOGGLE_GC_STATS 1
+ 
+static prof_measure_t
+measure_memory()
+{
+#if defined(HAVE_LONG_LONG)
+    return NUM2LL(rb_gc_allocated_size());
+#else
+    return NUM2ULONG(rb_gc_allocated_size());
+#endif
+}
+
+static double
+convert_memory(prof_measure_t c)
+{ 
+    return (double) c / 1024; 
+}
+
+/* Document-method: prof_measure_memory
+   call-seq:
+     measure_memory -> int
+
+Returns total allocated memory in bytes.*/
+static VALUE
+prof_measure_memory(VALUE self)
+{
+    return rb_gc_allocated_size();
+}
+
+#elif defined(HAVE_RB_GC_MALLOC_ALLOCATED_SIZE)
+#define MEASURE_MEMORY 4
+
+static prof_measure_t
+measure_memory()
+{
+    return rb_gc_malloc_allocated_size();
+}
+
+static double
+convert_memory(prof_measure_t c)
+{
+    return (double) c / 1024;
+}
+
+static VALUE
+prof_measure_memory(VALUE self)
+{
+    return UINT2NUM(rb_gc_malloc_allocated_size());
+}
+
+#elif defined(HAVE_RB_HEAP_TOTAL_MEM)
+#define MEASURE_MEMORY 4
+
+static prof_measure_t
+measure_memory()
+{
+    return rb_heap_total_mem();
+}
+
+static double
+convert_memory(prof_measure_t c)
+{
+    return (double) c / 1024;
+}
+
+static VALUE
+prof_measure_memory(VALUE self)
+{
+    return ULONG2NUM(rb_heap_total_mem());
+}
+
+#endif
diff --git a/ext/measure_process_time.h b/ext/measure_process_time.h
new file mode 100644
index 0000000..b104967
--- /dev/null
+++ b/ext/measure_process_time.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008  Shugo Maeda <shugo at ruby-lang.org>
+ *                     Charlie Savage <cfis at savagexi.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+
+#include <time.h>
+
+#define MEASURE_PROCESS_TIME 0
+
+static prof_measure_t
+measure_process_time()
+{
+    return clock();
+}
+
+static double
+convert_process_time(prof_measure_t c)
+{
+    return (double) c / CLOCKS_PER_SEC;
+}
+
+/* Document-method: measure_process_time
+   call-seq:
+     measure_process_time -> float
+
+Returns the process time.*/
+static VALUE
+prof_measure_process_time(VALUE self)
+{
+    return rb_float_new(convert_process_time(measure_process_time()));
+}
diff --git a/ext/measure_wall_time.h b/ext/measure_wall_time.h
new file mode 100644
index 0000000..faa550a
--- /dev/null
+++ b/ext/measure_wall_time.h
@@ -0,0 +1,53 @@
+/* :nodoc: 
+ * Copyright (C) 2008  Shugo Maeda <shugo at ruby-lang.org>
+ *                     Charlie Savage <cfis at savagexi.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+
+
+#define MEASURE_WALL_TIME 1
+
+static prof_measure_t
+measure_wall_time()
+{
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+static double
+convert_wall_time(prof_measure_t c)
+{
+    return (double) c / 1000000;
+}
+
+/* Document-method: prof_measure_wall_time
+   call-seq:
+     measure_wall_time -> float
+
+Returns the wall time.*/
+static VALUE
+prof_measure_wall_time(VALUE self)
+{
+    return rb_float_new(convert_wall_time(measure_wall_time()));
+}
diff --git a/ext/mingw/Rakefile b/ext/mingw/Rakefile
new file mode 100644
index 0000000..add032d
--- /dev/null
+++ b/ext/mingw/Rakefile
@@ -0,0 +1,23 @@
+# We can't use Ruby's standard build procedures
+# on Windows because the Ruby executable is
+# built with VC++ while here we want to build
+# with MingW.  So just roll our own...
+
+require 'fileutils'
+require 'rbconfig'
+
+EXTENSION_NAME = "ruby_prof.#{Config::CONFIG["DLEXT"]}"
+
+# This is called when the Windows GEM is installed!
+task :install do
+  # Gems will pass these two environment variables:
+  # RUBYARCHDIR=#{dest_path}
+  # RUBYLIBDIR=#{dest_path}
+  
+  dest_path = ENV['RUBYLIBDIR']
+
+  # Copy the extension
+  cp(EXTENSION_NAME, dest_path)
+end
+
+task :default => :install
diff --git a/ext/mingw/build.rake b/ext/mingw/build.rake
new file mode 100644
index 0000000..edd66a2
--- /dev/null
+++ b/ext/mingw/build.rake
@@ -0,0 +1,38 @@
+# We can't use Ruby's standard build procedures
+# on Windows because the Ruby executable is
+# built with VC++ while here we want to build
+# with MingW.  So just roll our own...
+
+require 'rake/clean'
+require 'rbconfig'
+
+RUBY_INCLUDE_DIR = Config::CONFIG["archdir"]
+RUBY_BIN_DIR = Config::CONFIG["bindir"]
+RUBY_LIB_DIR = Config::CONFIG["libdir"]
+RUBY_SHARED_LIB = Config::CONFIG["LIBRUBY"]
+RUBY_SHARED_DLL = RUBY_SHARED_LIB.gsub(/lib$/, 'dll')
+    
+EXTENSION_NAME = "ruby_prof.#{Config::CONFIG["DLEXT"]}"
+
+CLEAN.include('*.o')
+CLOBBER.include(EXTENSION_NAME)
+
+task :default => "ruby_prof"
+
+SRC = FileList['../*.c']
+OBJ = SRC.collect do |file_name|
+  File.basename(file_name).ext('o')
+end
+
+SRC.each do |srcfile|
+  objfile = File.basename(srcfile).ext('o')
+  file objfile => srcfile do
+    command = "gcc -c -fPIC -O2 -Wall -o #{objfile} -I/usr/local/include #{srcfile} -I#{RUBY_INCLUDE_DIR}"
+    sh "sh -c '#{command}'" 
+  end
+end
+
+file "ruby_prof" => OBJ do
+  command = "gcc -shared -o #{EXTENSION_NAME} -L/usr/local/lib #{OBJ} #{RUBY_BIN_DIR}/#{RUBY_SHARED_DLL}" 
+  sh "sh -c '#{command}'" 
+end
\ No newline at end of file
diff --git a/ext/mingw/ruby_prof.so b/ext/mingw/ruby_prof.so
new file mode 100755
index 0000000..70adf02
Binary files /dev/null and b/ext/mingw/ruby_prof.so differ
diff --git a/ext/ruby_prof.c b/ext/ruby_prof.c
new file mode 100644
index 0000000..34ee12a
--- /dev/null
+++ b/ext/ruby_prof.c
@@ -0,0 +1,1680 @@
+/*
+ * Copyright (C) 2008  Shugo Maeda <shugo at ruby-lang.org>
+ *                     Charlie Savage <cfis at savagexi.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* ruby-prof tracks the time spent executing every method in ruby programming.
+   The main players are:
+
+     prof_result_t     - Its one field, values,  contains the overall results
+     thread_data_t     - Stores data about a single thread.  
+     prof_stack_t      - The method call stack in a particular thread
+     prof_method_t     - Profiling information for each method
+     prof_call_info_t  - Keeps track a method's callers and callees. 
+
+  The final resulut is a hash table of thread_data_t, keyed on the thread
+  id.  Each thread has an hash a table of prof_method_t, keyed on the
+  method id.  A hash table is used for quick look up when doing a profile.
+  However, it is exposed to Ruby as an array.
+  
+  Each prof_method_t has two hash tables, parent and children, of prof_call_info_t.
+  These objects keep track of a method's callers (who called the method) and its
+  callees (who the method called).  These are keyed the method id, but once again,
+  are exposed to Ruby as arrays.  Each prof_call_into_t maintains a pointer to the
+  caller or callee method, thereby making it easy to navigate through the call 
+  hierarchy in ruby - which is very helpful for creating call graphs.      
+*/
+
+#include "ruby_prof.h"
+
+
+/* ================  Helper Functions  =================*/
+static VALUE
+figure_singleton_name(VALUE klass)
+{
+    VALUE result = Qnil;
+
+    /* We have come across a singleton object. First
+       figure out what it is attached to.*/
+    VALUE attached = rb_iv_get(klass, "__attached__");
+
+    /* Is this a singleton class acting as a metaclass? */
+    if (BUILTIN_TYPE(attached) == T_CLASS)
+    {
+        result = rb_str_new2("<Class::");
+        rb_str_append(result, rb_inspect(attached));
+        rb_str_cat2(result, ">");
+    }
+
+    /* Is this for singleton methods on a module? */
+    else if (BUILTIN_TYPE(attached) == T_MODULE)
+    {
+        result = rb_str_new2("<Module::");
+        rb_str_append(result, rb_inspect(attached));
+        rb_str_cat2(result, ">");
+    }
+
+    /* Is this for singleton methods on an object? */
+    else if (BUILTIN_TYPE(attached) == T_OBJECT)
+    {
+        /* Make sure to get the super class so that we don't
+           mistakenly grab a T_ICLASS which would lead to
+           unknown method errors. */
+#ifdef RCLASS_SUPER
+        VALUE super = rb_class_real(RCLASS_SUPER(klass));
+#else
+        VALUE super = rb_class_real(RCLASS(klass)->super);
+#endif
+        result = rb_str_new2("<Object::");
+        rb_str_append(result, rb_inspect(super));
+        rb_str_cat2(result, ">");
+    }
+    
+    /* Ok, this could be other things like an array made put onto
+       a singleton object (yeah, it happens, see the singleton
+       objects test case). */
+    else
+    {
+        result = rb_inspect(klass);
+    }
+
+    return result;
+}
+
+static VALUE
+klass_name(VALUE klass)
+{
+    VALUE result = Qnil;
+    
+    if (klass == 0 || klass == Qnil)
+    {
+        result = rb_str_new2("Global");
+    }
+    else if (BUILTIN_TYPE(klass) == T_MODULE)
+    {
+        result = rb_inspect(klass);
+    }
+    else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
+    {
+        result = figure_singleton_name(klass);
+    }
+    else if (BUILTIN_TYPE(klass) == T_CLASS)
+    {
+        result = rb_inspect(klass);
+    }
+    else
+    {
+        /* Should never happen. */
+        result = rb_str_new2("Unknown");
+    }
+
+    return result;
+}
+
+static VALUE
+method_name(ID mid, int depth)
+{
+    VALUE result;
+
+    if (mid == ID_ALLOCATOR) 
+        result = rb_str_new2("allocate");
+    else if (mid == 0)
+        result = rb_str_new2("[No method]");
+    else
+        result = rb_String(ID2SYM(mid));
+    
+    if (depth > 0)
+    {
+      char buffer[65];
+      sprintf(buffer, "%i", depth);
+      rb_str_cat2(result, "-");
+      rb_str_cat2(result, buffer);
+    }
+
+    return result;
+}
+
+static VALUE
+full_name(VALUE klass, ID mid, int depth)
+{
+  VALUE result = klass_name(klass);
+  rb_str_cat2(result, "#");
+  rb_str_append(result, method_name(mid, depth));
+  
+  return result;
+}
+
+/* ================  Stack Handling   =================*/
+/* Creates a stack of prof_frame_t to keep track
+   of timings for active methods. */
+static prof_stack_t *
+stack_create()
+{
+    prof_stack_t *stack = ALLOC(prof_stack_t);
+    stack->start = ALLOC_N(prof_frame_t, INITIAL_STACK_SIZE);
+    stack->ptr = stack->start;
+    stack->end = stack->start + INITIAL_STACK_SIZE;
+    return stack;
+}
+
+static void
+stack_free(prof_stack_t *stack)
+{
+    xfree(stack->start);
+    xfree(stack);
+}
+
+static prof_frame_t *
+stack_push(prof_stack_t *stack)
+{
+  /* Is there space on the stack?  If not, double
+     its size. */
+  if (stack->ptr == stack->end)
+  {
+    size_t len = stack->ptr - stack->start;
+    size_t new_capacity = (stack->end - stack->start) * 2;
+    REALLOC_N(stack->start, prof_frame_t, new_capacity);
+    stack->ptr = stack->start + len;
+    stack->end = stack->start + new_capacity;
+  }
+  return stack->ptr++;
+}
+
+static prof_frame_t *
+stack_pop(prof_stack_t *stack)
+{
+    if (stack->ptr == stack->start)
+      return NULL;
+    else
+      return --stack->ptr;
+}
+
+static prof_frame_t *
+stack_peek(prof_stack_t *stack)
+{
+    if (stack->ptr == stack->start)
+      return NULL;
+    else
+      return stack->ptr - 1;
+}
+
+/* ================  Method Key   =================*/
+static int 
+method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2) 
+{
+    return (key1->klass != key2->klass) || 
+           (key1->mid != key2->mid) || 
+           (key1->depth != key2->depth);
+}
+
+static int 
+method_table_hash(prof_method_key_t *key) 
+{
+   return key->key;
+}
+
+static struct st_hash_type type_method_hash = {
+    method_table_cmp,
+    method_table_hash
+};
+
+static void
+method_key(prof_method_key_t* key, VALUE klass, ID mid, int depth)
+{
+    key->klass = klass;
+    key->mid = mid;
+    key->depth = depth;
+    key->key = (klass << 4) + (mid << 2) + depth;
+}
+
+
+/* ================  Call Info   =================*/
+static st_table *
+call_info_table_create()
+{
+  return st_init_table(&type_method_hash);
+}
+
+static size_t
+call_info_table_insert(st_table *table, const prof_method_key_t *key, prof_call_info_t *val)
+{
+  return st_insert(table, (st_data_t) key, (st_data_t) val);
+}
+
+static prof_call_info_t *
+call_info_table_lookup(st_table *table, const prof_method_key_t *key)
+{
+    st_data_t val;
+    if (st_lookup(table, (st_data_t) key, &val))
+    {
+      return (prof_call_info_t *) val;
+    }
+    else
+    {
+      return NULL;
+    }
+}
+
+static void
+call_info_table_free(st_table *table)
+{
+    st_free_table(table);
+}
+
+/* Document-class: RubyProf::CallInfo
+RubyProf::CallInfo is a helper class used by RubyProf::MethodInfo
+to keep track of which child methods were called and how long
+they took to execute. */
+
+/* :nodoc: */
+static prof_call_info_t *
+prof_call_info_create(prof_method_t* method, prof_call_info_t* parent)
+{
+    prof_call_info_t *result = ALLOC(prof_call_info_t);
+    result->object = Qnil;
+    result->target = method;
+    result->parent = parent;
+    result->call_infos = call_info_table_create();
+    result->children = Qnil;
+
+    result->called = 0;
+    result->total_time = 0;
+    result->self_time = 0;
+    result->wait_time = 0;
+    result->line = 0;
+    return result;
+}
+
+static void
+prof_call_info_mark(prof_call_info_t *call_info)
+{
+  rb_gc_mark(prof_method_wrap(call_info->target));
+  rb_gc_mark(call_info->children);
+  if (call_info->parent)
+    rb_gc_mark(prof_call_info_wrap(call_info->parent));
+}
+
+static void
+prof_call_info_free(prof_call_info_t *call_info)
+{
+  call_info_table_free(call_info->call_infos); 
+  xfree(call_info);
+}
+
+static VALUE
+prof_call_info_wrap(prof_call_info_t *call_info)
+{
+  if (call_info->object == Qnil)
+  {
+    call_info->object = Data_Wrap_Struct(cCallInfo, prof_call_info_mark, prof_call_info_free, call_info);
+  }
+  return call_info->object;
+}
+
+static prof_call_info_t *
+prof_get_call_info_result(VALUE obj)
+{
+    if (BUILTIN_TYPE(obj) != T_DATA)
+    {
+        /* Should never happen */
+      rb_raise(rb_eTypeError, "Not a call info object");
+    }
+    return (prof_call_info_t *) DATA_PTR(obj);
+}
+
+
+/* call-seq:
+   called -> MethodInfo
+
+Returns the target method. */
+static VALUE
+prof_call_info_target(VALUE self)
+{
+    /* Target is a pointer to a method_info - so we have to be careful
+       about the GC.  We will wrap the method_info but provide no
+       free method so the underlying object is not freed twice! */
+    
+    prof_call_info_t *result = prof_get_call_info_result(self);
+    return prof_method_wrap(result->target);
+}
+
+/* call-seq:
+   called -> int
+
+Returns the total amount of time this method was called. */
+static VALUE
+prof_call_info_called(VALUE self)
+{
+    prof_call_info_t *result = prof_get_call_info_result(self);
+    return INT2NUM(result->called);
+}
+
+/* call-seq:
+   line_no -> int
+
+   returns the line number of the method */
+static VALUE
+prof_call_info_line(VALUE self)
+{
+  prof_call_info_t *result = prof_get_call_info_result(self);
+  return rb_int_new(result->line);
+}
+
+/* call-seq:
+   total_time -> float
+
+Returns the total amount of time spent in this method and its children. */
+static VALUE
+prof_call_info_total_time(VALUE self)
+{
+    prof_call_info_t *result = prof_get_call_info_result(self);
+    return rb_float_new(convert_measurement(result->total_time));
+}
+
+/* call-seq:
+   self_time -> float
+
+Returns the total amount of time spent in this method. */
+static VALUE
+prof_call_info_self_time(VALUE self)
+{
+    prof_call_info_t *result = prof_get_call_info_result(self);
+
+    return rb_float_new(convert_measurement(result->self_time));
+}
+
+/* call-seq:
+   wait_time -> float
+
+Returns the total amount of time this method waited for other threads. */
+static VALUE
+prof_call_info_wait_time(VALUE self)
+{
+    prof_call_info_t *result = prof_get_call_info_result(self);
+
+    return rb_float_new(convert_measurement(result->wait_time));
+}
+
+/* call-seq:
+   parent -> call_info
+
+Returns the call_infos parent call_info object (the method that called this method).*/
+static VALUE
+prof_call_info_parent(VALUE self)
+{
+    prof_call_info_t *result = prof_get_call_info_result(self);
+    if (result->parent)
+      return prof_call_info_wrap(result->parent);
+    else
+      return Qnil;
+}
+
+static int
+prof_call_info_collect_children(st_data_t key, st_data_t value, st_data_t result)
+{
+    prof_call_info_t *call_info = (prof_call_info_t *) value;
+    VALUE arr = (VALUE) result;
+    rb_ary_push(arr, prof_call_info_wrap(call_info));
+    return ST_CONTINUE;
+}
+
+/* call-seq:
+   children -> hash
+
+Returns an array of call info objects of methods that this method 
+called (ie, children).*/
+static VALUE
+prof_call_info_children(VALUE self)
+{
+    prof_call_info_t *call_info = prof_get_call_info_result(self);
+    if (call_info->children == Qnil)
+    {
+      call_info->children = rb_ary_new();
+      st_foreach(call_info->call_infos, prof_call_info_collect_children, call_info->children);
+    }
+    return call_info->children;
+}
+
+/* ================  Call Infos   =================*/
+static prof_call_infos_t*
+prof_call_infos_create()
+{
+   prof_call_infos_t *result = ALLOC(prof_call_infos_t);
+   result->start = ALLOC_N(prof_call_info_t*, INITIAL_CALL_INFOS_SIZE);
+   result->end = result->start + INITIAL_CALL_INFOS_SIZE;
+   result->ptr = result->start;
+   result->object = Qnil;
+   return result;
+}
+
+static void
+prof_call_infos_free(prof_call_infos_t *call_infos)
+{
+  xfree(call_infos->start);
+  xfree(call_infos);
+}
+
+static void
+prof_add_call_info(prof_call_infos_t *call_infos, prof_call_info_t *call_info)
+{
+  if (call_infos->ptr == call_infos->end)
+  {
+    size_t len = call_infos->ptr - call_infos->start;
+    size_t new_capacity = (call_infos->end - call_infos->start) * 2;
+    REALLOC_N(call_infos->start, prof_call_info_t*, new_capacity);
+    call_infos->ptr = call_infos->start + len;
+    call_infos->end = call_infos->start + new_capacity;
+  }
+  *call_infos->ptr = call_info;
+  call_infos->ptr++;
+}
+
+static VALUE
+prof_call_infos_wrap(prof_call_infos_t *call_infos)
+{
+  if (call_infos->object == Qnil)
+  {
+    prof_call_info_t **i;
+    call_infos->object = rb_ary_new();
+    for(i=call_infos->start; i<call_infos->ptr; i++)
+    {
+      VALUE call_info = prof_call_info_wrap(*i);
+      rb_ary_push(call_infos->object, call_info);
+    }
+  }
+  return call_infos->object;
+}
+
+
+/* ================  Method Info   =================*/
+/* Document-class: RubyProf::MethodInfo
+The RubyProf::MethodInfo class stores profiling data for a method.
+One instance of the RubyProf::MethodInfo class is created per method
+called per thread.  Thus, if a method is called in two different
+thread then there will be two RubyProf::MethodInfo objects
+created.  RubyProf::MethodInfo objects can be accessed via
+the RubyProf::Result object.
+*/
+
+static prof_method_t*
+prof_method_create(prof_method_key_t *key, const char* source_file, int line)
+{
+    prof_method_t *result = ALLOC(prof_method_t);
+    result->object = Qnil;
+    result->key = ALLOC(prof_method_key_t);
+    method_key(result->key, key->klass, key->mid, key->depth);   
+
+    result->call_infos = prof_call_infos_create();
+
+    result->active = 0;
+
+    if (source_file != NULL) 
+    {
+      int len = strlen(source_file) + 1;    
+      char *buffer = ALLOC_N(char, len);
+
+      MEMCPY(buffer, source_file, char, len);
+      result->source_file = buffer;
+    }
+    else 
+    {    
+      result->source_file = source_file;
+    }      
+    result->line = line;
+
+    return result;
+}
+
+static void
+prof_method_mark(prof_method_t *method)
+{
+  rb_gc_mark(method->call_infos->object);
+  rb_gc_mark(method->key->klass);
+}
+
+static void
+prof_method_free(prof_method_t *method)
+{
+  if (method->source_file)
+  {
+    xfree((char*)method->source_file);    
+  }
+
+  prof_call_infos_free(method->call_infos);
+  xfree(method->key);
+  xfree(method);
+}
+
+static VALUE
+prof_method_wrap(prof_method_t *result)
+{
+  if (result->object == Qnil)
+  {
+    result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_free, result);
+  }
+  return result->object;
+}
+
+static prof_method_t *
+get_prof_method(VALUE obj)
+{
+    return (prof_method_t *) DATA_PTR(obj);
+}
+
+/* call-seq:
+   line_no -> int
+
+   returns the line number of the method */
+static VALUE
+prof_method_line(VALUE self)
+{
+    return rb_int_new(get_prof_method(self)->line);
+}
+
+/* call-seq:
+   source_file => string
+
+return the source file of the method 
+*/
+static VALUE prof_method_source_file(VALUE self)
+{
+    const char* sf = get_prof_method(self)->source_file;
+    if(!sf)
+    {
+      return rb_str_new2("ruby_runtime");
+    }
+    else
+    {
+      return rb_str_new2(sf);
+    }
+}
+
+
+/* call-seq:
+   method_class -> klass
+
+Returns the Ruby klass that owns this method. */
+static VALUE
+prof_method_klass(VALUE self)
+{
+    prof_method_t *result = get_prof_method(self);
+    return result->key->klass;
+}
+
+/* call-seq:
+   method_id -> ID
+
+Returns the id of this method. */
+static VALUE
+prof_method_id(VALUE self)
+{
+    prof_method_t *result = get_prof_method(self);
+    return ID2SYM(result->key->mid);
+}
+
+/* call-seq:
+   klass_name -> string
+
+Returns the name of this method's class.  Singleton classes
+will have the form <Object::Object>. */
+
+static VALUE
+prof_klass_name(VALUE self)
+{
+    prof_method_t *method = get_prof_method(self);
+    return klass_name(method->key->klass);
+}
+
+/* call-seq:
+   method_name -> string
+
+Returns the name of this method in the format Object#method.  Singletons
+methods will be returned in the format <Object::Object>#method.*/
+
+static VALUE
+prof_method_name(VALUE self, int depth)
+{
+    prof_method_t *method = get_prof_method(self);
+    return method_name(method->key->mid, depth);
+}
+
+/* call-seq:
+   full_name -> string
+
+Returns the full name of this method in the format Object#method.*/
+
+static VALUE
+prof_full_name(VALUE self)
+{
+    prof_method_t *method = get_prof_method(self);
+    return full_name(method->key->klass, method->key->mid, method->key->depth);
+}
+
+/* call-seq:
+   call_infos -> Array of call_info
+
+Returns an array of call info objects that contain profiling information 
+about the current method.*/
+static VALUE
+prof_method_call_infos(VALUE self)
+{
+    prof_method_t *method = get_prof_method(self);
+    return prof_call_infos_wrap(method->call_infos);
+}
+
+static int
+collect_methods(st_data_t key, st_data_t value, st_data_t result)
+{
+    /* Called for each method stored in a thread's method table. 
+       We want to store the method info information into an array.*/
+    VALUE methods = (VALUE) result;
+    prof_method_t *method = (prof_method_t *) value;
+    rb_ary_push(methods, prof_method_wrap(method));
+
+    /* Wrap call info objects */
+    prof_call_infos_wrap(method->call_infos);
+
+    return ST_CONTINUE;
+}
+
+/* ================  Method Table   =================*/
+static st_table *
+method_table_create()
+{
+  return st_init_table(&type_method_hash);
+}
+
+static size_t
+method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
+{
+  return st_insert(table, (st_data_t) key, (st_data_t) val);
+}
+
+static prof_method_t *
+method_table_lookup(st_table *table, const prof_method_key_t* key)
+{
+    st_data_t val;
+    if (st_lookup(table, (st_data_t)key, &val))
+    {
+      return (prof_method_t *) val;
+    }
+    else 
+    {
+      return NULL;
+    }
+}
+
+
+static void
+method_table_free(st_table *table)
+{
+    /* Don't free the contents since they are wrapped by
+       Ruby objects! */
+    st_free_table(table);
+}
+
+
+/* ================  Thread Handling   =================*/
+
+/* ---- Keeps track of thread's stack and methods ---- */
+static thread_data_t*
+thread_data_create()
+{
+    thread_data_t* result = ALLOC(thread_data_t);
+    result->stack = stack_create();
+    result->method_table = method_table_create();
+    result->last_switch = get_measurement();
+    return result;
+}
+
+static void
+thread_data_free(thread_data_t* thread_data)
+{
+    method_table_free(thread_data->method_table);
+    stack_free(thread_data->stack);
+    xfree(thread_data);
+}
+
+/* ---- Hash, keyed on thread, that stores thread's stack
+        and methods---- */
+
+static st_table *
+threads_table_create()
+{
+    return st_init_numtable();
+}
+
+static size_t
+threads_table_insert(st_table *table, VALUE thread, thread_data_t *thread_data)
+{
+    /* Its too slow to key on the real thread id so just typecast thread instead. */
+    return st_insert(table, (st_data_t) thread, (st_data_t) thread_data);
+}
+
+static thread_data_t *
+threads_table_lookup(st_table *table, VALUE thread_id)
+{
+    thread_data_t* result;
+    st_data_t val;
+
+    /* Its too slow to key on the real thread id so just typecast thread instead. */
+    if (st_lookup(table, (st_data_t) thread_id, &val))
+    {
+      result = (thread_data_t *) val;
+    }
+    else
+    {
+        result = thread_data_create();
+        result->thread_id = thread_id;
+
+        /* Insert the table */
+        threads_table_insert(threads_tbl, thread_id, result);
+    }
+    return result;
+}
+
+static int
+free_thread_data(st_data_t key, st_data_t value, st_data_t dummy)
+{
+    thread_data_free((thread_data_t*)value);
+    return ST_CONTINUE;
+}
+
+
+static void
+threads_table_free(st_table *table)
+{
+    st_foreach(table, free_thread_data, 0);
+    st_free_table(table);
+}
+
+
+static int
+collect_threads(st_data_t key, st_data_t value, st_data_t result)
+{
+    /* Although threads are keyed on an id, that is actually a 
+       pointer to the VALUE object of the thread.  So its bogus.
+       However, in thread_data is the real thread id stored
+       as an int. */
+    thread_data_t* thread_data = (thread_data_t*) value;
+    VALUE threads_hash = (VALUE) result;
+
+    VALUE methods = rb_ary_new();
+
+    /* Now collect an array of all the called methods */
+    st_table* method_table = thread_data->method_table;
+    st_foreach(method_table, collect_methods, methods);
+ 
+    /* Store the results in the threads hash keyed on the thread id. */
+    rb_hash_aset(threads_hash, thread_data->thread_id, methods);
+
+    return ST_CONTINUE;
+}
+
+
+/* ================  Profiling    =================*/
+/* Copied from eval.c */
+#ifdef DEBUG
+static char *
+get_event_name(rb_event_flag_t event)
+{
+  switch (event) {
+    case RUBY_EVENT_LINE:
+  return "line";
+    case RUBY_EVENT_CLASS:
+  return "class";
+    case RUBY_EVENT_END:
+  return "end";
+    case RUBY_EVENT_CALL:
+  return "call";
+    case RUBY_EVENT_RETURN:
+  return "return";
+    case RUBY_EVENT_C_CALL:
+  return "c-call";
+    case RUBY_EVENT_C_RETURN:
+  return "c-return";
+    case RUBY_EVENT_RAISE:
+  return "raise";
+    default:
+  return "unknown";
+  }
+}
+#endif
+
+static prof_method_t* 
+get_method(rb_event_flag_t event, NODE *node, VALUE klass, ID mid, int depth, st_table* method_table)
+{
+    prof_method_key_t key;
+    prof_method_t *method = NULL;
+    
+    method_key(&key, klass, mid, depth);
+    method = method_table_lookup(method_table, &key);
+
+    if (!method)
+    {
+      const char* source_file = rb_sourcefile();
+      int line = rb_sourceline();
+      
+      /* Line numbers are not accurate for c method calls */
+      if (event == RUBY_EVENT_C_CALL)
+      {
+        line = 0;
+        source_file = NULL;
+      }
+        
+      method = prof_method_create(&key, source_file, line);
+      method_table_insert(method_table, method->key, method);
+    }
+    return method;
+}
+
+static void
+update_result(prof_measure_t total_time,
+              prof_frame_t *parent_frame, 
+              prof_frame_t *frame)
+{
+    prof_measure_t self_time = total_time - frame->child_time - frame->wait_time;
+
+    prof_call_info_t *call_info = frame->call_info;
+    
+    /* Update information about the current method */
+    call_info->called++;
+    call_info->total_time += total_time;
+    call_info->self_time += self_time;
+    call_info->wait_time += frame->wait_time;
+
+    /* Note where the current method was called from */
+    if (parent_frame)
+      call_info->line = parent_frame->line;
+}
+
+static thread_data_t *
+switch_thread(VALUE thread_id, prof_measure_t now)
+{
+    prof_frame_t *frame = NULL;
+    prof_measure_t wait_time = 0;
+    
+    /* Get new thread information. */
+    thread_data_t *thread_data = threads_table_lookup(threads_tbl, thread_id);
+
+    /* How long has this thread been waiting? */
+    wait_time = now - thread_data->last_switch;
+    thread_data->last_switch = 0;
+
+    /* Get the frame at the top of the stack.  This may represent
+       the current method (EVENT_LINE, EVENT_RETURN)  or the
+       previous method (EVENT_CALL).*/
+    frame = stack_peek(thread_data->stack);
+  
+    if (frame)
+      frame->wait_time += wait_time;
+      
+    /* Save on the last thread the time of the context switch
+       and reset this thread's last context switch to 0.*/
+    if (last_thread_data)
+      last_thread_data->last_switch = now;
+      
+    last_thread_data = thread_data;
+    return thread_data;
+}
+
+static prof_frame_t*
+pop_frame(thread_data_t *thread_data, prof_measure_t now)
+{
+  prof_frame_t *frame = NULL;
+  prof_frame_t* parent_frame = NULL;
+  prof_measure_t total_time;
+
+  frame = stack_pop(thread_data->stack);
+    
+  /* Frame can be null.  This can happen if RubProf.start is called from
+     a method that exits.  And it can happen if an exception is raised
+     in code that is being profiled and the stack unwinds (RubProf is
+     not notified of that by the ruby runtime. */
+  if (frame == NULL) return NULL;
+
+  /* Calculate the total time this method took */
+  total_time = now - frame->start_time;
+
+  /* Now deactivate the method */
+  frame->call_info->target->active = 0;
+
+  parent_frame = stack_peek(thread_data->stack);
+  if (parent_frame)
+  {
+    parent_frame->child_time += total_time;
+  }
+    
+  update_result(total_time, parent_frame, frame);
+  return frame;
+}
+
+static int 
+pop_frames(st_data_t key, st_data_t value, st_data_t now_arg)
+{
+    VALUE thread_id = (VALUE)key;
+    thread_data_t* thread_data = (thread_data_t *) value;
+    prof_measure_t now = *(prof_measure_t *) now_arg;
+
+    if (!last_thread_data || last_thread_data->thread_id != thread_id)
+      thread_data = switch_thread(thread_id, now);
+    else
+      thread_data = last_thread_data;
+
+    while (pop_frame(thread_data, now))
+    {
+    }
+    
+    return ST_CONTINUE;
+}
+
+static void 
+prof_pop_threads()
+{
+    /* Get current measurement*/
+    prof_measure_t now = get_measurement();
+    st_foreach(threads_tbl, pop_frames, (st_data_t) &now);
+}
+
+
+#ifdef RUBY_VM
+static void
+prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
+#else
+static void
+prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE klass)
+#endif
+{
+    
+    VALUE thread = Qnil;
+    VALUE thread_id = Qnil;
+    prof_measure_t now = 0;
+    thread_data_t* thread_data = NULL;
+    prof_frame_t *frame = NULL;
+
+
+#ifdef RUBY_VM
+
+    if (event != RUBY_EVENT_C_CALL && event != RUBY_EVENT_C_RETURN) {
+        rb_frame_method_id_and_class(&mid, &klass);
+    }
+#endif
+
+#ifdef DEBUG
+    /*  This code is here for debug purposes - uncomment it out
+        when debugging to see a print out of exactly what the
+        profiler is tracing. */
+    {
+        char* key = 0;
+        static VALUE last_thread_id = Qnil;
+
+        VALUE thread = rb_thread_current();
+        VALUE thread_id = rb_obj_id(thread);
+        char* class_name = NULL;
+        char* method_name = rb_id2name(mid);
+        char* source_file = rb_sourcefile();
+        unsigned int source_line = rb_sourceline();
+
+        char* event_name = get_event_name(event);
+
+        if (klass != 0)
+          klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
+
+        class_name = rb_class2name(klass);
+
+        if (last_thread_id != thread_id)
+          printf("\n");
+
+        printf("%2u: %-8s :%2d  %s#%s\n",
+               thread_id, event_name, source_line, class_name, method_name);
+        fflush(stdout);
+        last_thread_id = thread_id;               
+    }
+#endif
+    
+    /* Special case - skip any methods from the mProf 
+       module, such as Prof.stop, since they clutter
+       the results but aren't important to them results. */
+    if (self == mProf) return;
+
+    /* Get current measurement*/
+    now = get_measurement();
+    
+    /* Get the current thread information. */
+    thread = rb_thread_current();
+    thread_id = rb_obj_id(thread);
+
+    if (exclude_threads_tbl &&
+        st_lookup(exclude_threads_tbl, (st_data_t) thread_id, 0)) 
+    {
+      return;
+    }    
+    
+    /* Was there a context switch? */
+    if (!last_thread_data || last_thread_data->thread_id != thread_id)
+      thread_data = switch_thread(thread_id, now);
+    else
+      thread_data = last_thread_data;
+    
+    /* Get the current frame for the current thread. */
+    frame = stack_peek(thread_data->stack);
+
+    switch (event) {
+    case RUBY_EVENT_LINE:
+    {
+      /* Keep track of the current line number in this method.  When
+         a new method is called, we know what line number it was 
+         called from. */
+      if (frame)
+      {
+        frame->line = rb_sourceline();
+        break;
+      }
+
+      /* If we get here there was no frame, which means this is 
+         the first method seen for this thread, so fall through
+         to below to create it. */
+    }
+    case RUBY_EVENT_CALL:
+    case RUBY_EVENT_C_CALL:
+    {
+        prof_call_info_t *call_info = NULL;
+        prof_method_t *method = NULL;
+
+        /* Is this an include for a module?  If so get the actual
+           module class since we want to combine all profiling
+           results for that module. */
+        
+        if (klass != 0)
+          klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
+          
+        /* Assume this is the first time we have called this method. */
+        method = get_method(event, node, klass, mid, 0, thread_data->method_table);
+
+        /* Check for a recursive call */
+        if (method->active)
+        {
+          /* Yes, this method is already active */
+          method = get_method(event, node, klass, mid, method->key->depth + 1, thread_data->method_table);
+        }
+        else
+        {
+          /* No, so make it active */
+          method->active = 1;
+        }
+
+        if (!frame)
+        {
+          call_info = prof_call_info_create(method, NULL);
+          prof_add_call_info(method->call_infos, call_info);
+        }
+        else
+        {
+          call_info = call_info_table_lookup(frame->call_info->call_infos, method->key);
+
+          if (!call_info)
+          {
+            call_info = prof_call_info_create(method, frame->call_info);
+            call_info_table_insert(frame->call_info->call_infos, method->key, call_info);
+            prof_add_call_info(method->call_infos, call_info);
+          }
+        }
+
+        /* Push a new frame onto the stack */
+        frame = stack_push(thread_data->stack);
+        frame->call_info = call_info;
+        frame->start_time = now;
+        frame->wait_time = 0;
+        frame->child_time = 0;
+        frame->line = rb_sourceline();
+
+        break;
+    }
+    case RUBY_EVENT_RETURN:
+    case RUBY_EVENT_C_RETURN:
+    {
+        pop_frame(thread_data, now);
+        break;
+      }
+    }
+}
+
+
+/* ========  ProfResult ============== */
+
+/* Document-class: RubyProf::Result
+The RubyProf::Result class is used to store the results of a 
+profiling run.  And instace of the class is returned from
+the methods RubyProf#stop and RubyProf#profile.
+
+RubyProf::Result has one field, called threads, which is a hash
+table keyed on thread ID.  For each thread id, the hash table
+stores another hash table that contains profiling information
+for each method called during the threads execution.  That
+hash table is keyed on method name and contains
+RubyProf::MethodInfo objects. */
+
+
+static void
+prof_result_mark(prof_result_t *prof_result)
+{
+    VALUE threads = prof_result->threads;
+    rb_gc_mark(threads);
+}
+
+static void
+prof_result_free(prof_result_t *prof_result)
+{
+    prof_result->threads = Qnil;
+    xfree(prof_result);
+}
+
+static VALUE
+prof_result_new()
+{
+    prof_result_t *prof_result = ALLOC(prof_result_t);
+
+    /* Wrap threads in Ruby regular Ruby hash table. */
+    prof_result->threads = rb_hash_new();
+    st_foreach(threads_tbl, collect_threads, prof_result->threads);
+
+    return Data_Wrap_Struct(cResult, prof_result_mark, prof_result_free, prof_result);
+}
+
+
+static prof_result_t *
+get_prof_result(VALUE obj)
+{
+    if (BUILTIN_TYPE(obj) != T_DATA ||
+      RDATA(obj)->dfree != (RUBY_DATA_FUNC) prof_result_free)
+    {
+        /* Should never happen */
+      rb_raise(rb_eTypeError, "wrong result object");
+    }
+    return (prof_result_t *) DATA_PTR(obj);
+}
+
+/* call-seq:
+   threads -> Hash
+
+Returns a hash table keyed on thread ID.  For each thread id,
+the hash table stores another hash table that contains profiling
+information for each method called during the threads execution.
+That hash table is keyed on method name and contains 
+RubyProf::MethodInfo objects. */
+static VALUE
+prof_result_threads(VALUE self)
+{
+    prof_result_t *prof_result = get_prof_result(self);
+    return prof_result->threads;
+}
+
+
+
+/* call-seq:
+   measure_mode -> measure_mode
+   
+   Returns what ruby-prof is measuring.  Valid values include:
+   
+   *RubyProf::PROCESS_TIME - Measure process time.  This is default.  It is implemented using the clock functions in the C Runtime library.
+   *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
+   *RubyProf::CPU_TIME - Measure time using the CPU clock counter.  This mode is only supported on Pentium or PowerPC platforms. 
+   *RubyProf::ALLOCATIONS - Measure object allocations.  This requires a patched Ruby interpreter.
+   *RubyProf::MEMORY - Measure memory size.  This requires a patched Ruby interpreter.
+   *RubyProf::GC_RUNS - Measure number of garbage collections.  This requires a patched Ruby interpreter.
+   *RubyProf::GC_TIME - Measure time spent doing garbage collection.  This requires a patched Ruby interpreter.*/
+static VALUE
+prof_get_measure_mode(VALUE self)
+{
+    return INT2NUM(measure_mode);
+}
+
+/* call-seq:
+   measure_mode=value -> void
+   
+   Specifies what ruby-prof should measure.  Valid values include:
+   
+   *RubyProf::PROCESS_TIME - Measure process time.  This is default.  It is implemented using the clock functions in the C Runtime library.
+   *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
+   *RubyProf::CPU_TIME - Measure time using the CPU clock counter.  This mode is only supported on Pentium or PowerPC platforms. 
+   *RubyProf::ALLOCATIONS - Measure object allocations.  This requires a patched Ruby interpreter.
+   *RubyProf::MEMORY - Measure memory size.  This requires a patched Ruby interpreter.
+   *RubyProf::GC_RUNS - Measure number of garbage collections.  This requires a patched Ruby interpreter.
+   *RubyProf::GC_TIME - Measure time spent doing garbage collection.  This requires a patched Ruby interpreter.*/
+static VALUE
+prof_set_measure_mode(VALUE self, VALUE val)
+{
+    long mode = NUM2LONG(val);
+
+    if (threads_tbl)
+    {
+      rb_raise(rb_eRuntimeError, "can't set measure_mode while profiling");
+    }
+
+    switch (mode) {
+      case MEASURE_PROCESS_TIME:
+        get_measurement = measure_process_time;
+        convert_measurement = convert_process_time;
+        break;
+        
+      case MEASURE_WALL_TIME:
+        get_measurement = measure_wall_time;
+        convert_measurement = convert_wall_time;
+        break;
+        
+      #if defined(MEASURE_CPU_TIME)
+      case MEASURE_CPU_TIME:
+        if (cpu_frequency == 0)
+            cpu_frequency = get_cpu_frequency();
+        get_measurement = measure_cpu_time;
+        convert_measurement = convert_cpu_time;
+        break;
+      #endif
+              
+      #if defined(MEASURE_ALLOCATIONS)
+      case MEASURE_ALLOCATIONS:
+        get_measurement = measure_allocations;
+        convert_measurement = convert_allocations;
+        break;
+      #endif
+        
+      #if defined(MEASURE_MEMORY)
+      case MEASURE_MEMORY:
+        get_measurement = measure_memory;
+        convert_measurement = convert_memory;
+        break;
+      #endif
+
+      #if defined(MEASURE_GC_RUNS)
+      case MEASURE_GC_RUNS:
+        get_measurement = measure_gc_runs;
+        convert_measurement = convert_gc_runs;
+        break;
+      #endif
+
+      #if defined(MEASURE_GC_TIME)
+      case MEASURE_GC_TIME:
+        get_measurement = measure_gc_time;
+        convert_measurement = convert_gc_time;
+        break;
+      #endif
+
+      default:
+        rb_raise(rb_eArgError, "invalid mode: %ld", mode);
+        break;
+    }
+    
+    measure_mode = mode;
+    return val;
+}
+
+/* call-seq:
+   exclude_threads= -> void
+
+   Specifies what threads ruby-prof should exclude from profiling */
+static VALUE
+prof_set_exclude_threads(VALUE self, VALUE threads)
+{
+    int i;
+
+    if (threads_tbl != NULL)
+    {
+      rb_raise(rb_eRuntimeError, "can't set exclude_threads while profiling");
+    }
+
+    /* Stay simple, first free the old hash table */
+    if (exclude_threads_tbl)
+    {
+      st_free_table(exclude_threads_tbl);
+      exclude_threads_tbl = NULL;
+    }
+
+    /* Now create a new one if the user passed in any threads */
+    if (threads != Qnil)
+    {
+      Check_Type(threads, T_ARRAY);
+      exclude_threads_tbl = st_init_numtable();
+
+      for (i=0; i < RARRAY_LEN(threads); ++i) 
+      {
+        VALUE thread = rb_ary_entry(threads, i);
+        st_insert(exclude_threads_tbl, (st_data_t) rb_obj_id(thread), 0);
+      }
+    }    
+    return threads;
+}
+
+
+/* =========  Profiling ============= */
+void
+prof_install_hook()
+{
+#ifdef RUBY_VM
+    rb_add_event_hook(prof_event_hook,
+          RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
+          RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN 
+          | RUBY_EVENT_LINE, Qnil);
+#else
+    rb_add_event_hook(prof_event_hook,
+          RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
+          RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN 
+          | RUBY_EVENT_LINE);
+#endif
+
+#if defined(TOGGLE_GC_STATS)
+    rb_gc_enable_stats();
+#endif
+}
+
+void
+prof_remove_hook()
+{
+#if defined(TOGGLE_GC_STATS)
+    rb_gc_disable_stats();
+#endif
+
+    /* Now unregister from event   */
+    rb_remove_event_hook(prof_event_hook);
+}
+
+
+
+/* call-seq:
+   running? -> boolean
+   
+   Returns whether a profile is currently running.*/
+static VALUE
+prof_running(VALUE self)
+{
+    if (threads_tbl != NULL)
+        return Qtrue;
+    else
+        return Qfalse;
+}
+
+/* call-seq:
+   start -> RubyProf
+   
+   Starts recording profile data.*/
+static VALUE
+prof_start(VALUE self)
+{
+    if (threads_tbl != NULL)
+    {
+        rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
+    }
+
+    /* Setup globals */
+    last_thread_data = NULL;
+    threads_tbl = threads_table_create();
+
+    prof_install_hook();              
+    return self;
+}    
+
+/* call-seq:
+   pause -> RubyProf
+
+   Pauses collecting profile data. */
+static VALUE
+prof_pause(VALUE self)
+{
+    if (threads_tbl == NULL)
+    {
+        rb_raise(rb_eRuntimeError, "RubyProf is not running.");
+    }
+
+    prof_remove_hook();
+    return self;
+}
+
+/* call-seq:
+   resume {block} -> RubyProf
+   
+   Resumes recording profile data.*/
+static VALUE
+prof_resume(VALUE self)
+{
+    if (threads_tbl == NULL)
+    { 
+        prof_start(self);
+    }
+    else
+    { 
+        prof_install_hook();
+    }
+    
+    if (rb_block_given_p())
+    {
+      rb_ensure(rb_yield, self, prof_pause, self);
+    }
+
+    return self;
+}
+
+/* call-seq:
+   stop -> RubyProf::Result
+
+   Stops collecting profile data and returns a RubyProf::Result object. */
+static VALUE
+prof_stop(VALUE self)
+{
+    VALUE result = Qnil;
+    
+    prof_remove_hook();
+
+    prof_pop_threads();
+
+    /* Create the result */
+    result = prof_result_new();
+
+    /* Unset the last_thread_data (very important!) 
+       and the threads table */
+    last_thread_data = NULL;
+    threads_table_free(threads_tbl);
+    threads_tbl = NULL;
+
+    return result;
+}
+
+/* call-seq:
+   profile {block} -> RubyProf::Result
+
+Profiles the specified block and returns a RubyProf::Result object. */
+static VALUE
+prof_profile(VALUE self)
+{
+    int result;
+    
+    if (!rb_block_given_p())
+    {
+        rb_raise(rb_eArgError, "A block must be provided to the profile method.");
+    }
+
+    prof_start(self);
+    rb_protect(rb_yield, self, &result);
+    return prof_stop(self);
+}
+
+/* Get arround annoying limitations in RDOC */
+
+/* Document-method: measure_process_time
+   call-seq:
+     measure_process_time -> float
+
+Returns the process time.*/
+
+/* Document-method: measure_wall_time
+   call-seq:
+     measure_wall_time -> float
+
+Returns the wall time.*/
+
+/* Document-method: measure_cpu_time
+   call-seq:
+     measure_cpu_time -> float
+
+Returns the cpu time.*/
+
+/* Document-method: get_cpu_frequency
+   call-seq:
+     cpu_frequency -> int
+
+Returns the cpu's frequency.  This value is needed when 
+RubyProf::measure_mode is set to CPU_TIME. */
+
+/* Document-method: cpu_frequency
+   call-seq:
+     cpu_frequency -> int
+
+Returns the cpu's frequency.  This value is needed when 
+RubyProf::measure_mode is set to CPU_TIME. */
+
+/* Document-method: cpu_frequency=
+   call-seq:
+     cpu_frequency = frequency
+
+Sets the cpu's frequency.  This value is needed when 
+RubyProf::measure_mode is set to CPU_TIME. */
+
+/* Document-method: measure_allocations
+   call-seq:
+     measure_allocations -> int
+
+Returns the total number of object allocations since Ruby started.*/
+
+/* Document-method: measure_memory
+   call-seq:
+     measure_memory -> int
+
+Returns total allocated memory in bytes.*/
+
+/* Document-method: measure_gc_runs
+   call-seq:
+     gc_runs -> Integer
+
+Returns the total number of garbage collections.*/
+
+/* Document-method: measure_gc_time
+   call-seq:
+     gc_time -> Integer
+
+Returns the time spent doing garbage collections in microseconds.*/
+
+
+#if defined(_WIN32)
+__declspec(dllexport) 
+#endif
+void
+
+Init_ruby_prof()
+{
+    mProf = rb_define_module("RubyProf");
+    rb_define_const(mProf, "VERSION", rb_str_new2(RUBY_PROF_VERSION));
+    rb_define_module_function(mProf, "start", prof_start, 0);
+    rb_define_module_function(mProf, "stop", prof_stop, 0);
+    rb_define_module_function(mProf, "resume", prof_resume, 0);
+    rb_define_module_function(mProf, "pause", prof_pause, 0);
+    rb_define_module_function(mProf, "running?", prof_running, 0);
+    rb_define_module_function(mProf, "profile", prof_profile, 0);
+    
+    rb_define_singleton_method(mProf, "exclude_threads=", prof_set_exclude_threads, 1);
+    rb_define_singleton_method(mProf, "measure_mode", prof_get_measure_mode, 0);
+    rb_define_singleton_method(mProf, "measure_mode=", prof_set_measure_mode, 1);
+
+    rb_define_const(mProf, "CLOCKS_PER_SEC", INT2NUM(CLOCKS_PER_SEC));
+    rb_define_const(mProf, "PROCESS_TIME", INT2NUM(MEASURE_PROCESS_TIME));
+    rb_define_singleton_method(mProf, "measure_process_time", prof_measure_process_time, 0); /* in measure_process_time.h */
+    rb_define_const(mProf, "WALL_TIME", INT2NUM(MEASURE_WALL_TIME));
+    rb_define_singleton_method(mProf, "measure_wall_time", prof_measure_wall_time, 0); /* in measure_wall_time.h */
+
+    #ifndef MEASURE_CPU_TIME
+    rb_define_const(mProf, "CPU_TIME", Qnil);
+    #else
+    rb_define_const(mProf, "CPU_TIME", INT2NUM(MEASURE_CPU_TIME));
+    rb_define_singleton_method(mProf, "measure_cpu_time", prof_measure_cpu_time, 0); /* in measure_cpu_time.h */
+    rb_define_singleton_method(mProf, "cpu_frequency", prof_get_cpu_frequency, 0); /* in measure_cpu_time.h */
+    rb_define_singleton_method(mProf, "cpu_frequency=", prof_set_cpu_frequency, 1); /* in measure_cpu_time.h */
+    #endif
+        
+    #ifndef MEASURE_ALLOCATIONS
+    rb_define_const(mProf, "ALLOCATIONS", Qnil);
+    #else
+    rb_define_const(mProf, "ALLOCATIONS", INT2NUM(MEASURE_ALLOCATIONS));
+    rb_define_singleton_method(mProf, "measure_allocations", prof_measure_allocations, 0); /* in measure_allocations.h */
+    #endif
+    
+    #ifndef MEASURE_MEMORY
+    rb_define_const(mProf, "MEMORY", Qnil);
+    #else
+    rb_define_const(mProf, "MEMORY", INT2NUM(MEASURE_MEMORY));
+    rb_define_singleton_method(mProf, "measure_memory", prof_measure_memory, 0); /* in measure_memory.h */
+    #endif
+
+    #ifndef MEASURE_GC_RUNS
+    rb_define_const(mProf, "GC_RUNS", Qnil);
+    #else
+    rb_define_const(mProf, "GC_RUNS", INT2NUM(MEASURE_GC_RUNS));
+    rb_define_singleton_method(mProf, "measure_gc_runs", prof_measure_gc_runs, 0); /* in measure_gc_runs.h */
+    #endif
+
+    #ifndef MEASURE_GC_TIME
+    rb_define_const(mProf, "GC_TIME", Qnil);
+    #else
+    rb_define_const(mProf, "GC_TIME", INT2NUM(MEASURE_GC_TIME));
+    rb_define_singleton_method(mProf, "measure_gc_time", prof_measure_gc_time, 0); /* in measure_gc_time.h */
+    #endif
+
+    cResult = rb_define_class_under(mProf, "Result", rb_cObject);
+    rb_undef_method(CLASS_OF(cMethodInfo), "new");
+    rb_define_method(cResult, "threads", prof_result_threads, 0);
+
+    /* MethodInfo */
+    cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
+    rb_undef_method(CLASS_OF(cMethodInfo), "new");
+    
+    rb_define_method(cMethodInfo, "klass", prof_method_klass, 0);
+    rb_define_method(cMethodInfo, "klass_name", prof_klass_name, 0);
+    rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
+    rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
+    rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
+    
+    rb_define_method(cMethodInfo, "source_file", prof_method_source_file,0);
+    rb_define_method(cMethodInfo, "line", prof_method_line, 0);
+
+    rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
+
+    /* CallInfo */
+    cCallInfo = rb_define_class_under(mProf, "CallInfo", rb_cObject);
+    rb_undef_method(CLASS_OF(cCallInfo), "new");
+    rb_define_method(cCallInfo, "parent", prof_call_info_parent, 0);
+    rb_define_method(cCallInfo, "children", prof_call_info_children, 0);
+    rb_define_method(cCallInfo, "target", prof_call_info_target, 0);
+    rb_define_method(cCallInfo, "called", prof_call_info_called, 0);
+    rb_define_method(cCallInfo, "total_time", prof_call_info_total_time, 0);
+    rb_define_method(cCallInfo, "self_time", prof_call_info_self_time, 0);
+    rb_define_method(cCallInfo, "wait_time", prof_call_info_wait_time, 0);
+    rb_define_method(cCallInfo, "line", prof_call_info_line, 0);
+}
diff --git a/ext/ruby_prof.h b/ext/ruby_prof.h
new file mode 100644
index 0000000..cd6a1eb
--- /dev/null
+++ b/ext/ruby_prof.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008  Shugo Maeda <shugo at ruby-lang.org>
+ *                     Charlie Savage <cfis at savagexi.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* ruby-prof tracks the time spent executing every method in ruby programming.
+   The main players are:
+
+     prof_result_t     - Its one field, values,  contains the overall results
+     thread_data_t     - Stores data about a single thread.  
+     prof_stack_t      - The method call stack in a particular thread
+     prof_method_t     - Profiling information for each method
+     prof_call_info_t  - Keeps track a method's callers and callees. 
+
+  The final resulut is a hash table of thread_data_t, keyed on the thread
+  id.  Each thread has an hash a table of prof_method_t, keyed on the
+  method id.  A hash table is used for quick look up when doing a profile.
+  However, it is exposed to Ruby as an array.
+  
+  Each prof_method_t has two hash tables, parent and children, of prof_call_info_t.
+  These objects keep track of a method's callers (who called the method) and its
+  callees (who the method called).  These are keyed the method id, but once again,
+  are exposed to Ruby as arrays.  Each prof_call_into_t maintains a pointer to the
+  caller or callee method, thereby making it easy to navigate through the call 
+  hierarchy in ruby - which is very helpful for creating call graphs.      
+*/
+
+/* #define DEBUG */
+
+#ifndef RUBY_PROF_H
+#define RUBY_PROF_H
+
+#include <stdio.h>
+
+#include <ruby.h>
+
+#ifndef RUBY_VM
+#include <node.h>
+#include <st.h>
+typedef rb_event_t rb_event_flag_t;
+#define rb_sourcefile() (node ? node->nd_file : 0)
+#define rb_sourceline() (node ? nd_line(node) : 0)
+#endif
+
+#include "version.h"
+
+/* ================  Constants  =================*/
+#define INITIAL_STACK_SIZE 8
+#define INITIAL_CALL_INFOS_SIZE 2
+
+
+/* ================  Measurement  =================*/
+#ifdef HAVE_LONG_LONG
+typedef unsigned LONG_LONG prof_measure_t;
+#else
+typedef unsigned long prof_measure_t;
+#endif
+
+#include "measure_process_time.h"
+#include "measure_wall_time.h"
+#include "measure_cpu_time.h"
+#include "measure_allocations.h"
+#include "measure_memory.h"
+#include "measure_gc_runs.h"
+#include "measure_gc_time.h"
+
+static prof_measure_t (*get_measurement)() = measure_process_time;
+static double (*convert_measurement)(prof_measure_t) = convert_process_time;
+
+/* ================  DataTypes  =================*/
+static VALUE mProf;
+static VALUE cResult;
+static VALUE cMethodInfo;
+static VALUE cCallInfo;
+
+/* Profiling information for each method. */
+typedef struct {
+    VALUE klass;                            /* The method's class. */
+    ID mid;                                 /* The method id. */
+    int depth;                              /* The recursion depth. */
+    int key;                                /* Cache calculated key */
+} prof_method_key_t;
+
+struct prof_call_infos_t;
+
+/* Profiling information for each method. */
+typedef struct {
+    prof_method_key_t *key;                 /* Method key */
+    const char *source_file;                /* The method's source file */
+    int line;                               /* The method's line number. */
+    int active;                             /* Is this  recursion depth. */
+    struct prof_call_infos_t *call_infos;   /* Call info objects for this method */
+    VALUE object;                           /* Cahced ruby object */
+} prof_method_t;
+
+/* Callers and callee information for a method. */
+typedef struct prof_call_info_t {
+    prof_method_t *target; /* Use target instead of method to avoid conflict with Ruby method */
+    struct prof_call_info_t *parent;
+    st_table *call_infos;
+    int called;
+    prof_measure_t total_time;
+    prof_measure_t self_time;
+    prof_measure_t wait_time;
+    int line;  
+    VALUE object;
+    VALUE children;
+} prof_call_info_t;
+
+/* Array of call_info objects */
+typedef struct prof_call_infos_t {
+    prof_call_info_t **start;
+    prof_call_info_t **end;
+    prof_call_info_t **ptr;
+    VALUE object;
+} prof_call_infos_t;
+
+
+/* Temporary object that maintains profiling information
+   for active methods - there is one per method.*/
+typedef struct {
+    /* Caching prof_method_t values significantly
+       increases performance. */
+    prof_call_info_t *call_info;
+    prof_measure_t start_time;
+    prof_measure_t wait_time;
+    prof_measure_t child_time;
+    unsigned int line;
+} prof_frame_t;
+
+/* Current stack of active methods.*/
+typedef struct {
+    prof_frame_t *start;
+    prof_frame_t *end;
+    prof_frame_t *ptr;
+} prof_stack_t;
+
+/* Profiling information for a thread. */
+typedef struct {
+    VALUE thread_id;                  /* Thread id */
+    st_table* method_table;           /* Methods called in the thread */
+    prof_stack_t* stack;              /* Active methods */
+    prof_measure_t last_switch;       /* Point of last context switch */
+} thread_data_t;
+
+typedef struct {
+    VALUE threads;
+} prof_result_t;
+
+
+/* ================  Variables  =================*/
+static int measure_mode;
+static st_table *threads_tbl = NULL;
+static st_table *exclude_threads_tbl = NULL;
+
+/* TODO - If Ruby become multi-threaded this has to turn into
+   a separate stack since this isn't thread safe! */
+static thread_data_t* last_thread_data = NULL;
+
+
+/* Forward declarations */
+static VALUE prof_call_infos_wrap(prof_call_infos_t *call_infos);
+static VALUE prof_call_info_wrap(prof_call_info_t *call_info);
+static VALUE prof_method_wrap(prof_method_t *result);
+
+#endif
diff --git a/ext/vc/ruby_prof.sln b/ext/vc/ruby_prof.sln
new file mode 100644
index 0000000..2f123b1
--- /dev/null
+++ b/ext/vc/ruby_prof.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ruby_prof", "ruby_prof.vcproj", "{DDB3E992-BF4B-4413-B061-288E40AECAD3}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{DDB3E992-BF4B-4413-B061-288E40AECAD3}.Debug|Win32.ActiveCfg = Debug|Win32
+		{DDB3E992-BF4B-4413-B061-288E40AECAD3}.Debug|Win32.Build.0 = Debug|Win32
+		{DDB3E992-BF4B-4413-B061-288E40AECAD3}.Release|Win32.ActiveCfg = Release|Win32
+		{DDB3E992-BF4B-4413-B061-288E40AECAD3}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/ext/vc/ruby_prof.vcproj b/ext/vc/ruby_prof.vcproj
new file mode 100644
index 0000000..d5041e3
--- /dev/null
+++ b/ext/vc/ruby_prof.vcproj
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="ruby_prof"
+	ProjectGUID="{DDB3E992-BF4B-4413-B061-288E40AECAD3}"
+	RootNamespace="rubyprof"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="131072"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories=""C:\Development\ruby\lib\ruby\1.8\i386-mswin32""
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;RUBYPROF_EXPORTS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				BrowseInformation="0"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="msvcrt-ruby18.lib"
+				OutputFile="C:\Development\ruby\lib\ruby\gems\1.8\gems\ruby-prof-0.7.0-x86-mswin32-60\lib\$(ProjectName).so"
+				LinkIncremental="2"
+				AdditionalLibraryDirectories="C:\Development\ruby\lib"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="C:\Development\ruby\lib\ruby\1.8\i386-mswin32"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;RUBYPROF_EXPORTS"
+				RuntimeLibrary="2"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="msvcrt-ruby18.lib"
+				OutputFile="$(OutDir)\$(ProjectName).so"
+				LinkIncremental="1"
+				AdditionalLibraryDirectories="C:\Development\ruby\lib"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath="..\ruby_prof.c"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath="..\measure_allocations.h"
+				>
+			</File>
+			<File
+				RelativePath="..\measure_cpu_time.h"
+				>
+			</File>
+			<File
+				RelativePath="..\measure_gc_runs.h"
+				>
+			</File>
+			<File
+				RelativePath="..\measure_gc_time.h"
+				>
+			</File>
+			<File
+				RelativePath="..\measure_memory.h"
+				>
+			</File>
+			<File
+				RelativePath="..\measure_process_time.h"
+				>
+			</File>
+			<File
+				RelativePath="..\measure_wall_time.h"
+				>
+			</File>
+			<File
+				RelativePath="..\ruby_prof.h"
+				>
+			</File>
+			<File
+				RelativePath="..\version.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/ext/version.h b/ext/version.h
new file mode 100644
index 0000000..3ceb126
--- /dev/null
+++ b/ext/version.h
@@ -0,0 +1,4 @@
+#define RUBY_PROF_VERSION  "0.7.3"
+#define RUBY_PROF_VERSION_MAJ   0
+#define RUBY_PROF_VERSION_MIN   7
+#define RUBY_PROF_VERSION_MIC   3
diff --git a/lib/ruby-prof.rb b/lib/ruby-prof.rb
new file mode 100644
index 0000000..8b24a7e
--- /dev/null
+++ b/lib/ruby-prof.rb
@@ -0,0 +1,48 @@
+require "ruby_prof.so"
+
+require "ruby-prof/method_info"
+require "ruby-prof/call_info"
+require "ruby-prof/aggregate_call_info"
+require "ruby-prof/flat_printer"
+require "ruby-prof/graph_printer"
+require "ruby-prof/graph_html_printer"
+require "ruby-prof/call_tree_printer"
+
+require "ruby-prof/test"
+
+module RubyProf
+  # See if the user specified the clock mode via 
+  # the RUBY_PROF_MEASURE_MODE environment variable
+  def self.figure_measure_mode
+    case ENV["RUBY_PROF_MEASURE_MODE"]
+    when "wall" || "wall_time"
+      RubyProf.measure_mode = RubyProf::WALL_TIME
+    when "cpu" || "cpu_time"
+      if ENV.key?("RUBY_PROF_CPU_FREQUENCY")
+        RubyProf.cpu_frequency = ENV["RUBY_PROF_CPU_FREQUENCY"].to_f
+      else
+        begin
+          open("/proc/cpuinfo") do |f|
+            f.each_line do |line|
+              s = line.slice(/cpu MHz\s*:\s*(.*)/, 1)
+              if s
+                RubyProf.cpu_frequency = s.to_f * 1000000
+                break
+              end
+            end
+          end
+        rescue Errno::ENOENT
+        end
+      end
+      RubyProf.measure_mode = RubyProf::CPU_TIME
+    when "allocations"
+      RubyProf.measure_mode = RubyProf::ALLOCATIONS
+    when "memory"
+      RubyProf.measure_mode = RubyProf::MEMORY
+    else
+      RubyProf.measure_mode = RubyProf::PROCESS_TIME
+    end
+  end
+end
+
+RubyProf::figure_measure_mode
diff --git a/lib/ruby-prof/abstract_printer.rb b/lib/ruby-prof/abstract_printer.rb
new file mode 100644
index 0000000..63cd60d
--- /dev/null
+++ b/lib/ruby-prof/abstract_printer.rb
@@ -0,0 +1,41 @@
+module RubyProf
+  class AbstractPrinter
+    def initialize(result)
+      @result = result
+      @output = nil
+      @options = {}
+    end
+
+    # Specify print options.
+    # 
+    # options - Hash table
+    #   :min_percent - Number 0 to 100 that specifes the minimum
+    #                  %self (the methods self time divided by the
+    #                  overall total time) that a method must take
+    #                  for it to be printed out in the report.
+    #                  Default value is 0.
+    #
+    #   :print_file  - True or false. Specifies if a method's source
+    #                  file should be printed.  Default value if false.
+    #
+    def setup_options(options = {})
+      @options = options
+    end      
+
+    def min_percent
+      @options[:min_percent] || 0
+    end
+    
+    def print_file
+      @options[:print_file] || false
+    end
+    
+    def method_name(method)
+      name = method.full_name
+      if print_file
+        name += " (#{method.source_file}:#{method.line}}"
+      end
+      name
+    end
+  end
+end 
\ No newline at end of file
diff --git a/lib/ruby-prof/aggregate_call_info.rb b/lib/ruby-prof/aggregate_call_info.rb
new file mode 100644
index 0000000..c86d2c4
--- /dev/null
+++ b/lib/ruby-prof/aggregate_call_info.rb
@@ -0,0 +1,62 @@
+module RubyProf
+  class AggregateCallInfo
+    attr_reader :call_infos
+    def initialize(call_infos)
+      if call_infos.length == 0
+        raise(ArgumentError, "Must specify at least one call info.")
+      end
+      @call_infos = call_infos
+    end
+
+    def target
+      call_infos.first.target
+    end
+
+    def parent
+      call_infos.first.parent
+    end
+
+    def line
+      call_infos.first.line
+    end
+
+    def children
+      call_infos.inject(Array.new) do |result, call_info|
+        result.concat(call_info.children)
+      end
+    end
+
+    def total_time
+      aggregate(:total_time)
+    end
+
+    def self_time
+      aggregate(:self_time)
+    end
+
+    def wait_time
+      aggregate(:wait_time)
+    end
+
+    def children_time
+      aggregate(:children_time)
+    end
+
+    def called
+      aggregate(:called)
+    end
+
+    def to_s
+      "#{call_infos.first.full_name}"
+    end
+
+    private
+
+    def aggregate(method_name)
+      self.call_infos.inject(0) do |sum, call_info|
+        sum += call_info.send(method_name)
+        sum
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/ruby-prof/call_info.rb b/lib/ruby-prof/call_info.rb
new file mode 100644
index 0000000..0f08d90
--- /dev/null
+++ b/lib/ruby-prof/call_info.rb
@@ -0,0 +1,47 @@
+module RubyProf
+  class CallInfo
+    def depth
+      result = 0
+      call_info = self.parent
+
+      while call_info
+        result += 1
+        call_info = call_info.parent
+      end
+      result
+    end
+
+    def children_time
+      children.inject(0) do |sum, call_info|
+        sum += call_info.total_time
+      end
+    end
+
+    def stack
+      @stack ||= begin
+        methods = Array.new
+        call_info = self
+
+        while call_info
+          methods << call_info.target
+          call_info = call_info.parent
+        end
+        methods.reverse
+      end
+    end
+
+    def call_sequence
+      @call_sequence ||= begin
+        stack.map {|method| method.full_name}.join('->')
+      end
+    end
+
+    def root?
+      self.parent.nil?
+    end
+
+    def to_s
+      "#{call_sequence}"
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/ruby-prof/call_tree_printer.rb b/lib/ruby-prof/call_tree_printer.rb
new file mode 100644
index 0000000..a01648c
--- /dev/null
+++ b/lib/ruby-prof/call_tree_printer.rb
@@ -0,0 +1,84 @@
+require 'ruby-prof/abstract_printer'
+
+module RubyProf
+  # Generate profiling information in calltree format
+  # for use by kcachegrind and similar tools.
+
+  class CallTreePrinter  < AbstractPrinter
+    def print(output = STDOUT, options = {})
+      @output = output
+      setup_options(options)
+        
+      # add a header - this information is somewhat arbitrary
+      @output << "events: "
+      case RubyProf.measure_mode
+        when RubyProf::PROCESS_TIME
+          @value_scale = RubyProf::CLOCKS_PER_SEC;
+          @output << 'process_time'
+        when RubyProf::WALL_TIME
+          @value_scale = 1_000_000
+          @output << 'wall_time'
+        when RubyProf.const_defined?(:CPU_TIME) && RubyProf::CPU_TIME
+          @value_scale = RubyProf.cpu_frequency
+          @output << 'cpu_time'
+        when RubyProf.const_defined?(:ALLOCATIONS) && RubyProf::ALLOCATIONS
+          @value_scale = 1
+          @output << 'allocations'
+        when RubyProf.const_defined?(:MEMORY) && RubyProf::MEMORY
+          @value_scale = 1
+          @output << 'memory'
+        when RubyProf.const_defined?(:GC_RUNS) && RubyProf::GC_RUNS
+          @value_scale = 1
+          @output << 'gc_runs'
+        when RubyProf.const_defined?(:GC_TIME) && RubyProf::GC_TIME
+          @value_scale = 1000000
+          @output << 'gc_time'
+        else
+          raise "Unknown measure mode: #{RubyProf.measure_mode}"
+      end
+      @output << "\n\n"
+
+      print_threads
+    end
+
+    def print_threads
+      @result.threads.each do |thread_id, methods|
+        print_methods(thread_id, methods)
+      end
+    end
+
+    def convert(value)
+      (value * @value_scale).round
+    end
+
+    def file(method)
+      File.expand_path(method.source_file)
+    end
+
+    def name(method)
+      "#{method.klass_name}::#{method.method_name}"
+    end
+
+    def print_methods(thread_id, methods)
+      methods.reverse_each do |method| 
+        # Print out the file and method name
+        @output << "fl=#{file(method)}\n"
+        @output << "fn=#{name(method)}\n"
+
+        # Now print out the function line number and its self time
+        @output << "#{method.line} #{convert(method.self_time)}\n"
+
+        # Now print out all the children methods
+        method.children.each do |callee|
+          @output << "cfl=#{file(callee.target)}\n"
+          @output << "cfn=#{name(callee.target)}\n"
+          @output << "calls=#{callee.called} #{callee.line}\n"
+
+          # Print out total times here!
+          @output << "#{callee.line} #{convert(callee.total_time)}\n"
+        end
+      @output << "\n"
+      end
+    end #end print_methods
+  end # end class
+end # end packages
diff --git a/lib/ruby-prof/flat_printer.rb b/lib/ruby-prof/flat_printer.rb
new file mode 100644
index 0000000..6c8259d
--- /dev/null
+++ b/lib/ruby-prof/flat_printer.rb
@@ -0,0 +1,79 @@
+require 'ruby-prof/abstract_printer'
+
+module RubyProf
+  # Generates flat[link:files/examples/flat_txt.html] profile reports as text. 
+  # To use the flat printer:
+  #
+  #   result = RubyProf.profile do
+  #     [code to profile]
+  #   end
+  #
+  #   printer = RubyProf::FlatPrinter.new(result)
+  #   printer.print(STDOUT, 0)
+  #
+  class FlatPrinter < AbstractPrinter
+    # Print a flat profile report to the provided output.
+    # 
+    # output - Any IO oject, including STDOUT or a file. 
+    # The default value is STDOUT.
+    # 
+    # options - Hash of print options.  See #setup_options 
+    #           for more information.
+    #
+    def print(output = STDOUT, options = {})
+      @output = output
+      setup_options(options)
+      print_threads
+    end      
+    
+    private 
+    
+    def print_threads
+      @result.threads.each do |thread_id, methods|
+        print_methods(thread_id, methods)
+        @output << "\n" * 2
+      end
+    end
+    
+    def print_methods(thread_id, methods)
+      # Get total time
+      toplevel = methods.sort.last
+      total_time = toplevel.total_time
+      if total_time == 0
+        total_time = 0.01
+      end
+      
+      # Now sort methods by largest self time,
+      # not total time like in other printouts
+      methods = methods.sort do |m1, m2|
+        m1.self_time <=> m2.self_time
+      end.reverse
+      
+      @output << "Thread ID: %d\n" % thread_id
+      @output << "Total: %0.6f\n" % total_time
+      @output << "\n"
+      @output << " %self     total     self     wait    child    calls  name\n"
+
+      sum = 0    
+      methods.each do |method|
+        self_percent = (method.self_time / total_time) * 100
+        next if self_percent < min_percent
+        
+        sum += method.self_time
+        #self_time_called = method.called > 0 ? method.self_time/method.called : 0
+        #total_time_called = method.called > 0? method.total_time/method.called : 0
+        
+        @output << "%6.2f  %8.2f %8.2f %8.2f %8.2f %8d  %s\n" % [
+                      method.self_time / total_time * 100, # %self
+                      method.total_time,                   # total
+                      method.self_time,                    # self
+                      method.wait_time,                    # wait
+                      method.children_time,                # children
+                      method.called,                       # calls
+                    method_name(method)                  # name
+                  ]
+      end
+    end
+  end
+end 
+
diff --git a/lib/ruby-prof/graph_html_printer.rb b/lib/ruby-prof/graph_html_printer.rb
new file mode 100644
index 0000000..7b04d4b
--- /dev/null
+++ b/lib/ruby-prof/graph_html_printer.rb
@@ -0,0 +1,256 @@
+require 'ruby-prof/abstract_printer'
+require 'erb'
+
+module RubyProf
+  # Generates graph[link:files/examples/graph_html.html] profile reports as html. 
+  # To use the grap html printer:
+  #
+  #   result = RubyProf.profile do
+  #     [code to profile]
+  #   end
+  #
+  #   printer = RubyProf::GraphHtmlPrinter.new(result)
+  #   printer.print(STDOUT, :min_percent=>0)
+  #
+  # The constructor takes two arguments.  The first is
+  # a RubyProf::Result object generated from a profiling
+  # run.  The second is the minimum %total (the methods 
+  # total time divided by the overall total time) that
+  # a method must take for it to be printed out in 
+  # the report.  Use this parameter to eliminate methods
+  # that are not important to the overall profiling results.
+  
+  class GraphHtmlPrinter < AbstractPrinter
+    include ERB::Util
+    
+    PERCENTAGE_WIDTH = 8
+    TIME_WIDTH = 10
+    CALL_WIDTH = 20
+  
+    # Create a GraphPrinter.  Result is a RubyProf::Result  
+    # object generated from a profiling run.
+    def initialize(result)
+      super(result)
+      @thread_times = Hash.new
+      calculate_thread_times
+    end
+
+    # Print a graph html report to the provided output.
+    # 
+    # output - Any IO oject, including STDOUT or a file. 
+    # The default value is STDOUT.
+    # 
+    # options - Hash of print options.  See #setup_options 
+    #           for more information.
+    #
+    def print(output = STDOUT, options = {})
+      @output = output
+      setup_options(options)
+      
+      filename = options[:filename]
+      template = filename ? File.read(filename).untaint : (options[:template] || self.template)
+      _erbout = @output
+      erb = ERB.new(template, nil, nil)
+      erb.filename = filename
+      @output << erb.result(binding)
+    end
+
+    # These methods should be private but then ERB doesn't
+    # work.  Turn off RDOC though 
+    #--
+    def calculate_thread_times
+      # Cache thread times since this is an expensive
+      # operation with the required sorting      
+      @result.threads.each do |thread_id, methods|
+        top = methods.sort.last
+        
+        thread_time = 0.01
+        thread_time = top.total_time if top.total_time > 0
+
+        @thread_times[thread_id] = thread_time 
+      end
+    end
+    
+    def thread_time(thread_id)
+      @thread_times[thread_id]
+    end
+   
+    def total_percent(thread_id, method)
+      overall_time = self.thread_time(thread_id)
+      (method.total_time/overall_time) * 100
+    end
+    
+    def self_percent(method)
+      overall_time = self.thread_time(method.thread_id)
+      (method.self_time/overall_time) * 100
+    end
+
+    # Creates a link to a method.  Note that we do not create
+    # links to methods which are under the min_perecent 
+    # specified by the user, since they will not be
+    # printed out.
+    def create_link(thread_id, method)
+      if self.total_percent(thread_id, method) < min_percent
+        # Just return name
+        h method.full_name
+      else
+        href = '#' + method_href(thread_id, method)
+        "<a href=\"#{href}\">#{h method.full_name}</a>" 
+      end
+    end
+    
+    def method_href(thread_id, method)
+      h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + thread_id.to_s)
+    end
+    
+    def template
+'
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+  <style media="all" type="text/css">
+    table {
+      border-collapse: collapse;
+      border: 1px solid #CCC;
+      font-family: Verdana, Arial, Helvetica, sans-serif;
+      font-size: 9pt;
+      line-height: normal;
+      width: 100%;
+    }
+
+    th {
+      text-align: center;
+      border-top: 1px solid #FB7A31;
+      border-bottom: 1px solid #FB7A31;
+      background: #FFC;
+      padding: 0.3em;
+      border-left: 1px solid silver;
+    }
+
+    tr.break td {
+      border: 0;
+      border-top: 1px solid #FB7A31;
+      padding: 0;
+      margin: 0;
+    }
+
+    tr.method td {
+      font-weight: bold;
+    }
+
+    td {
+      padding: 0.3em;
+    }
+
+    td:first-child {
+      width: 190px;
+      }
+
+    td {
+      border-left: 1px solid #CCC;
+      text-align: center;
+    } 
+
+    .method_name {
+      text-align: left;
+    }
+  </style>
+  </head>
+  <body>
+    <h1>Profile Report</h1>
+    <!-- Threads Table -->
+    <table>
+      <tr>
+        <th>Thread ID</th>
+        <th>Total Time</th>
+      </tr>
+      <% for thread_id, methods in @result.threads %>
+      <tr>
+        <td><a href="#<%= thread_id %>"><%= thread_id %></a></td>
+        <td><%= thread_time(thread_id) %></td>
+      </tr>
+      <% end %>
+    </table>
+
+    <!-- Methods Tables -->
+    <% for thread_id, methods in @result.threads
+         total_time = thread_time(thread_id) %>
+      <h2><a name="<%= thread_id %>">Thread <%= thread_id %></a></h2>
+
+      <table>
+        <tr>
+          <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Total") %></th>
+          <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Self") %></th>
+          <th><%= sprintf("%#{TIME_WIDTH}s", "Total") %></th>
+          <th><%= sprintf("%#{TIME_WIDTH}s", "Self") %></th>
+          <th><%= sprintf("%#{TIME_WIDTH}s", "Wait") %></th>
+          <th><%= sprintf("%#{TIME_WIDTH+2}s", "Child") %></th>
+          <th><%= sprintf("%#{CALL_WIDTH}s", "Calls") %></th>
+          <th class="method_name">Name</th>
+          <th>Line</th>
+        </tr>
+
+        <% min_time = @options[:min_time] || (@options[:nonzero] ? 0.005 : nil)
+           methods.sort.reverse_each do |method|
+            total_percentage = (method.total_time/total_time) * 100
+            next if total_percentage < min_percent
+            next if min_time && method.total_time < min_time
+            self_percentage = (method.self_time/total_time) * 100 %>
+          
+            <!-- Parents -->
+            <% for caller in method.aggregate_parents
+                 next unless caller.parent
+                 next if min_time && caller.total_time < min_time  %>
+              <tr>
+                <td> </td>
+                <td> </td>
+                <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.total_time) %></td>
+                <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.self_time) %></td>
+                <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.wait_time) %></td>
+                <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.children_time) %></td>
+                <% called = "#{caller.called}/#{method.called}" %>
+                <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
+                <td class="method_name"><%= create_link(thread_id, caller.parent.target) %></td>
+                <td><a href="file://<%=h srcfile=File.expand_path(caller.parent.target.source_file) %>#line=<%= linenum=caller.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= caller.line %></a></td>
+              </tr>
+            <% end %>
+
+            <tr class="method">
+              <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage) %></td>
+              <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage) %></td>
+              <td><%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %></td>
+              <td><%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %></td>
+              <td><%= sprintf("%#{TIME_WIDTH}.2f", method.wait_time) %></td>
+              <td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
+              <td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
+              <td class="method_name"><a name="<%= method_href(thread_id, method) %>"><%= h method.full_name %></a></td>
+              <td><a href="file://<%=h srcfile=File.expand_path(method.source_file) %>#line=<%= linenum=method.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= method.line %></a></td>
+            </tr>
+
+            <!-- Children -->
+            <% for callee in method.aggregate_children %>
+            <%   next if min_time && callee.total_time < min_time  %>
+              <tr>
+                <td> </td>
+                <td> </td>
+                <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.total_time) %></td>
+                <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.self_time) %></td>
+                <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.wait_time) %></td>
+                <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.children_time) %></td>
+                <% called = "#{callee.called}/#{callee.target.called}" %>
+                <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
+                <td class="method_name"><%= create_link(thread_id, callee.target) %></td>
+                <td><a href="file://<%=h srcfile=File.expand_path(method.source_file) %>#line=<%= linenum=callee.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= callee.line %></a></td>
+              </tr>
+            <% end %>
+            <!-- Create divider row -->
+            <tr class="break"><td colspan="9"></td></tr>
+        <% end %>
+      </table>
+    <% end %>
+  </body>
+</html>'
+    end
+  end
+end 
+
diff --git a/lib/ruby-prof/graph_printer.rb b/lib/ruby-prof/graph_printer.rb
new file mode 100644
index 0000000..a03eebc
--- /dev/null
+++ b/lib/ruby-prof/graph_printer.rb
@@ -0,0 +1,164 @@
+require 'ruby-prof/abstract_printer'
+
+module RubyProf
+  # Generates graph[link:files/examples/graph_txt.html] profile reports as text. 
+  # To use the graph printer:
+  #
+  #   result = RubyProf.profile do
+  #     [code to profile]
+  #   end
+  #
+  #   printer = RubyProf::GraphPrinter.new(result, 5)
+  #   printer.print(STDOUT, 0)
+  #
+  # The constructor takes two arguments.  The first is
+  # a RubyProf::Result object generated from a profiling
+  # run.  The second is the minimum %total (the methods 
+  # total time divided by the overall total time) that
+  # a method must take for it to be printed out in 
+  # the report.  Use this parameter to eliminate methods
+  # that are not important to the overall profiling results.
+
+  class GraphPrinter < AbstractPrinter
+    PERCENTAGE_WIDTH = 8
+    TIME_WIDTH = 10
+    CALL_WIDTH = 17
+  
+    # Create a GraphPrinter.  Result is a RubyProf::Result  
+    # object generated from a profiling run.
+    def initialize(result)
+      super(result)
+      @thread_times = Hash.new
+      calculate_thread_times
+    end
+
+    def calculate_thread_times
+      # Cache thread times since this is an expensive
+      # operation with the required sorting      
+      @result.threads.each do |thread_id, methods|
+        top = methods.sort.last
+        
+        thread_time = 0.01
+        thread_time = top.total_time if top.total_time > 0
+
+        @thread_times[thread_id] = thread_time 
+      end
+    end
+    
+    # Print a graph report to the provided output.
+    # 
+    # output - Any IO oject, including STDOUT or a file. 
+    # The default value is STDOUT.
+    # 
+    # options - Hash of print options.  See #setup_options 
+    #           for more information.
+    #
+    def print(output = STDOUT, options = {})
+      @output = output
+      setup_options(options)
+      print_threads
+    end
+
+    private 
+    def print_threads
+      # sort assumes that spawned threads have higher object_ids
+      @result.threads.sort.each do |thread_id, methods|
+        print_methods(thread_id, methods)
+        @output << "\n" * 2
+      end
+    end
+    
+    def print_methods(thread_id, methods)
+      # Sort methods from longest to shortest total time
+      methods = methods.sort
+      
+      toplevel = methods.last
+      total_time = toplevel.total_time
+      if total_time == 0
+        total_time = 0.01
+      end
+      
+      print_heading(thread_id)
+    
+      # Print each method in total time order
+      methods.reverse_each do |method|
+        total_percentage = (method.total_time/total_time) * 100
+        self_percentage = (method.self_time/total_time) * 100
+        
+        next if total_percentage < min_percent
+        
+        @output << "-" * 80 << "\n"
+
+        print_parents(thread_id, method)
+    
+        # 1 is for % sign
+        @output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage)
+        @output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage)
+        @output << sprintf("%#{TIME_WIDTH}.2f", method.total_time)
+        @output << sprintf("%#{TIME_WIDTH}.2f", method.self_time)
+        @output << sprintf("%#{TIME_WIDTH}.2f", method.wait_time)
+        @output << sprintf("%#{TIME_WIDTH}.2f", method.children_time)
+        @output << sprintf("%#{CALL_WIDTH}i", method.called)
+        @output << sprintf("     %s", method_name(method))
+        if print_file
+          @output << sprintf("  %s:%s", method.source_file, method.line)
+        end          
+        @output << "\n"
+    
+        print_children(method)
+      end
+    end
+  
+    def print_heading(thread_id)
+      @output << "Thread ID: #{thread_id}\n"
+      @output << "Total Time: #{@thread_times[thread_id]}\n"
+      @output << "\n"
+      
+      # 1 is for % sign
+      @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%total")
+      @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%self")
+      @output << sprintf("%#{TIME_WIDTH}s", "total")
+      @output << sprintf("%#{TIME_WIDTH}s", "self")
+      @output << sprintf("%#{TIME_WIDTH}s", "wait")
+      @output << sprintf("%#{TIME_WIDTH}s", "child")
+      @output << sprintf("%#{CALL_WIDTH}s", "calls")
+      @output << "   Name"
+      @output << "\n"
+    end
+    
+    def print_parents(thread_id, method)
+      method.aggregate_parents.each do |caller|
+        next unless caller.parent
+        @output << " " * 2 * PERCENTAGE_WIDTH
+        @output << sprintf("%#{TIME_WIDTH}.2f", caller.total_time)
+        @output << sprintf("%#{TIME_WIDTH}.2f", caller.self_time)
+        @output << sprintf("%#{TIME_WIDTH}.2f", caller.wait_time)
+        @output << sprintf("%#{TIME_WIDTH}.2f", caller.children_time)
+    
+        call_called = "#{caller.called}/#{method.called}"
+        @output << sprintf("%#{CALL_WIDTH}s", call_called)
+        @output << sprintf("     %s", caller.parent.target.full_name)
+        @output << "\n"
+      end
+    end
+  
+    def print_children(method)
+      method.aggregate_children.each do |child|
+        # Get children method
+        
+        @output << " " * 2 * PERCENTAGE_WIDTH
+        
+        @output << sprintf("%#{TIME_WIDTH}.2f", child.total_time)
+        @output << sprintf("%#{TIME_WIDTH}.2f", child.self_time)
+        @output << sprintf("%#{TIME_WIDTH}.2f", child.wait_time)
+        @output << sprintf("%#{TIME_WIDTH}.2f", child.children_time)
+
+        call_called = "#{child.called}/#{child.target.called}"
+        @output << sprintf("%#{CALL_WIDTH}s", call_called)
+        @output << sprintf("     %s", child.target.full_name)
+        @output << "\n"
+      end
+    end
+  end
+end 
+
diff --git a/lib/ruby-prof/method_info.rb b/lib/ruby-prof/method_info.rb
new file mode 100644
index 0000000..9f5ec2e
--- /dev/null
+++ b/lib/ruby-prof/method_info.rb
@@ -0,0 +1,111 @@
+module RubyProf
+  class MethodInfo
+    include Comparable
+
+    def <=>(other)
+      if self.total_time < other.total_time
+        -1
+      elsif self.total_time > other.total_time
+        1
+      elsif self.min_depth < other.min_depth
+        1
+      elsif self.min_depth > other.min_depth
+        -1
+      else
+        -1 * (self.full_name <=> other.full_name)
+      end
+    end
+
+    def called
+      @called ||= begin
+        call_infos.inject(0) do |sum, call_info|
+          sum += call_info.called
+        end
+      end
+    end
+
+    def total_time
+      @total_time ||= begin
+        call_infos.inject(0) do |sum, call_info|
+          sum += call_info.total_time
+        end
+      end
+    end
+    
+    def self_time
+      @self_time ||= begin
+        call_infos.inject(0) do |sum, call_info|
+          sum += call_info.self_time
+        end
+      end
+    end
+
+    def wait_time
+      @wait_time ||= begin
+        call_infos.inject(0) do |sum, call_info|
+          sum += call_info.wait_time
+        end
+      end
+    end
+
+    def children_time
+      @children_time ||= begin
+        call_infos.inject(0) do |sum, call_info|
+          sum += call_info.children_time
+        end
+      end
+    end
+
+    def min_depth
+      call_infos.map do |call_info|
+        call_info.depth
+      end.min
+    end
+
+    def root?
+      @root ||= begin
+        call_infos.find do |call_info|
+          not call_info.root?
+        end.nil?
+      end
+    end
+
+    def children
+      @children ||= begin
+        call_infos.map do |call_info|
+          call_info.children
+        end.flatten
+      end
+    end
+
+    def aggregate_parents
+      # Group call info's based on their parents
+      groups = self.call_infos.inject(Hash.new) do |hash, call_info|
+        key = call_info.parent ? call_info.parent.target : self
+        (hash[key] ||= []) << call_info
+        hash
+      end
+
+      groups.map do |key, value|
+        AggregateCallInfo.new(value)
+      end
+    end
+
+    def aggregate_children
+      # Group call info's based on their targets
+      groups = self.children.inject(Hash.new) do |hash, call_info|
+        key = call_info.target
+        (hash[key] ||= []) << call_info
+        hash
+      end
+
+      groups.map do |key, value|
+        AggregateCallInfo.new(value)
+      end
+    end
+
+    def to_s
+      full_name
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/ruby-prof/task.rb b/lib/ruby-prof/task.rb
new file mode 100755
index 0000000..a29fe9c
--- /dev/null
+++ b/lib/ruby-prof/task.rb
@@ -0,0 +1,146 @@
+#!/usr/bin/env ruby
+
+require 'rake'
+require 'rake/testtask'
+require 'fileutils'
+
+module RubyProf
+
+  # Define a task library for profiling unit tests with ruby-prof.
+  #
+  # All of the options provided by
+  # the Rake:TestTask are supported except the loader
+  # which is set to ruby-prof.  For detailed information
+  # please refer to the Rake:TestTask documentation.
+  #
+  # ruby-prof specific options include:
+  #
+  #   output_dir - For each file specified an output 
+  #                file with profile information will be
+  #                written to the output directory.
+  #                By default, the output directory is
+  #                called "profile" and is created underneath
+  #                the current working directory.
+  #
+  #   printer - Specifies the output printer.  Valid values include
+  #             :flat, :graph, :graph_html and :call_tree.
+  #
+  #   min_percent - Methods that take less than the specified percent
+  #                 will not be written out.
+  #
+  # Example:
+  #   
+  #   require 'ruby-prof/task'
+  #   
+  #   RubyProf::ProfileTask.new do |t|
+  #     t.test_files = FileList['test/test*.rb']
+  #     t.output_dir = "c:/temp"
+  #     t.printer = :graph
+  #     t.min_percent = 10
+  #   end
+  #
+  # If rake is invoked with a "TEST=filename" command line option,
+  # then the list of test files will be overridden to include only the
+  # filename specified on the command line.  This provides an easy way
+  # to run just one test.
+  #
+  # If rake is invoked with a "TESTOPTS=options" command line option,
+  # then the given options are passed to the test process after a
+  # '--'.  This allows Test::Unit options to be passed to the test
+  # suite.
+  #
+  # Examples:
+  #
+  #   rake profile                           # run tests normally
+  #   rake profile TEST=just_one_file.rb     # run just one test file.
+  #   rake profile TESTOPTS="-v"             # run in verbose mode
+  #   rake profile TESTOPTS="--runner=fox"   # use the fox test runner
+  
+  class ProfileTask < Rake::TestTask
+    attr_accessor :output_dir 
+    attr_accessor :min_percent 
+    attr_accessor :printer
+    
+    def initialize(name = :profile)
+      super(name)
+    end
+    
+    # Create the tasks defined by this task lib.
+    def define
+      lib_path = @libs.join(File::PATH_SEPARATOR)
+      desc "Profile" + (@name==:profile ? "" : " for #{@name}")
+      
+      task @name do
+        create_output_directory
+        
+        @ruby_opts.unshift( "-I#{lib_path}" )
+        @ruby_opts.unshift( "-w" ) if @warning
+        @ruby_opts.push("-S ruby-prof")
+        @ruby_opts.push("--printer #{@printer}")
+        @ruby_opts.push("--min_percent #{@min_percent}")
+
+        file_list.each do |file_path|  
+          run_script(file_path)
+        end
+      end
+      self
+    end
+    
+    # Run script
+    def run_script(script_path)
+      run_code = ''
+      RakeFileUtils.verbose(@verbose) do
+        file_name = File.basename(script_path, File.extname(script_path))
+        case @printer
+          when :flat, :graph, :call_tree
+            file_name += ".txt"
+          when :graph_html
+            file_name += ".html"
+          else
+            file_name += ".txt"
+        end
+          
+        output_file_path = File.join(output_directory, file_name)
+          
+        command_line = @ruby_opts.join(" ") + 
+                      " --file=" + output_file_path +
+                      " " + script_path
+
+        puts "ruby " + command_line 
+        # We have to catch the exeption to continue on.  However,
+        # the error message will have been output to STDERR
+        # already by the time we get here so we don't have to
+        # do that again
+        begin
+          ruby command_line
+        rescue => e
+          STDOUT << e << "\n"
+          STDOUT.flush
+        end
+        puts ""
+        puts ""
+      end
+    end
+
+    def output_directory
+      File.expand_path(@output_dir)
+    end
+    
+    def create_output_directory
+      if not File.exist?(output_directory)
+        Dir.mkdir(output_directory)
+      end
+    end
+
+    def clean_output_directory
+      if File.exist?(output_directory)
+        files = Dir.glob(output_directory + '/*')
+        FileUtils.rm(files)
+      end
+    end
+    
+    def option_list # :nodoc:
+      ENV['OPTIONS'] || @options.join(" ") || ""
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/ruby-prof/test.rb b/lib/ruby-prof/test.rb
new file mode 100644
index 0000000..a0691cc
--- /dev/null
+++ b/lib/ruby-prof/test.rb
@@ -0,0 +1,148 @@
+# Now load ruby-prof and away we go
+require 'fileutils'
+require 'ruby-prof'
+require 'benchmark'
+
+module RubyProf
+  module Test
+    PROFILE_OPTIONS = {
+      :measure_modes => [RubyProf::PROCESS_TIME],
+      :count => 10,
+      :printers => [RubyProf::FlatPrinter, RubyProf::GraphHtmlPrinter],
+      :min_percent => 0.05,
+      :output_dir => Dir.pwd }
+
+    def output_dir
+      PROFILE_OPTIONS[:output_dir]
+    end
+          
+    def run(result)
+      return if @method_name.to_s == "default_test"
+
+      yield(self.class::STARTED, name)
+      @_result = result
+      run_warmup
+      PROFILE_OPTIONS[:measure_modes].each do |measure_mode|
+        data = run_profile(measure_mode)
+        report_profile(data, measure_mode)
+        result.add_run
+      end
+      yield(self.class::FINISHED, name)
+    end
+
+    def run_test
+      begin
+        setup
+        yield
+      rescue ::Test::Unit::AssertionFailedError => e
+        add_failure(e.message, e.backtrace)
+      rescue StandardError, ScriptError
+        add_error($!)
+      ensure
+        begin
+          teardown
+        rescue ::Test::Unit::AssertionFailedError => e
+          add_failure(e.message, e.backtrace)
+        rescue StandardError, ScriptError
+          add_error($!)
+        end
+      end
+    end
+
+    def run_warmup
+      print "\n#{self.class.name}##{method_name}"
+
+      run_test do
+        bench = Benchmark.realtime do
+          __send__(@method_name)
+        end
+        puts " (%.2fs warmup)" % bench
+      end
+    end
+
+    def run_profile(measure_mode)
+      RubyProf.measure_mode = measure_mode
+
+      print '  '
+      PROFILE_OPTIONS[:count].times do |i|
+        run_test do
+          begin
+            print '.'
+            $stdout.flush
+            GC.disable
+
+            RubyProf.resume do
+              __send__(@method_name)
+            end
+          ensure
+            GC.enable
+          end
+        end
+      end
+
+      data = RubyProf.stop
+      bench = data.threads.values.inject(0) do |total, method_infos|
+        top = method_infos.sort.last
+        total += top.total_time
+        total
+      end
+
+      puts "\n  #{measure_mode_name(measure_mode)}: #{format_profile_total(bench, measure_mode)}\n"
+
+      data
+    end
+
+    def format_profile_total(total, measure_mode)
+      case measure_mode
+        when RubyProf::PROCESS_TIME, RubyProf::WALL_TIME
+          "%.2f seconds" % total
+        when RubyProf::MEMORY
+          "%.2f kilobytes" % total
+        when RubyProf::ALLOCATIONS
+          "%d allocations" % total
+        else
+          "%.2f #{measure_mode}"
+      end
+    end
+
+    def report_profile(data, measure_mode)
+      PROFILE_OPTIONS[:printers].each do |printer_klass|
+        printer = printer_klass.new(data)
+        
+        # Makes sure the output directory exits
+        FileUtils.mkdir_p(output_dir)
+
+        # Open the file
+        file_name = report_filename(printer, measure_mode)
+
+        File.open(file_name, 'wb') do |file|
+          printer.print(file, PROFILE_OPTIONS)
+        end
+      end
+    end
+
+    # The report filename is test_name + measure_mode + report_type
+    def report_filename(printer, measure_mode)
+      suffix =
+        case printer
+          when RubyProf::FlatPrinter; 'flat.txt'
+          when RubyProf::GraphPrinter; 'graph.txt'
+          when RubyProf::GraphHtmlPrinter; 'graph.html'
+          when RubyProf::CallTreePrinter; 'tree.txt'
+          else printer.to_s.downcase
+        end
+
+      "#{output_dir}/#{method_name}_#{measure_mode_name(measure_mode)}_#{suffix}"
+    end
+
+    def measure_mode_name(measure_mode)
+      case measure_mode
+        when RubyProf::PROCESS_TIME; 'process_time'
+        when RubyProf::WALL_TIME; 'wall_time'
+        when RubyProf::MEMORY; 'memory'
+        when RubyProf::ALLOCATIONS; 'allocations'
+        else "measure#{measure_mode}"
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/unprof.rb b/lib/unprof.rb
new file mode 100644
index 0000000..0838096
--- /dev/null
+++ b/lib/unprof.rb
@@ -0,0 +1,8 @@
+require "ruby-prof"
+
+at_exit {
+  result = RubyProf.stop
+  printer = RubyProf::FlatPrinter.new(result)
+  printer.print(STDOUT)
+}
+RubyProf.start
diff --git a/rails/environment/profile.rb b/rails/environment/profile.rb
new file mode 100644
index 0000000..328100b
--- /dev/null
+++ b/rails/environment/profile.rb
@@ -0,0 +1,24 @@
+# Settings specified here will take precedence over those in config/environment.rb
+# The profile environment should match the same settings
+# as the production environment to give a reasonalbe
+# approximation of performance.  However, it should
+# definitely not use the production databse!
+
+
+# Cache classes - otherwise your code 
+# will run approximately 5 times slower and the
+# profiling results will be overwhelmed by Rails
+# dependency loading mechanism
+config.cache_classes = true
+
+# Don't check template timestamps - once again this
+# is to avoid IO times overwhelming profile results
+config.action_view.cache_template_loading            = true
+
+# This is debatable, but turn off action controller
+# caching to see how long it really takes to run
+# queries and render templates
+config.action_controller.perform_caching             = false
+
+# Turn off most logging
+config.log_level = :info
\ No newline at end of file
diff --git a/rails/example/example_test.rb b/rails/example/example_test.rb
new file mode 100644
index 0000000..131f987
--- /dev/null
+++ b/rails/example/example_test.rb
@@ -0,0 +1,9 @@
+require File.dirname(__FILE__) + '../profile_test_helper'
+
+class ExampleTest < Test::Unit::TestCase
+  include RubyProf::Test
+  
+  def test_stuff
+    puts "Test method"
+  end
+end   
diff --git a/rails/profile_test_helper.rb b/rails/profile_test_helper.rb
new file mode 100644
index 0000000..4692bd9
--- /dev/null
+++ b/rails/profile_test_helper.rb
@@ -0,0 +1,21 @@
+# Load profile environment
+env = ENV["RAILS_ENV"] = "profile"
+require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
+
+# Load Rails testing infrastructure
+require 'test_help'
+
+# Now we can load test_helper since we've already loaded the
+# profile RAILS environment.
+require File.expand_path(File.join(RAILS_ROOT, 'test', 'test_helper'))
+
+# Reset the current environment back to profile
+# since test_helper reset it to test
+ENV["RAILS_ENV"] = env
+
+# Now load ruby-prof and away we go
+require 'ruby-prof'
+
+# Setup output directory to Rails tmp directory
+RubyProf::Test::PROFILE_OPTIONS[:output_dir] = 
+    File.expand_path(File.join(RAILS_ROOT, 'tmp', 'profile'))
diff --git a/test/aggregate_test.rb b/test/aggregate_test.rb
new file mode 100755
index 0000000..87769e6
--- /dev/null
+++ b/test/aggregate_test.rb
@@ -0,0 +1,121 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'ruby-prof'
+
+# Test data
+#   A   B   C
+#   |   |   |
+#   Z   A   A
+#       |   |
+#       Z   Z
+
+class AggClass
+  def z
+    sleep 1
+  end
+  
+  def a
+    z
+  end
+
+  def b
+    a
+  end
+
+  def c
+   a
+  end
+end
+
+class AggregateTest < Test::Unit::TestCase
+  def setup
+    # Need to use wall time for this test due to the sleep calls
+    RubyProf::measure_mode = RubyProf::WALL_TIME
+  end
+
+  def test_call_infos
+    c1 = AggClass.new
+    result = RubyProf.profile do
+      c1.a
+      c1.b
+      c1.c
+    end
+
+    methods = result.threads.values.first.sort.reverse
+    method = methods.find {|method| method.full_name == 'AggClass#z'}
+
+    # Check AggClass#z
+    assert_equal('AggClass#z', method.full_name)
+    assert_equal(3, method.called)
+    assert_in_delta(3, method.total_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(3, method.children_time, 0.01)
+    assert_equal(3, method.call_infos.length)
+
+    call_info = method.call_infos[0]
+    assert_equal('AggregateTest#test_call_infos->AggClass#a->AggClass#z', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    call_info = method.call_infos[1]
+    assert_equal('AggregateTest#test_call_infos->AggClass#b->AggClass#a->AggClass#z', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    call_info = method.call_infos[2]
+    assert_equal('AggregateTest#test_call_infos->AggClass#c->AggClass#a->AggClass#z', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+  end
+
+  def test_aggregates_parents
+    c1 = AggClass.new
+    result = RubyProf.profile do
+      c1.a
+      c1.b
+      c1.c
+    end
+
+    methods = result.threads.values.first.sort.reverse
+    method = methods.find {|method| method.full_name == 'AggClass#z'}
+
+    # Check AggClass#z
+    assert_equal('AggClass#z', method.full_name)
+
+    call_infos = method.aggregate_parents
+    assert_equal(1, call_infos.length)
+
+    call_info = call_infos.first
+    assert_equal('AggClass#a', call_info.parent.target.full_name)
+    assert_in_delta(3, call_info.total_time, 0.01)
+    assert_in_delta(0, call_info.wait_time, 0.01)
+    assert_in_delta(0, call_info.self_time, 0.01)
+    assert_in_delta(3, call_info.children_time, 0.01)
+    assert_equal(3, call_info.called)
+  end
+
+  def test_aggregates_children
+    c1 = AggClass.new
+    result = RubyProf.profile do
+      c1.a
+      c1.b
+      c1.c
+    end
+
+    methods = result.threads.values.first.sort.reverse
+    method = methods.find {|method| method.full_name == 'AggClass#a'}
+
+    # Check AggClass#a
+    assert_equal('AggClass#a', method.full_name)
+
+    call_infos = method.aggregate_children
+    assert_equal(1, call_infos.length)
+
+    call_info = call_infos.first
+    assert_equal('AggClass#z', call_info.target.full_name)
+    assert_in_delta(3, call_info.total_time, 0.01)
+    assert_in_delta(0, call_info.wait_time, 0.01)
+    assert_in_delta(0, call_info.self_time, 0.01)
+    assert_in_delta(3, call_info.children_time, 0.01)
+    assert_equal(3, call_info.called)
+  end
+end
\ No newline at end of file
diff --git a/test/basic_test.rb b/test/basic_test.rb
new file mode 100755
index 0000000..e3d19cf
--- /dev/null
+++ b/test/basic_test.rb
@@ -0,0 +1,283 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'ruby-prof'
+
+class C1
+  def C1.hello
+    sleep(0.1)
+  end
+  
+  def hello
+    sleep(0.2)
+  end
+end
+
+module M1
+  def hello
+    sleep(0.3)
+  end
+end
+
+class C2
+  include M1
+  extend M1
+end
+
+class C3
+  def hello
+    sleep(0.4)
+  end
+end
+
+module M4
+  def hello
+    sleep(0.5)
+  end
+end
+
+module M5
+  include M4
+  def goodbye
+    hello
+  end
+end
+
+class C6
+  include M5
+  def test
+    goodbye
+  end
+end
+
+class BasicTest < Test::Unit::TestCase
+  def setup
+    # Need to use wall time for this test due to the sleep calls
+    RubyProf::measure_mode = RubyProf::WALL_TIME
+  end
+
+  def test_running
+    assert(!RubyProf.running?)
+    RubyProf.start
+    assert(RubyProf.running?)
+    RubyProf.stop
+    assert(!RubyProf.running?)
+  end
+
+  def test_double_profile
+    RubyProf.start
+    assert_raise(RuntimeError) do
+      RubyProf.start
+    end
+
+    assert_raise(RuntimeError) do
+      RubyProf.profile do
+        puts 1
+      end
+    end
+    RubyProf.stop
+  end
+
+  def test_no_block
+    assert_raise(ArgumentError) do
+      RubyProf.profile
+    end
+  end
+
+  def test_class_methods
+    result = RubyProf.profile do
+      C1.hello
+    end
+
+    # Length should be 3:
+    #   BasicTest#test_class_methods
+    #   <Class::C1>#hello
+    #   Kernel#sleep
+
+    methods = result.threads.values.first.sort.reverse
+    assert_equal(3, methods.length)
+
+    # Check the names
+    assert_equal('BasicTest#test_class_methods', methods[0].full_name)
+    assert_equal('<Class::C1>#hello', methods[1].full_name)
+    assert_equal('Kernel#sleep', methods[2].full_name)
+
+    # Check times
+    assert_in_delta(0.1, methods[0].total_time, 0.01)
+    assert_in_delta(0, methods[0].wait_time, 0.01)
+    assert_in_delta(0, methods[0].self_time, 0.01)
+
+    assert_in_delta(0.1, methods[1].total_time, 0.01)
+    assert_in_delta(0, methods[1].wait_time, 0.01)
+    assert_in_delta(0, methods[1].self_time, 0.01)
+
+    assert_in_delta(0.1, methods[2].total_time, 0.01)
+    assert_in_delta(0, methods[2].wait_time, 0.01)
+    assert_in_delta(0.1, methods[2].self_time, 0.01)
+  end
+
+  def test_instance_methods
+    result = RubyProf.profile do
+      C1.new.hello
+    end
+
+    # Methods called
+    #   BasicTest#test_instance_methods
+    #   Class.new
+    #   Class:Object#allocate
+    #   for Object#initialize
+    #   C1#hello
+    #   Kernel#sleep
+
+    methods = result.threads.values.first.sort.reverse
+    assert_equal(6, methods.length)
+
+    assert_equal('BasicTest#test_instance_methods', methods[0].full_name)
+    assert_equal('C1#hello', methods[1].full_name)
+    assert_equal('Kernel#sleep', methods[2].full_name)
+    assert_equal('Class#new', methods[3].full_name)
+    assert_equal('<Class::Object>#allocate', methods[4].full_name)
+    assert_equal('Object#initialize', methods[5].full_name)
+
+    # Check times
+    assert_in_delta(0.2, methods[0].total_time, 0.01)
+    assert_in_delta(0, methods[0].wait_time, 0.01)
+    assert_in_delta(0, methods[0].self_time, 0.01)
+
+    assert_in_delta(0.2, methods[1].total_time, 0.01)
+    assert_in_delta(0, methods[1].wait_time, 0.01)
+    assert_in_delta(0, methods[1].self_time, 0.01)
+
+    assert_in_delta(0.2, methods[2].total_time, 0.01)
+    assert_in_delta(0, methods[2].wait_time, 0.01)
+    assert_in_delta(0.2, methods[2].self_time, 0.01)
+
+    assert_in_delta(0, methods[3].total_time, 0.01)
+    assert_in_delta(0, methods[3].wait_time, 0.01)
+    assert_in_delta(0, methods[3].self_time, 0.01)
+
+    assert_in_delta(0, methods[4].total_time, 0.01)
+    assert_in_delta(0, methods[4].wait_time, 0.01)
+    assert_in_delta(0, methods[4].self_time, 0.01)
+
+    assert_in_delta(0, methods[5].total_time, 0.01)
+    assert_in_delta(0, methods[5].wait_time, 0.01)
+    assert_in_delta(0, methods[5].self_time, 0.01)
+  end
+
+  def test_module_methods
+    result = RubyProf.profile do
+      C2.hello
+    end
+
+    # Methods:
+    #   BasicTest#test_module_methods
+    #   M1#hello
+    #   Kernel#sleep
+
+    methods = result.threads.values.first.sort.reverse
+    assert_equal(3, methods.length)
+
+    assert_equal('BasicTest#test_module_methods', methods[0].full_name)
+    assert_equal('M1#hello', methods[1].full_name)
+    assert_equal('Kernel#sleep', methods[2].full_name)
+
+    # Check times
+    assert_in_delta(0.3, methods[0].total_time, 0.01)
+    assert_in_delta(0, methods[0].wait_time, 0.01)
+    assert_in_delta(0, methods[0].self_time, 0.01)
+
+    assert_in_delta(0.3, methods[1].total_time, 0.01)
+    assert_in_delta(0, methods[1].wait_time, 0.01)
+    assert_in_delta(0, methods[1].self_time, 0.01)
+
+    assert_in_delta(0.3, methods[2].total_time, 0.01)
+    assert_in_delta(0, methods[2].wait_time, 0.01)
+    assert_in_delta(0.3, methods[2].self_time, 0.01)
+  end
+
+  def test_module_instance_methods
+    result = RubyProf.profile do
+      C2.new.hello
+    end
+
+    # Methods:
+    #   BasicTest#test_module_instance_methods
+    #   Class#new
+    #   <Class::Object>#allocate
+    #   Object#initialize
+    #   M1#hello
+    #   Kernel#sleep
+
+    methods = result.threads.values.first.sort.reverse
+    assert_equal(6, methods.length)
+
+    assert_equal('BasicTest#test_module_instance_methods', methods[0].full_name)
+    assert_equal('M1#hello', methods[1].full_name)
+    assert_equal('Kernel#sleep', methods[2].full_name)
+    assert_equal('Class#new', methods[3].full_name)
+    assert_equal('<Class::Object>#allocate', methods[4].full_name)
+    assert_equal('Object#initialize', methods[5].full_name)
+
+    # Check times
+    assert_in_delta(0.3, methods[0].total_time, 0.01)
+    assert_in_delta(0, methods[0].wait_time, 0.01)
+    assert_in_delta(0, methods[0].self_time, 0.01)
+
+    assert_in_delta(0.3, methods[1].total_time, 0.01)
+    assert_in_delta(0, methods[1].wait_time, 0.01)
+    assert_in_delta(0, methods[1].self_time, 0.01)
+
+    assert_in_delta(0.3, methods[2].total_time, 0.01)
+    assert_in_delta(0, methods[2].wait_time, 0.01)
+    assert_in_delta(0.3, methods[2].self_time, 0.01)
+
+    assert_in_delta(0, methods[3].total_time, 0.01)
+    assert_in_delta(0, methods[3].wait_time, 0.01)
+    assert_in_delta(0, methods[3].self_time, 0.01)
+
+    assert_in_delta(0, methods[4].total_time, 0.01)
+    assert_in_delta(0, methods[4].wait_time, 0.01)
+    assert_in_delta(0, methods[4].self_time, 0.01)
+
+    assert_in_delta(0, methods[5].total_time, 0.01)
+    assert_in_delta(0, methods[5].wait_time, 0.01)
+    assert_in_delta(0, methods[5].self_time, 0.01)
+  end
+
+  def test_singleton
+    c3 = C3.new
+
+    class << c3
+      def hello
+      end
+    end
+
+    result = RubyProf.profile do
+      c3.hello
+    end
+
+    methods = result.threads.values.first.sort.reverse
+    assert_equal(2, methods.length)
+
+    assert_equal('BasicTest#test_singleton', methods[0].full_name)
+    assert_equal('<Object::C3>#hello', methods[1].full_name)
+
+    assert_in_delta(0, methods[0].total_time, 0.01)
+    assert_in_delta(0, methods[0].wait_time, 0.01)
+    assert_in_delta(0, methods[0].self_time, 0.01)
+
+    assert_in_delta(0, methods[1].total_time, 0.01)
+    assert_in_delta(0, methods[1].wait_time, 0.01)
+    assert_in_delta(0, methods[1].self_time, 0.01)
+  end
+
+  def test_traceback
+    RubyProf.start
+    assert_raise(NoMethodError) do
+      RubyProf.xxx
+    end
+
+    RubyProf.stop
+  end
+end
\ No newline at end of file
diff --git a/test/duplicate_names_test.rb b/test/duplicate_names_test.rb
new file mode 100755
index 0000000..172ff3f
--- /dev/null
+++ b/test/duplicate_names_test.rb
@@ -0,0 +1,32 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'ruby-prof'
+
+class DuplicateNames < Test::Unit::TestCase
+  def test_names
+    result = RubyProf::profile do
+      str = %{module Foo; class Bar; def foo; end end end}
+
+      eval str
+      Foo::Bar.new.foo
+      DuplicateNames.class_eval {remove_const :Foo}
+
+      eval str
+      Foo::Bar.new.foo
+      DuplicateNames.class_eval {remove_const :Foo}
+
+      eval str
+      Foo::Bar.new.foo
+    end
+    
+    # There should be 3 foo methods
+    methods = result.threads.values.first.sort.reverse
+    
+    methods = methods.select do |method|
+      method.full_name == 'DuplicateNames::Foo::Bar#foo'
+    end
+
+    assert_equal(3, methods.length)
+  end
+end
diff --git a/test/exceptions_test.rb b/test/exceptions_test.rb
new file mode 100755
index 0000000..5116c8b
--- /dev/null
+++ b/test/exceptions_test.rb
@@ -0,0 +1,15 @@
+#!/usr/bin/env ruby
+require 'test/unit'
+require 'ruby-prof'
+
+class ExceptionsTest < Test::Unit::TestCase
+  def test_profile
+    result = begin
+      RubyProf.profile do 
+        raise(RuntimeError, 'Test error')
+      end
+    rescue => e
+    end    
+    assert_not_nil(result)
+  end
+end
diff --git a/test/exclude_threads_test.rb b/test/exclude_threads_test.rb
new file mode 100755
index 0000000..4c2fdb3
--- /dev/null
+++ b/test/exclude_threads_test.rb
@@ -0,0 +1,54 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'ruby-prof'
+
+
+# --  Tests ----
+class ExcludeThreadsTest < Test::Unit::TestCase
+  def test_exclude_threads
+
+    def thread1_proc
+      sleep(0.5)
+      sleep(2)
+    end
+
+    def thread2_proc
+      sleep(0.5)
+      sleep(2)
+    end
+        
+    thread1 = Thread.new do 
+      thread1_proc
+    end
+
+    thread2 = Thread.new do
+      thread2_proc
+    end
+
+    RubyProf::exclude_threads = [ thread2 ]
+ 
+    RubyProf.start
+
+    thread1.join
+    thread2.join
+
+    result = RubyProf.stop
+
+    RubyProf::exclude_threads = nil
+
+    assert_equal(2, result.threads.length)
+
+    output = Array.new
+    result.threads.each do | thread_id, methods |
+      methods.each do | m |
+        if m.full_name.index("ExcludeThreadsTest#thread") == 0
+          output.push(m.full_name)
+        end
+      end
+    end
+
+    assert_equal(1, output.length)
+    assert_equal("ExcludeThreadsTest#thread1_proc", output[0])
+  end
+end
\ No newline at end of file
diff --git a/test/line_number_test.rb b/test/line_number_test.rb
new file mode 100755
index 0000000..8742f44
--- /dev/null
+++ b/test/line_number_test.rb
@@ -0,0 +1,73 @@
+#!/usr/bin/env ruby
+require 'test/unit'
+require 'ruby-prof'
+require 'prime'
+
+class LineNumbers
+  def method1
+    a = 3
+  end
+  
+  def method2
+    a = 3
+    method1
+  end
+  
+  def method3
+    sleep(1)
+  end
+end
+
+# --  Tests ----
+class LineNumbersTest < Test::Unit::TestCase
+  def test_function_line_no
+    numbers = LineNumbers.new
+    
+    result = RubyProf.profile do
+      numbers.method2
+    end
+
+    methods = result.threads.values.first.sort.reverse
+    assert_equal(3, methods.length)
+    
+    method = methods[0]
+    assert_equal('LineNumbersTest#test_function_line_no', method.full_name)
+    assert_equal(27, method.line)
+    
+    method = methods[1]
+    assert_equal('LineNumbers#method2', method.full_name)
+    assert_equal(11, method.line)
+    
+    method = methods[2]
+    assert_equal('LineNumbers#method1', method.full_name)
+    assert_equal(7, method.line)
+  end
+  
+  def test_c_function
+    numbers = LineNumbers.new
+    
+    result = RubyProf.profile do
+      numbers.method3
+    end
+
+    methods = result.threads.values.first.sort_by {|method| method.full_name}
+    assert_equal(3, methods.length)
+
+    # Methods:
+    #   LineNumbers#method3
+    #   LineNumbersTest#test_c_function
+    #   Kernel#sleep
+
+    method = methods[0]
+    assert_equal('Kernel#sleep', method.full_name)
+    assert_equal(0, method.line)
+    
+    method = methods[1]
+    assert_equal('LineNumbers#method3', method.full_name)
+    assert_equal(16, method.line)
+    
+    method = methods[2]
+    assert_equal('LineNumbersTest#test_c_function', method.full_name)
+    assert_equal(50, method.line)
+  end
+end
\ No newline at end of file
diff --git a/test/measurement_test.rb b/test/measurement_test.rb
new file mode 100755
index 0000000..f1df249
--- /dev/null
+++ b/test/measurement_test.rb
@@ -0,0 +1,121 @@
+#!/usr/bin/env ruby
+require 'test/unit'
+require 'ruby-prof'
+
+class MeasurementTest < Test::Unit::TestCase
+  def setup
+    GC.enable_stats if GC.respond_to?(:enable_stats)
+  end
+
+  def teardown
+    GC.disable_stats if GC.respond_to?(:disable_stats)
+  end
+
+  def test_process_time_mode
+    RubyProf::measure_mode = RubyProf::PROCESS_TIME
+    assert_equal(RubyProf::PROCESS_TIME, RubyProf::measure_mode)
+  end
+
+  def test_process_time
+    t = RubyProf.measure_process_time
+    assert_kind_of(Float, t)
+
+    u = RubyProf.measure_process_time
+    assert(u >= t, [t, u].inspect)
+  end
+
+  def test_wall_time_mode
+    RubyProf::measure_mode = RubyProf::WALL_TIME
+    assert_equal(RubyProf::WALL_TIME, RubyProf::measure_mode)
+  end
+
+  def test_wall_time
+    t = RubyProf.measure_wall_time
+    assert_kind_of Float, t
+
+    u = RubyProf.measure_wall_time
+    assert u >= t, [t, u].inspect
+  end
+
+  if RubyProf::CPU_TIME
+    def test_cpu_time_mode
+      RubyProf::measure_mode = RubyProf::CPU_TIME
+      assert_equal(RubyProf::CPU_TIME, RubyProf::measure_mode)
+    end
+    
+    def test_cpu_time
+      RubyProf.cpu_frequency = 2.33e9
+
+      t = RubyProf.measure_cpu_time
+      assert_kind_of Float, t
+
+      u = RubyProf.measure_cpu_time
+      assert u > t, [t, u].inspect
+    end
+  end
+
+  if RubyProf::ALLOCATIONS
+    def test_allocations_mode
+      RubyProf::measure_mode = RubyProf::ALLOCATIONS
+      assert_equal(RubyProf::ALLOCATIONS, RubyProf::measure_mode)
+    end
+
+    def test_allocations
+      t = RubyProf.measure_allocations
+      assert_kind_of Integer, t
+
+      u = RubyProf.measure_allocations
+      assert u > t, [t, u].inspect
+    end
+  end
+
+  if RubyProf::MEMORY
+    def test_memory_mode
+      RubyProf::measure_mode = RubyProf::MEMORY
+      assert_equal(RubyProf::MEMORY, RubyProf::measure_mode)
+    end
+
+    def test_memory
+      t = RubyProf.measure_memory
+      assert_kind_of Integer, t
+
+      u = RubyProf.measure_memory
+      assert(u >= t, [t, u].inspect)
+
+      result = RubyProf.profile {Array.new}
+      total = result.threads.values.first.methods.inject(0) { |sum, m| sum + m.total_time }
+
+      assert(total > 0, 'Should measure more than zero kilobytes of memory usage')
+      assert_not_equal(0, total % 1, 'Should not truncate fractional kilobyte measurements')
+    end
+  end
+
+  if RubyProf::GC_RUNS
+    def test_gc_runs_mode
+      RubyProf::measure_mode = RubyProf::GC_RUNS
+      assert_equal(RubyProf::GC_RUNS, RubyProf::measure_mode)
+    end
+
+    def test_gc_runs
+      t = RubyProf.measure_gc_runs
+      assert_kind_of Integer, t
+
+      GC.start
+
+      u = RubyProf.measure_gc_runs
+      assert u > t, [t, u].inspect
+    end
+  end
+
+  if RubyProf::GC_TIME
+    def test_gc_time
+      t = RubyProf.measure_gc_time
+      assert_kind_of Integer, t
+
+      GC.start
+
+      u = RubyProf.measure_gc_time
+      assert u > t, [t, u].inspect
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/module_test.rb b/test/module_test.rb
new file mode 100755
index 0000000..ff94bf9
--- /dev/null
+++ b/test/module_test.rb
@@ -0,0 +1,54 @@
+#!/usr/bin/env ruby
+require 'test/unit'
+require 'ruby-prof'
+
+# Need to use wall time for this test due to the sleep calls
+RubyProf::measure_mode = RubyProf::WALL_TIME
+
+module Foo
+  def Foo::hello
+    sleep(0.5)
+  end
+end
+
+module Bar
+  def Bar::hello
+    sleep(0.5)
+    Foo::hello
+  end
+  
+  def hello
+    sleep(0.5)
+    Bar::hello
+  end
+end
+
+include Bar
+
+class ModuleTest < Test::Unit::TestCase
+  def test_nested_modules
+    result = RubyProf.profile do
+      hello
+    end
+
+    methods = result.threads.values.first.sort.reverse
+      
+    # Length should be 5
+    assert_equal(5, methods.length)
+    
+    method = methods[0]
+    assert_equal('ModuleTest#test_nested_modules', method.full_name)
+    
+    method = methods[1]
+    assert_equal('Bar#hello', method.full_name)
+
+    method = methods[2]
+    assert_equal('Kernel#sleep', method.full_name)
+    
+    method = methods[3]
+    assert_equal('<Module::Bar>#hello', method.full_name)
+    
+    method = methods[4]
+    assert_equal('<Module::Foo>#hello', method.full_name)
+  end 
+end
diff --git a/test/no_method_class_test.rb b/test/no_method_class_test.rb
new file mode 100755
index 0000000..9273dd6
--- /dev/null
+++ b/test/no_method_class_test.rb
@@ -0,0 +1,13 @@
+#!/usr/bin/env ruby
+require 'ruby-prof'
+
+# Make sure this works with no class or method
+result = RubyProf.profile do 
+  sleep 1
+end
+
+methods = result.threads.values.first
+global_method = methods.sort_by {|method| method.full_name}.first
+if global_method.full_name != 'Global#[No method]'
+  raise(RuntimeError, "Wrong method name.  Expected: Global#[No method].  Actual: #{global_method.full_name}")
+end
\ No newline at end of file
diff --git a/test/prime.rb b/test/prime.rb
new file mode 100644
index 0000000..5ef16b6
--- /dev/null
+++ b/test/prime.rb
@@ -0,0 +1,58 @@
+# A silly little test program that finds prime numbers.  It
+# is intentionally badly designed to show off the use
+# of ruby-prof.
+# 
+# Source from http://people.cs.uchicago.edu/~bomb154/154/maclabs/profilers-lab/
+
+def make_random_array(length, maxnum)
+  result = Array.new(length)
+  result.each_index do |i|
+    result[i] = rand(maxnum)
+  end
+    
+  result
+end
+ 
+def is_prime(x)
+  y = 2
+  y.upto(x-1) do |i|
+    return false if (x % i) == 0
+  end
+  true
+end
+
+def find_primes(arr)
+  result = arr.select do |value|
+    is_prime(value)
+  end
+  result
+end
+
+def find_largest(primes)
+  largest = primes.first
+
+  # Intentionally use upto for example purposes
+  # (upto is also called from is_prime)
+  0.upto(primes.length-1) do |i|
+    sleep(0.02)
+    prime = primes[i]
+    if prime > largest
+      largest = prime
+    end
+  end
+  largest
+end
+
+def run_primes
+  length = 10
+  maxnum = 10000
+  
+  # Create random numbers
+  random_array = make_random_array(length, maxnum)
+  
+  # Find the primes
+  primes = find_primes(random_array)
+  
+  # Find the largest primes
+  largest = find_largest(primes)
+end
\ No newline at end of file
diff --git a/test/prime_test.rb b/test/prime_test.rb
new file mode 100755
index 0000000..3fbc9db
--- /dev/null
+++ b/test/prime_test.rb
@@ -0,0 +1,13 @@
+#!/usr/bin/env ruby
+require 'test/unit'
+require 'ruby-prof'
+require 'prime'
+
+# --  Tests ----
+class PrimeTest< Test::Unit::TestCase
+  def test_consistency
+    result = RubyProf.profile do
+      run_primes
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/printers_test.rb b/test/printers_test.rb
new file mode 100755
index 0000000..195e61d
--- /dev/null
+++ b/test/printers_test.rb
@@ -0,0 +1,71 @@
+#!/usr/bin/env ruby
+require 'test/unit'
+require 'ruby-prof'
+require 'prime'
+
+# --  Tests ----
+class PrintersTest < Test::Unit::TestCase
+  def setup
+    RubyProf::measure_mode = RubyProf::PROCESS_TIME
+    @result = RubyProf.profile do
+      run_primes
+    end
+  end
+    
+  def test_printers
+    printer = RubyProf::FlatPrinter.new(@result)
+    printer.print(STDOUT)
+    
+    printer = RubyProf::GraphHtmlPrinter.new(@result)
+    printer.print
+    
+    printer = RubyProf::GraphPrinter.new(@result)
+    printer.print
+    
+    printer = RubyProf::CallTreePrinter.new(@result)
+    printer.print(STDOUT)
+
+    # we should get here
+    assert(true)
+  end
+
+  def test_flat_string
+    output = ''
+    
+    printer = RubyProf::FlatPrinter.new(@result)
+    assert_nothing_raised { printer.print(output) }
+    
+    assert_match(/Thread ID: -?\d+/i, output)
+    assert_match(/Total: \d+\.\d+/i, output)
+    assert_match(/Object#run_primes/i, output)
+  end
+    
+  def test_graph_html_string
+    output = ''
+    printer = RubyProf::GraphHtmlPrinter.new(@result)
+    assert_nothing_raised { printer.print(output) }
+
+    assert_match( /DTD HTML 4\.01/i, output )
+    assert_match( %r{<th>Total Time</th>}i, output )
+    assert_match( /Object#run_primes/i, output )
+  end
+    
+  def test_graph_string
+    output = ''
+    printer = RubyProf::GraphPrinter.new(@result)
+    assert_nothing_raised { printer.print(output) }
+
+    assert_match( /Thread ID: -?\d+/i, output )
+    assert_match( /Total Time: \d+\.\d+/i, output )
+    assert_match( /Object#run_primes/i, output )
+  end
+    
+  def test_call_tree_string
+    output = ''
+    printer = RubyProf::CallTreePrinter.new(@result)
+    assert_nothing_raised { printer.print(output) }
+
+    assert_match(/fn=Object::find_primes/i, output)
+    assert_match(/events: process_time/i, output)
+  end
+end
diff --git a/test/recursive_test.rb b/test/recursive_test.rb
new file mode 100755
index 0000000..56f1e0f
--- /dev/null
+++ b/test/recursive_test.rb
@@ -0,0 +1,254 @@
+#!/usr/bin/env ruby
+require 'test/unit'
+require 'ruby-prof'
+
+def simple(n)
+  sleep(1)
+  n -= 1
+  return if n == 0
+  simple(n)
+end
+
+def cycle(n)
+  sub_cycle(n)
+end
+
+def sub_cycle(n)
+  sleep(1)
+  n -= 1
+  return if n == 0
+  cycle(n)
+end
+
+
+# --  Tests ----
+class RecursiveTest < Test::Unit::TestCase
+  def setup
+    # Need to use wall time for this test due to the sleep calls
+    RubyProf::measure_mode = RubyProf::WALL_TIME
+  end
+
+  def test_simple
+    result = RubyProf.profile do
+      simple(2)
+    end
+
+    methods = result.threads.values.first.sort.reverse
+    assert_equal(6, methods.length)
+
+    method = methods[0]
+    assert_equal('RecursiveTest#test_simple', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(2, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_simple', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    method = methods[1]
+    assert_equal('Object#simple', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(2, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_simple->Object#simple', call_info.call_sequence)
+    assert_equal(4, call_info.children.length)
+
+    method = methods[2]
+    assert_equal('Kernel#sleep', method.full_name)
+    assert_equal(2, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(2, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(2, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_simple->Object#simple->Kernel#sleep', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    call_info = method.call_infos[1]
+    assert_equal('RecursiveTest#test_simple->Object#simple->Object#simple-1->Kernel#sleep', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    method = methods[3]
+    assert_equal('Object#simple-1', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(1, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(1, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_simple->Object#simple->Object#simple-1', call_info.call_sequence)
+    assert_equal(3, call_info.children.length)
+
+    method = methods[4]
+    assert_equal('Fixnum#-', method.full_name)
+    assert_equal(2, method.called)
+    assert_in_delta(0, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(2, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_simple->Object#simple->Fixnum#-', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    call_info = method.call_infos[1]
+    assert_equal('RecursiveTest#test_simple->Object#simple->Object#simple-1->Fixnum#-', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    method = methods[5]
+    assert_equal('Fixnum#==', method.full_name)
+    assert_equal(2, method.called)
+    assert_in_delta(0, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(2, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_simple->Object#simple->Fixnum#==', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    call_info = method.call_infos[1]
+    assert_equal('RecursiveTest#test_simple->Object#simple->Object#simple-1->Fixnum#==', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+  end
+  
+  def test_cycle
+    result = RubyProf.profile do
+      cycle(2)
+    end
+
+    methods = result.threads.values.first.sort.reverse
+    assert_equal(8, methods.length)
+
+    method = methods[0]
+    assert_equal('RecursiveTest#test_cycle', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(2, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_cycle', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    method = methods[1]
+    assert_equal('Object#cycle', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(2, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_cycle->Object#cycle', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    method = methods[2]
+    assert_equal('Object#sub_cycle', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(2, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_cycle->Object#cycle->Object#sub_cycle', call_info.call_sequence)
+    assert_equal(4, call_info.children.length)
+
+    method = methods[3]
+    assert_equal('Kernel#sleep', method.full_name)
+    assert_equal(2, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(2, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(2, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_cycle->Object#cycle->Object#sub_cycle->Kernel#sleep', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    call_info = method.call_infos[1]
+    assert_equal('RecursiveTest#test_cycle->Object#cycle->Object#sub_cycle->Object#cycle-1->Object#sub_cycle-1->Kernel#sleep', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    method = methods[4]
+    assert_equal('Object#cycle-1', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(1, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(1, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_cycle->Object#cycle->Object#sub_cycle->Object#cycle-1', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    method = methods[5]
+    assert_equal('Object#sub_cycle-1', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(1, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(1, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_cycle->Object#cycle->Object#sub_cycle->Object#cycle-1->Object#sub_cycle-1', call_info.call_sequence)
+    assert_equal(3, call_info.children.length)
+
+    method = methods[6]
+    assert_equal('Fixnum#-', method.full_name)
+    assert_equal(2, method.called)
+    assert_in_delta(0, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(2, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_cycle->Object#cycle->Object#sub_cycle->Fixnum#-', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    call_info = method.call_infos[1]
+    assert_equal('RecursiveTest#test_cycle->Object#cycle->Object#sub_cycle->Object#cycle-1->Object#sub_cycle-1->Fixnum#-', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    method = methods[7]
+    assert_equal('Fixnum#==', method.full_name)
+    assert_equal(2, method.called)
+    assert_in_delta(0, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(2, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('RecursiveTest#test_cycle->Object#cycle->Object#sub_cycle->Fixnum#==', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    call_info = method.call_infos[1]
+    assert_equal('RecursiveTest#test_cycle->Object#cycle->Object#sub_cycle->Object#cycle-1->Object#sub_cycle-1->Fixnum#==', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+  end
+end
\ No newline at end of file
diff --git a/test/singleton_test.rb b/test/singleton_test.rb
new file mode 100755
index 0000000..9ac4914
--- /dev/null
+++ b/test/singleton_test.rb
@@ -0,0 +1,37 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'ruby-prof'
+require 'timeout'
+
+# --  Test for bug [#5657]
+# http://rubyforge.org/tracker/index.php?func=detail&aid=5657&group_id=1814&atid=7060
+
+
+class A
+  attr_accessor :as
+  def initialize
+    @as = []
+    class << @as
+      def <<(an_a)
+        super
+      end
+    end
+  end
+
+  def <<(an_a)
+    @as << an_a
+  end
+end
+
+class SingletonTest < Test::Unit::TestCase
+  def test_singleton
+    result = RubyProf.profile do
+      a = A.new
+      a << :first_thing
+      assert_equal(1, a.as.size)
+    end
+    printer = RubyProf::FlatPrinter.new(result)
+    printer.print(STDOUT)
+  end
+end
\ No newline at end of file
diff --git a/test/stack_test.rb b/test/stack_test.rb
new file mode 100755
index 0000000..7c991fa
--- /dev/null
+++ b/test/stack_test.rb
@@ -0,0 +1,138 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'ruby-prof'
+
+# Test data
+#     A
+#    / \
+#   B   C
+#        \
+#         B
+
+class StackClass
+  def a
+    sleep 1
+    b
+    c
+  end
+
+  def b
+    sleep 2
+  end
+
+  def c
+    sleep 3
+    b
+  end
+end
+
+class StackTest < Test::Unit::TestCase
+  def setup
+    # Need to use wall time for this test due to the sleep calls
+    RubyProf::measure_mode = RubyProf::WALL_TIME
+  end
+
+  def test_call_sequence
+    c = StackClass.new
+    result = RubyProf.profile do
+      c.a
+    end
+
+    # Length should be 5:
+    #   StackTest#test_call_sequence
+    #   StackClass#a
+    #   Kernel#sleep
+    #   StackClass#c
+    #   StackClass#b
+
+    methods = result.threads.values.first.sort.reverse
+    assert_equal(5, methods.length)
+
+    # Check StackTest#test_call_sequence
+    method = methods[0]
+    assert_equal('StackTest#test_call_sequence', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(8, method.total_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(8, method.children_time, 0.01)
+    assert_equal(1, method.call_infos.length)
+
+    call_info = method.call_infos[0]
+    assert_equal('StackTest#test_call_sequence', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    # Check StackClass#a
+    method = methods[1]
+    assert_equal('StackClass#a', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(8, method.total_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(8, method.children_time, 0.01)
+    assert_equal(1, method.call_infos.length)
+
+    call_info = method.call_infos[0]
+    assert_equal('StackTest#test_call_sequence->StackClass#a', call_info.call_sequence)
+    assert_equal(3, call_info.children.length)
+    
+    # Check Kernel#sleep
+    method = methods[2]
+    assert_equal('Kernel#sleep', method.full_name)
+    assert_equal(4, method.called)
+    assert_in_delta(8, method.total_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(8, method.self_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+    assert_equal(4, method.call_infos.length)
+
+    call_info = method.call_infos[0]
+    assert_equal('StackTest#test_call_sequence->StackClass#a->Kernel#sleep', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    call_info = method.call_infos[1]
+    assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#b->Kernel#sleep', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    call_info = method.call_infos[2]
+    assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#c->Kernel#sleep', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    call_info = method.call_infos[3]
+    assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#c->StackClass#b->Kernel#sleep', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    # Check StackClass#c
+    method = methods[3]
+    assert_equal('StackClass#c', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(5, method.total_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(5, method.children_time, 0.01)
+    assert_equal(1, method.call_infos.length)
+
+    call_info = method.call_infos[0]
+    assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#c', call_info.call_sequence)
+    assert_equal(2, call_info.children.length)
+
+    # Check StackClass#b
+    method = methods[4]
+    assert_equal('StackClass#b', method.full_name)
+    assert_equal(2, method.called)
+    assert_in_delta(4, method.total_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(4, method.children_time, 0.01)
+    assert_equal(2, method.call_infos.length)
+
+    call_info = method.call_infos[0]
+    assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#b', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    call_info = method.call_infos[1]
+    assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#c->StackClass#b', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+  end
+end
\ No newline at end of file
diff --git a/test/start_stop_test.rb b/test/start_stop_test.rb
new file mode 100755
index 0000000..c5976e5
--- /dev/null
+++ b/test/start_stop_test.rb
@@ -0,0 +1,95 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'ruby-prof'
+ 
+class StartStopTest < Test::Unit::TestCase
+  def setup
+    # Need to use wall time for this test due to the sleep calls
+    RubyProf::measure_mode = RubyProf::WALL_TIME
+  end
+
+  def method1
+     RubyProf.start
+     method2
+  end
+
+  def method2
+     method3
+  end
+
+  def method3
+    sleep(2)
+    @result = RubyProf.stop
+  end
+
+  def test_different_methods
+    method1
+
+    # Ruby prof should be stopped
+    assert_equal(false, RubyProf.running?)
+
+
+    # Length should be 4:
+    #   StartStopTest#method1
+    #   StartStopTest#method2
+    #   StartStopTest#method3
+    #   Kernel#sleep
+
+    methods = @result.threads.values.first.sort.reverse
+    assert_equal(4, methods.length)
+
+    # Check StackTest#test_call_sequence
+    method = methods[0]
+    assert_equal('StartStopTest#method1', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(2, method.children_time, 0.01)
+    assert_equal(1, method.call_infos.length)
+
+    call_info = method.call_infos[0]
+    assert_equal('StartStopTest#method1', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    method = methods[1]
+    assert_equal('StartStopTest#method2', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(2, method.children_time, 0.01)
+    assert_equal(1, method.call_infos.length)
+
+    call_info = method.call_infos[0]
+    assert_equal('StartStopTest#method1->StartStopTest#method2', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    method = methods[2]
+    assert_equal('StartStopTest#method3', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(2, method.children_time, 0.01)
+    assert_equal(1, method.call_infos.length)
+
+    call_info = method.call_infos[0]
+    assert_equal('StartStopTest#method1->StartStopTest#method2->StartStopTest#method3', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    method = methods[3]
+    assert_equal('Kernel#sleep', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(2, method.total_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(2, method.self_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+    assert_equal(1, method.call_infos.length)
+
+    call_info = method.call_infos[0]
+    assert_equal('StartStopTest#method1->StartStopTest#method2->StartStopTest#method3->Kernel#sleep', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+  end
+end
\ No newline at end of file
diff --git a/test/test_suite.rb b/test/test_suite.rb
new file mode 100644
index 0000000..8bc3c01
--- /dev/null
+++ b/test/test_suite.rb
@@ -0,0 +1,23 @@
+require 'test/unit'
+
+require 'aggregate_test'
+require 'basic_test'
+require 'duplicate_names_test'
+require 'exceptions_test'
+require 'line_number_test'
+require 'measurement_test'
+require 'module_test'
+require 'no_method_class_test'
+require 'prime_test'
+require 'printers_test'
+require 'recursive_test'
+require 'singleton_test'
+require 'stack_test'
+require 'start_stop_test'
+require 'thread_test'
+require 'unique_call_path_test'
+
+# Can't use this one here cause it breaks
+# the rest of the unit tets (Ruby Prof gets
+# started twice).
+#require 'profile_unit_test'
diff --git a/test/thread_test.rb b/test/thread_test.rb
new file mode 100755
index 0000000..2af3b25
--- /dev/null
+++ b/test/thread_test.rb
@@ -0,0 +1,159 @@
+#!/usr/bin/env ruby
+require 'test/unit'
+require 'ruby-prof'
+require 'timeout'
+
+# --  Tests ----
+class ThreadTest < Test::Unit::TestCase
+  def setup
+    # Need to use wall time for this test due to the sleep calls
+    RubyProf::measure_mode = RubyProf::WALL_TIME
+  end
+
+  def test_thread_count
+    RubyProf.start
+
+    thread = Thread.new do
+      sleep(1)
+    end
+
+    thread.join
+    result = RubyProf.stop
+
+    assert_equal(2, result.threads.keys.length)
+  end
+
+  def test_thread_identity
+    RubyProf.start
+
+    thread = Thread.new do
+      sleep(1)
+    end
+
+    thread.join
+    result = RubyProf.stop
+
+    thread_ids = result.threads.keys.sort
+    threads = [Thread.current, thread].sort_by {|thread| thread.object_id}
+
+    assert_equal(threads[0].object_id, thread_ids[0])
+    assert_equal(threads[1].object_id, thread_ids[1])
+
+    assert_instance_of(Thread, ObjectSpace._id2ref(thread_ids[0]))
+    assert_equal(threads[0], ObjectSpace._id2ref(thread_ids[0]))
+
+    assert_instance_of(Thread, ObjectSpace._id2ref(thread_ids[1]))
+    assert_equal(threads[1], ObjectSpace._id2ref(thread_ids[1]))
+  end
+
+  def test_thread_timings
+    RubyProf.start
+
+    thread = Thread.new do
+      sleep(1)
+    end
+
+    thread.join
+
+    result = RubyProf.stop
+
+    # Check background thread
+    methods = result.threads[thread.object_id].sort.reverse
+    assert_equal(2, methods.length)
+
+    method = methods[0]
+    assert_equal('ThreadTest#test_thread_timings', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(1, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(1, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('ThreadTest#test_thread_timings', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    method = methods[1]
+    assert_equal('Kernel#sleep', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(1, method.total_time, 0.01)
+    assert_in_delta(1.0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('ThreadTest#test_thread_timings->Kernel#sleep', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    # Check foreground thread
+    methods = result.threads[Thread.current.object_id].sort.reverse
+    assert_equal(4, methods.length)
+    methods = methods.sort.reverse
+
+    method = methods[0]
+    assert_equal('ThreadTest#test_thread_timings', method.full_name)
+    assert_equal(0, method.called)
+    assert_in_delta(1, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(1.0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('ThreadTest#test_thread_timings', call_info.call_sequence)
+    assert_equal(2, call_info.children.length)
+
+    method = methods[1]
+    assert_equal('Thread#join', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(1, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(1.0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('ThreadTest#test_thread_timings->Thread#join', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+
+    method = methods[2]
+    assert_equal('<Class::Thread>#new', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(0, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('ThreadTest#test_thread_timings-><Class::Thread>#new', call_info.call_sequence)
+    assert_equal(1, call_info.children.length)
+
+    method = methods[3]
+    assert_equal('Thread#initialize', method.full_name)
+    assert_equal(1, method.called)
+    assert_in_delta(0, method.total_time, 0.01)
+    assert_in_delta(0, method.self_time, 0.01)
+    assert_in_delta(0, method.wait_time, 0.01)
+    assert_in_delta(0, method.children_time, 0.01)
+
+    assert_equal(1, method.call_infos.length)
+    call_info = method.call_infos[0]
+    assert_equal('ThreadTest#test_thread_timings-><Class::Thread>#new->Thread#initialize', call_info.call_sequence)
+    assert_equal(0, call_info.children.length)
+  end
+  
+  def test_thread
+    result = RubyProf.profile do
+      begin
+        status = Timeout::timeout(2) do
+          while true
+            next
+          end
+        end
+      rescue Timeout::Error
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/unique_call_path_test.rb b/test/unique_call_path_test.rb
new file mode 100755
index 0000000..d3f9a04
--- /dev/null
+++ b/test/unique_call_path_test.rb
@@ -0,0 +1,206 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'ruby-prof'
+
+class UniqueCallPath
+  def method_a(i)
+    if i==1
+      method_b
+    else
+      method_c
+    end
+  end
+
+  def method_b
+    method_c
+  end
+
+  def method_c
+    c = 3
+  end
+
+  def method_k(i)
+    method_a(i)
+  end
+end
+
+
+# --  Tests ----
+class UniqueCallPathTest < Test::Unit::TestCase
+  def test_root_method
+    unique_call_path = UniqueCallPath.new
+
+    result = RubyProf.profile do
+      unique_call_path.method_a(1)
+    end
+
+    root_methods = Array.new
+    result.threads.each do | thread_id, methods |
+      methods.each do | m |
+        if m.root?
+          root_methods.push(m)
+        end
+      end
+    end
+
+    assert_equal(1, root_methods.length)
+    assert_equal("UniqueCallPathTest#test_root_method", root_methods[0].full_name)
+  end
+
+  def test_root_children
+    unique_call_path = UniqueCallPath.new
+
+    result = RubyProf.profile do
+      unique_call_path.method_a(1)
+      unique_call_path.method_k(2)
+    end
+
+    root_methods = Array.new
+    result.threads.each do | thread_id, methods |
+      methods.each do | m |
+        if m.root?
+          root_methods.push(m)
+        end
+      end
+    end
+
+    assert_equal(1, root_methods.length)
+
+    root_children = Array.new
+    root_methods[0].children.each do | c |
+      if c.parent.target.eql?(root_methods[0])
+        root_children.push(c)
+      end
+    end
+
+    children = root_children.sort do |c1, c2|
+      c1.target.full_name <=> c2.target.full_name
+    end
+
+    assert_equal(2, children.length)
+    assert_equal("UniqueCallPath#method_a", children[0].target.full_name)
+    assert_equal("UniqueCallPath#method_k", children[1].target.full_name)
+  end
+
+  def test_children_of
+    unique_call_path = UniqueCallPath.new
+
+    result = RubyProf.profile do
+      unique_call_path.method_a(1)
+      unique_call_path.method_k(2)
+    end
+
+    root_methods = Array.new
+    result.threads.each do | thread_id, methods |
+      methods.each do | m |
+        if m.root?
+          root_methods.push(m)
+        end
+      end
+    end
+
+    assert_equal(1, root_methods.length)
+    method = root_methods[0]
+    assert_equal('UniqueCallPathTest#test_children_of', method.full_name)
+
+    call_info_a = nil
+    root_methods[0].children.each do | c |
+      if c.target.full_name == "UniqueCallPath#method_a"
+        call_info_a = c
+        break
+      end
+    end
+
+    assert !call_info_a.nil? 
+
+    children_of_a = Array.new
+
+    call_info_a.children.each do | c |
+      if c.parent.eql?(call_info_a)
+        children_of_a.push(c)
+      end
+    end
+
+    assert_equal(4, call_info_a.target.children.length)
+
+    children_of_a = children_of_a.sort do |c1, c2|
+      c1.target.full_name <=> c2.target.full_name
+    end
+
+    assert_equal(2, children_of_a.length)
+    assert_equal("Fixnum#==", children_of_a[0].target.full_name)
+    assert_equal("UniqueCallPath#method_b", children_of_a[1].target.full_name)
+  end
+
+  def test_id2ref
+    unique_call_path = UniqueCallPath.new
+
+    result = RubyProf.profile do
+      unique_call_path.method_a(1)
+    end
+
+    root_methods = Array.new
+    result.threads.each do | thread_id, methods |
+      methods.each do | m |
+        if m.root?
+          root_methods.push(m)
+        end
+      end
+    end
+
+    child = root_methods[0].children[0]
+
+    assert_not_equal(0, child.id)
+    #assert_equal(RubyProf::CallInfo.id2ref(child.id).target.full_name, child.target.full_name)
+  end
+
+  def test_unique_path
+    unique_call_path = UniqueCallPath.new
+
+    result = RubyProf.profile do
+      unique_call_path.method_a(1)
+      unique_call_path.method_k(1)
+    end
+
+    root_methods = Array.new
+    result.threads.each do | thread_id, methods |
+      methods.each do | m |
+        if m.root?
+          root_methods.push(m)
+        end
+      end
+    end
+
+    assert_equal(1, root_methods.length)
+
+    call_info_a = nil
+    root_methods[0].children.each do | c |
+      if c.target.full_name == "UniqueCallPath#method_a"
+        call_info_a = c
+        break
+      end
+    end
+
+    assert !call_info_a.nil?
+
+    children_of_a = Array.new
+    call_info_a.children.each do |c|
+      if c.parent.eql?(call_info_a)
+        children_of_a.push(c)
+      end
+    end
+
+    assert_equal(4, call_info_a.target.children.length)
+
+    children_of_a = children_of_a.sort do |c1, c2|
+      c1.target.full_name <=> c2.target.full_name
+    end
+
+    assert_equal(2, children_of_a.length)
+    assert_equal(1, children_of_a[0].called)
+    assert_equal("Fixnum#==", children_of_a[0].target.full_name)
+    assert_equal(1, children_of_a[1].called)
+    assert_equal("UniqueCallPath#method_b", children_of_a[1].target.full_name)
+  end
+end
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-prof.git



More information about the Pkg-ruby-extras-commits mailing list