ruby-prof 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +523 -0
  3. data/LICENSE +25 -0
  4. data/README.rdoc +5 -0
  5. data/Rakefile +110 -0
  6. data/bin/ruby-prof +380 -0
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/ext/ruby_prof/extconf.rb +36 -0
  9. data/ext/ruby_prof/rp_allocation.c +292 -0
  10. data/ext/ruby_prof/rp_allocation.h +31 -0
  11. data/ext/ruby_prof/rp_call_info.c +283 -0
  12. data/ext/ruby_prof/rp_call_info.h +35 -0
  13. data/ext/ruby_prof/rp_measure_allocations.c +52 -0
  14. data/ext/ruby_prof/rp_measure_memory.c +42 -0
  15. data/ext/ruby_prof/rp_measure_process_time.c +63 -0
  16. data/ext/ruby_prof/rp_measure_wall_time.c +62 -0
  17. data/ext/ruby_prof/rp_measurement.c +236 -0
  18. data/ext/ruby_prof/rp_measurement.h +49 -0
  19. data/ext/ruby_prof/rp_method.c +642 -0
  20. data/ext/ruby_prof/rp_method.h +70 -0
  21. data/ext/ruby_prof/rp_profile.c +881 -0
  22. data/ext/ruby_prof/rp_profile.h +36 -0
  23. data/ext/ruby_prof/rp_stack.c +196 -0
  24. data/ext/ruby_prof/rp_stack.h +56 -0
  25. data/ext/ruby_prof/rp_thread.c +338 -0
  26. data/ext/ruby_prof/rp_thread.h +36 -0
  27. data/ext/ruby_prof/ruby_prof.c +48 -0
  28. data/ext/ruby_prof/ruby_prof.h +17 -0
  29. data/ext/ruby_prof/vc/ruby_prof.sln +31 -0
  30. data/ext/ruby_prof/vc/ruby_prof.vcxproj +143 -0
  31. data/lib/ruby-prof.rb +53 -0
  32. data/lib/ruby-prof/assets/call_stack_printer.css.html +117 -0
  33. data/lib/ruby-prof/assets/call_stack_printer.js.html +385 -0
  34. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  35. data/lib/ruby-prof/assets/graph_printer.html.erb +356 -0
  36. data/lib/ruby-prof/call_info.rb +57 -0
  37. data/lib/ruby-prof/call_info_visitor.rb +38 -0
  38. data/lib/ruby-prof/compatibility.rb +109 -0
  39. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  40. data/lib/ruby-prof/measurement.rb +14 -0
  41. data/lib/ruby-prof/method_info.rb +90 -0
  42. data/lib/ruby-prof/printers/abstract_printer.rb +118 -0
  43. data/lib/ruby-prof/printers/call_info_printer.rb +51 -0
  44. data/lib/ruby-prof/printers/call_stack_printer.rb +269 -0
  45. data/lib/ruby-prof/printers/call_tree_printer.rb +151 -0
  46. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  47. data/lib/ruby-prof/printers/flat_printer.rb +52 -0
  48. data/lib/ruby-prof/printers/graph_html_printer.rb +64 -0
  49. data/lib/ruby-prof/printers/graph_printer.rb +114 -0
  50. data/lib/ruby-prof/printers/multi_printer.rb +127 -0
  51. data/lib/ruby-prof/profile.rb +33 -0
  52. data/lib/ruby-prof/rack.rb +171 -0
  53. data/lib/ruby-prof/task.rb +147 -0
  54. data/lib/ruby-prof/thread.rb +35 -0
  55. data/lib/ruby-prof/version.rb +3 -0
  56. data/lib/unprof.rb +10 -0
  57. data/ruby-prof.gemspec +58 -0
  58. data/test/abstract_printer_test.rb +26 -0
  59. data/test/alias_test.rb +129 -0
  60. data/test/basic_test.rb +129 -0
  61. data/test/call_info_visitor_test.rb +31 -0
  62. data/test/duplicate_names_test.rb +32 -0
  63. data/test/dynamic_method_test.rb +53 -0
  64. data/test/enumerable_test.rb +21 -0
  65. data/test/exceptions_test.rb +24 -0
  66. data/test/exclude_methods_test.rb +146 -0
  67. data/test/exclude_threads_test.rb +53 -0
  68. data/test/line_number_test.rb +161 -0
  69. data/test/marshal_test.rb +119 -0
  70. data/test/measure_allocations.rb +30 -0
  71. data/test/measure_allocations_test.rb +385 -0
  72. data/test/measure_allocations_trace_test.rb +385 -0
  73. data/test/measure_memory_trace_test.rb +756 -0
  74. data/test/measure_process_time_test.rb +849 -0
  75. data/test/measure_times.rb +54 -0
  76. data/test/measure_wall_time_test.rb +459 -0
  77. data/test/multi_printer_test.rb +71 -0
  78. data/test/no_method_class_test.rb +15 -0
  79. data/test/parser_timings.rb +24 -0
  80. data/test/pause_resume_test.rb +166 -0
  81. data/test/prime.rb +56 -0
  82. data/test/printer_call_tree_test.rb +31 -0
  83. data/test/printer_flat_test.rb +68 -0
  84. data/test/printer_graph_html_test.rb +60 -0
  85. data/test/printer_graph_test.rb +41 -0
  86. data/test/printers_test.rb +141 -0
  87. data/test/printing_recursive_graph_test.rb +81 -0
  88. data/test/rack_test.rb +157 -0
  89. data/test/recursive_test.rb +210 -0
  90. data/test/singleton_test.rb +38 -0
  91. data/test/stack_printer_test.rb +64 -0
  92. data/test/start_stop_test.rb +109 -0
  93. data/test/test_helper.rb +24 -0
  94. data/test/thread_test.rb +144 -0
  95. data/test/unique_call_path_test.rb +190 -0
  96. data/test/yarv_test.rb +56 -0
  97. metadata +189 -0
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+ require 'stringio'
6
+ require 'fileutils'
7
+ require 'tmpdir'
8
+ require_relative 'prime'
9
+
10
+ # -- Tests ----
11
+ class PrinterGraphTest < TestCase
12
+ def setup
13
+ # WALL_TIME so we can use sleep in our test and get same measurements on linux and windows
14
+ RubyProf::measure_mode = RubyProf::WALL_TIME
15
+ @result = RubyProf.profile do
16
+ run_primes(1000, 5000)
17
+ end
18
+ end
19
+
20
+ def graph_output_nth_column_values(output, n)
21
+ only_root_calls = output.split("\n").select { |line| line =~ /^ +[\d\.]+%/ }
22
+ only_root_calls.collect { |line| line.split(/ +/)[n] }
23
+ end
24
+
25
+ def assert_sorted array
26
+ array = array.map{|n| n.to_f} # allow for > 10s times to sort right, since lexographically 4.0 > 10.0
27
+ assert_equal array, array.sort.reverse, "Array #{array.inspect} is not sorted"
28
+ end
29
+
30
+ def test_graph_results_sorting
31
+ printer = RubyProf::GraphPrinter.new(@result)
32
+
33
+ sort_method_with_column_number = {:total_time => 3, :self_time => 4, :wait_time => 5, :children_time => 6}
34
+
35
+ sort_method_with_column_number.each_pair do |sort_method, n|
36
+ printer.print(output = '', :sort_method => sort_method)
37
+ times = graph_output_nth_column_values(output, n)
38
+ assert_sorted times
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+ require 'stringio'
6
+ require 'fileutils'
7
+ require 'tmpdir'
8
+ require_relative 'prime'
9
+
10
+ # -- Tests ----
11
+ class PrintersTest < TestCase
12
+ def setup
13
+ # WALL_TIME so we can use sleep in our test and get same measurements on linux and windows
14
+ RubyProf::measure_mode = RubyProf::WALL_TIME
15
+ @result = RubyProf.profile do
16
+ run_primes(1000, 5000)
17
+ end
18
+ end
19
+
20
+ def test_printers
21
+ output = ENV['SHOW_RUBY_PROF_PRINTER_OUTPUT'] == "1" ? STDOUT : StringIO.new('')
22
+
23
+ printer = RubyProf::CallInfoPrinter.new(@result)
24
+ printer.print(output)
25
+
26
+ printer = RubyProf::CallTreePrinter.new(@result)
27
+ printer.print()
28
+
29
+ printer = RubyProf::FlatPrinter.new(@result)
30
+ printer.print(output)
31
+
32
+ printer = RubyProf::GraphHtmlPrinter.new(@result)
33
+ printer.print(output)
34
+
35
+ printer = RubyProf::GraphPrinter.new(@result)
36
+ printer.print(output)
37
+ end
38
+
39
+ def test_print_to_files
40
+ output_dir = 'examples2'
41
+
42
+ if ENV['SAVE_NEW_PRINTER_EXAMPLES']
43
+ output_dir = 'examples'
44
+ end
45
+ FileUtils.mkdir_p output_dir
46
+
47
+ printer = RubyProf::DotPrinter.new(@result)
48
+ File.open("#{output_dir}/graph.dot", "w") {|f| printer.print(f)}
49
+
50
+ printer = RubyProf::CallStackPrinter.new(@result)
51
+ File.open("#{output_dir}/stack.html", "w") {|f| printer.print(f, :application => "primes")}
52
+
53
+ # printer = RubyProf::MultiPrinter.new(@result)
54
+ # printer.print(:path => "#{output_dir}", :profile => "multi", :application => "primes")
55
+ # for file in ['graph.dot', 'multi.flat.txt', 'multi.graph.html', "multi.callgrind.out.#{$$}", 'multi.stack.html', 'stack.html']
56
+ # existant_file = output_dir + '/' + file
57
+ # assert File.size(existant_file) > 0
58
+ # end
59
+ end
60
+
61
+ def test_refuses_io_objects
62
+ p = RubyProf::MultiPrinter.new(@result)
63
+ begin
64
+ p.print(STDOUT)
65
+ flunk "should have raised an ArgumentError"
66
+ rescue ArgumentError => e
67
+ assert_match(/IO/, e.to_s)
68
+ end
69
+ end
70
+
71
+ def test_refuses_non_hashes
72
+ p = RubyProf::MultiPrinter.new (@result)
73
+ begin
74
+ p.print([])
75
+ flunk "should have raised an ArgumentError"
76
+ rescue ArgumentError => e
77
+ assert_match(/hash/, e.to_s)
78
+ end
79
+ end
80
+
81
+ def test_flat_string
82
+ output = helper_test_flat_string(RubyProf::FlatPrinter)
83
+ assert_match(/prime.rb/, output)
84
+ end
85
+
86
+ def helper_test_flat_string(klass)
87
+ output = ''
88
+
89
+ printer = klass.new(@result)
90
+ printer.print(output)
91
+
92
+ assert_match(/Thread ID: -?\d+/i, output)
93
+ assert_match(/Fiber ID: -?\d+/i, output)
94
+ assert_match(/Total: \d+\.\d+/i, output)
95
+ assert_match(/Object#run_primes/i, output)
96
+ output
97
+ end
98
+
99
+ def test_graph_html_string
100
+ output = ''
101
+ printer = RubyProf::GraphHtmlPrinter.new(@result)
102
+ printer.print(output)
103
+
104
+ assert_match(/<!DOCTYPE html>/i, output)
105
+ assert_match( %r{<th>Total</th>}i, output)
106
+ assert_match(/Object#run_primes/i, output)
107
+ end
108
+
109
+ def test_graph_string
110
+ output = ''
111
+ printer = RubyProf::GraphPrinter.new(@result)
112
+ printer.print(output)
113
+
114
+ assert_match(/Thread ID: -?\d+/i, output)
115
+ assert_match(/Fiber ID: -?\d+/i, output)
116
+ assert_match(/Total Time: \d+\.\d+/i, output)
117
+ assert_match(/Object#run_primes/i, output)
118
+ end
119
+
120
+ def do_nothing
121
+ start = Time.now
122
+ while(Time.now == start)
123
+ end
124
+ end
125
+
126
+ def test_all_with_small_percentiles
127
+ result = RubyProf.profile do
128
+ sleep 2
129
+ do_nothing
130
+ end
131
+
132
+ # RubyProf::CallTreePrinter doesn't "do" a min_percent
133
+ # RubyProf::FlatPrinter only outputs if self time > percent...
134
+ for klass in [RubyProf::GraphPrinter, RubyProf::GraphHtmlPrinter]
135
+ printer = klass.new(result)
136
+ out = ''
137
+ printer.print(out, :min_percent => 0.00000001)
138
+ assert_match(/do_nothing/, out)
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+ require 'stringio'
6
+ require 'fileutils'
7
+
8
+ # --- code to be tested ---
9
+ module PRGT
10
+ extend self
11
+
12
+ def f(n)
13
+ n.times { sleep 0.1 }
14
+ end
15
+
16
+ def g(n)
17
+ n.times { sleep 0.2 }
18
+ end
19
+
20
+ def run
21
+ 2.times { f(2); g(4) }
22
+ end
23
+ end
24
+
25
+ # --- expected test output ---
26
+ =begin
27
+ Measure Mode: wall_time
28
+ Thread ID: 1307675084040
29
+ Fiber ID: 1307708787440
30
+ Total Time: 2.0939999999973224
31
+ Sort by:
32
+
33
+ %total %self total self wait child calls name
34
+ --------------------------------------------------------------------------------
35
+ 1.657 0.000 0.000 1.657 2/2 Integer#times
36
+ 79.13% 0.00% 1.657 0.000 0.000 1.657 2 PRGT#g
37
+ 1.657 0.000 0.000 1.657 2/5 Integer#times
38
+ --------------------------------------------------------------------------------
39
+ 2.094 2.094 0.000 0.000 12/12 Integer#times
40
+ 100.00% 100.00% 2.094 2.094 0.000 0.000 12 Kernel#sleep
41
+ --------------------------------------------------------------------------------
42
+ 0.437 0.000 0.000 0.437 2/2 Integer#times
43
+ 20.87% 0.00% 0.437 0.000 0.000 0.437 2 PRGT#f
44
+ 0.437 0.000 0.000 0.437 2/5 Integer#times
45
+ --------------------------------------------------------------------------------
46
+ 0.437 0.000 0.000 0.437 2/5 PRGT#f
47
+ 1.657 0.000 0.000 1.657 2/5 PRGT#g
48
+ 2.094 0.000 0.000 2.094 1/5 PRGT#run
49
+ 100.00% 0.00% 2.094 0.000 0.000 2.094 5 *Integer#times
50
+ 2.094 2.094 0.000 0.000 12/12 Kernel#sleep
51
+ 1.657 0.000 0.000 1.657 2/2 PRGT#g
52
+ 0.437 0.000 0.000 0.437 2/2 PRGT#f
53
+ --------------------------------------------------------------------------------
54
+ 2.094 0.000 0.000 2.094 1/1 PrintingRecursiveGraphTest#setup
55
+ 100.00% 0.00% 2.094 0.000 0.000 2.094 1 PRGT#run
56
+ 2.094 0.000 0.000 2.094 1/5 Integer#times
57
+ --------------------------------------------------------------------------------
58
+ 100.00% 0.00% 2.094 0.000 0.000 2.094 1 PrintingRecursiveGraphTest#setup
59
+ 2.094 0.000 0.000 2.094 1/1 PRGT#run
60
+
61
+ * indicates recursively called methods
62
+ =end
63
+
64
+ class PrintingRecursiveGraphTest < TestCase
65
+ def setup
66
+ # WALL_TIME so we can use sleep in our test and get same measurements on linux and windows
67
+ RubyProf::measure_mode = RubyProf::WALL_TIME
68
+ @result = RubyProf.profile do
69
+ PRGT.run
70
+ end
71
+ end
72
+
73
+ def test_printing_rescursive_graph
74
+ printer = RubyProf::GraphPrinter.new(@result)
75
+ buffer = ''
76
+ printer.print(StringIO.new(buffer))
77
+ puts buffer if ENV['SHOW_RUBY_PROF_PRINTER_OUTPUT'] == "1"
78
+
79
+ refute_nil(buffer)
80
+ end
81
+ end
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ class FakeRackApp
7
+ def call(env)
8
+ end
9
+ end
10
+
11
+ module Rack
12
+ class Request
13
+ def initialize(env)
14
+ if env == :fake_env
15
+ @env = {}
16
+ else
17
+ @env = env
18
+ end
19
+ end
20
+
21
+ def path
22
+ @env[:path] || '/path/to/resource.json'
23
+ end
24
+ end
25
+ end
26
+
27
+ module Rack
28
+ class RubyProf
29
+ attr_reader :_profiler
30
+
31
+ def public_delete_profiler!
32
+ delete_profiler!
33
+ end
34
+ end
35
+ end
36
+
37
+ class RackTest < TestCase
38
+ def test_create_print_path
39
+ path = Dir.mktmpdir
40
+ Dir.delete(path)
41
+
42
+ Rack::RubyProf.new(FakeRackApp.new, :path => path)
43
+
44
+ assert(Dir.exist?(path))
45
+ end
46
+
47
+ def test_create_profile_reports
48
+ path = Dir.mktmpdir
49
+
50
+ adapter = Rack::RubyProf.new(FakeRackApp.new, :path => path)
51
+
52
+ adapter.call(:fake_env)
53
+
54
+ %w(flat.txt graph.txt graph.html call_stack.html).each do |base_name|
55
+ file_path = ::File.join(path, "path-to-resource.json-#{base_name}")
56
+ assert(File.exist?(file_path))
57
+ end
58
+ end
59
+
60
+ def test_skip_paths
61
+ path = Dir.mktmpdir
62
+
63
+ adapter = Rack::RubyProf.new(FakeRackApp.new, :path => path, :skip_paths => [%r{\.json$}])
64
+
65
+ adapter.call(:fake_env)
66
+
67
+ %w(flat.txt graph.txt graph.html call_stack.html).each do |base_name|
68
+ file_path = ::File.join(path, "path-to-resource.json-#{base_name}")
69
+ assert(!File.exist?(file_path))
70
+ end
71
+ end
72
+
73
+ def test_only_paths
74
+ path = Dir.mktmpdir
75
+
76
+ adapter = Rack::RubyProf.new(FakeRackApp.new, :path => path, :only_paths => [%r{\.json$}])
77
+
78
+ adapter.call({path: '/path/to/resource.json'})
79
+
80
+ %w(flat.txt graph.txt graph.html call_stack.html).each do |base_name|
81
+ file_path = ::File.join(path, "path-to-resource.json-#{base_name}")
82
+ assert(File.exist?(file_path))
83
+ end
84
+
85
+ adapter.call({path: '/path/to/resource.html'})
86
+ %w(flat.txt graph.txt graph.html call_stack.html).each do |base_name|
87
+ file_path = ::File.join(path, "path-to-resource.html-#{base_name}")
88
+ assert(!File.exist?(file_path))
89
+ end
90
+ end
91
+
92
+ def test_allows_lazy_filename_setting
93
+ path = Dir.mktmpdir
94
+
95
+ printer = {::RubyProf::FlatPrinter => lambda { 'dynamic.txt' }}
96
+ adapter = Rack::RubyProf.new(FakeRackApp.new, :path => path, :printers => printer)
97
+
98
+ adapter.call(:fake_env)
99
+
100
+ file_path = ::File.join(path, 'path-to-resource.json-dynamic.txt')
101
+ assert(File.exist?(file_path))
102
+ end
103
+
104
+ def test_works_for_multiple_requests
105
+ path = Dir.mktmpdir
106
+
107
+ adapter = Rack::RubyProf.new(FakeRackApp.new, :path => path, :max_requests => 2)
108
+
109
+ # make a 1st request, and check that this didn't create any files
110
+ adapter.call(:fake_env)
111
+ assert(Dir["#{path}/*"].empty?)
112
+
113
+ # now a second request should create all the expected files
114
+ adapter.call(:fake_env)
115
+ %w(flat.txt graph.txt graph.html call_stack.html).each do |base_name|
116
+ file_path = ::File.join(path, "multi-requests-2-#{base_name}")
117
+ assert(File.exist?(file_path))
118
+ end
119
+
120
+ # let's clean up
121
+ FileUtils.rm_rf(Dir["#{path}/*"])
122
+
123
+ # and do the same again for the next 2 requests
124
+ adapter.call(:fake_env)
125
+ assert(Dir["#{path}/*"].empty?)
126
+
127
+ adapter.call(:fake_env)
128
+ %w(flat.txt graph.txt graph.html call_stack.html).each do |base_name|
129
+ file_path = ::File.join(path, "multi-requests-2-#{base_name}")
130
+ assert(File.exist?(file_path))
131
+ end
132
+ end
133
+
134
+ def test_tries_to_print_results_if_shut_down_before_max_requests_reached
135
+ path = Dir.mktmpdir
136
+
137
+ adapter = Rack::RubyProf.new(FakeRackApp.new, :path => path, :max_requests => 100)
138
+
139
+ # make a 1st request, and check that this didn't create any files
140
+ adapter.call(:fake_env)
141
+ assert(Dir["#{path}/*"].empty?)
142
+
143
+ adapter.public_delete_profiler!
144
+
145
+ %w(flat.txt graph.txt graph.html call_stack.html).each do |base_name|
146
+ file_path = ::File.join(path, "multi-requests-1-#{base_name}")
147
+ assert(File.exist?(file_path))
148
+ end
149
+ end
150
+
151
+ def test_it_uses_separate_profilers_if_not_aggregating_multiple_requests
152
+ adapter1 = Rack::RubyProf.new(FakeRackApp.new)
153
+ adapter2 = Rack::RubyProf.new(FakeRackApp.new)
154
+
155
+ assert(adapter1.object_id != adapter2.object_id)
156
+ end
157
+ end
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ module SimpleRecursion
7
+ # Simple recursive test
8
+ def simple(n)
9
+ sleep(1)
10
+ return if n == 0
11
+ simple(n-1)
12
+ end
13
+
14
+ # More complicated recursive test
15
+ def render_partial(i)
16
+ sleep(1)
17
+ case i
18
+ when 0
19
+ render_partial(10)
20
+ when 1
21
+ 2.times do |j|
22
+ render_partial(j + 10)
23
+ end
24
+ end
25
+ end
26
+
27
+ def render
28
+ 2.times do |i|
29
+ render_partial(i)
30
+ end
31
+ end
32
+ end
33
+
34
+ # -- Tests ----
35
+ class RecursiveTest < TestCase
36
+ def setup
37
+ # Need to use wall time for this test due to the sleep calls
38
+ RubyProf::measure_mode = RubyProf::WALL_TIME
39
+ end
40
+
41
+ include SimpleRecursion
42
+
43
+ def test_simple
44
+ result = RubyProf.profile do
45
+ simple(1)
46
+ end
47
+
48
+ methods = result.threads.first.methods.sort.reverse
49
+ assert_equal(3, methods.length)
50
+
51
+ # Method 0: RecursiveTest#test_simple
52
+ method = methods[0]
53
+ assert_equal('RecursiveTest#test_simple', method.full_name)
54
+ assert_equal(1, method.called)
55
+ refute(method.recursive?)
56
+ assert_in_delta(2, method.total_time, 0.1)
57
+ assert_in_delta(0, method.self_time, 0.01)
58
+ assert_in_delta(0, method.wait_time, 0.01)
59
+ assert_in_delta(2, method.children_time, 0.1)
60
+
61
+ assert_equal(1, method.callers.length)
62
+ call_info = method.callers[0]
63
+ assert_nil(call_info.parent)
64
+
65
+ assert_equal(1, method.callees.length)
66
+ call_info = method.callees[0]
67
+ assert_equal('SimpleRecursion#simple', call_info.target.full_name)
68
+
69
+ # Method 1: SimpleRecursion#simple
70
+ method = methods[1]
71
+ assert_equal('SimpleRecursion#simple', method.full_name)
72
+ assert_equal(2, method.called)
73
+ assert(method.recursive?)
74
+ assert_in_delta(2, method.total_time, 0.1)
75
+ assert_in_delta(0, method.self_time, 0.1)
76
+ assert_in_delta(0, method.wait_time, 0.1)
77
+ assert_in_delta(2, method.children_time, 0.1)
78
+
79
+ assert_equal(2, method.callers.length)
80
+ call_info = method.callers[0]
81
+ assert_equal('RecursiveTest#test_simple', call_info.parent.full_name)
82
+
83
+ call_info = method.callers[1]
84
+ assert_equal('SimpleRecursion#simple', call_info.parent.full_name)
85
+
86
+ assert_equal(2, method.callees.length)
87
+ call_info = method.callees[0]
88
+ assert_equal('Kernel#sleep', call_info.target.full_name)
89
+
90
+ call_info = method.callees[1]
91
+ assert_equal('SimpleRecursion#simple', call_info.target.full_name)
92
+
93
+ # Method 2: Kernel#sleep
94
+ method = methods[2]
95
+ assert_equal('Kernel#sleep', method.full_name)
96
+ assert_equal(2, method.called)
97
+ refute(method.recursive?)
98
+ assert_in_delta(2, method.total_time, 0.1)
99
+ assert_in_delta(2, method.self_time, 0.1)
100
+ assert_in_delta(0, method.wait_time, 0.1)
101
+ assert_in_delta(0, method.children_time, 0.1)
102
+
103
+ assert_equal(1, method.callers.length)
104
+ call_info = method.callers[0]
105
+ assert_equal('SimpleRecursion#simple', call_info.parent.full_name)
106
+ assert_equal(0, method.callees.length)
107
+
108
+ assert_equal(0, method.callees.length)
109
+ end
110
+
111
+ def test_cycle
112
+ result = RubyProf.profile do
113
+ render
114
+ end
115
+
116
+ methods = result.threads.first.methods.sort.reverse
117
+ assert_equal(5, methods.length)
118
+
119
+ method = methods[0]
120
+ assert_equal('RecursiveTest#test_cycle', method.full_name)
121
+ assert_equal(1, method.called)
122
+ refute(method.recursive?)
123
+ assert_in_delta(5, method.total_time, 0.1)
124
+ assert_in_delta(0, method.self_time, 0.01)
125
+ assert_in_delta(0, method.wait_time, 0.01)
126
+ assert_in_delta(5, method.children_time, 0.1)
127
+
128
+ assert_equal(1, method.callers.length)
129
+ call_info = method.callers[0]
130
+ assert_nil(call_info.parent)
131
+
132
+ assert_equal(1, method.callees.length)
133
+ call_info = method.callees[0]
134
+ assert_equal('SimpleRecursion#render', call_info.target.full_name)
135
+
136
+ method = methods[1]
137
+ assert_equal('SimpleRecursion#render', method.full_name)
138
+ assert_equal(1, method.called)
139
+ refute(method.recursive?)
140
+ assert_in_delta(5, method.total_time, 0.1)
141
+ assert_in_delta(0, method.self_time, 0.01)
142
+ assert_in_delta(0, method.wait_time, 0.01)
143
+ assert_in_delta(5, method.children_time, 0.1)
144
+
145
+ assert_equal(1, method.callers.length)
146
+ call_info = method.callers[0]
147
+ assert_equal('RecursiveTest#test_cycle', call_info.parent.full_name)
148
+
149
+ assert_equal(1, method.callees.length)
150
+ call_info = method.callees[0]
151
+ assert_equal('Integer#times', call_info.target.full_name)
152
+
153
+ method = methods[2]
154
+ assert_equal('Integer#times', method.full_name)
155
+ assert_equal(2, method.called)
156
+ assert(method.recursive?)
157
+ assert_in_delta(5, method.total_time, 0.1)
158
+ assert_in_delta(0, method.self_time, 0.1)
159
+ assert_in_delta(0, method.wait_time, 0.1)
160
+ assert_in_delta(5, method.children_time, 0.1)
161
+
162
+ assert_equal(2, method.callers.length)
163
+ call_info = method.callers[0]
164
+ assert_equal('SimpleRecursion#render', call_info.parent.full_name)
165
+
166
+ call_info = method.callers[1]
167
+ assert_equal('SimpleRecursion#render_partial', call_info.parent.full_name)
168
+
169
+ assert_equal(1, method.callees.length)
170
+ call_info = method.callees[0]
171
+ assert_equal('SimpleRecursion#render_partial', call_info.target.full_name)
172
+
173
+ method = methods[3]
174
+ assert_equal('SimpleRecursion#render_partial', method.full_name)
175
+ assert_equal(5, method.called)
176
+ assert(method.recursive?)
177
+ assert_in_delta(5, method.total_time, 0.1)
178
+ assert_in_delta(0, method.self_time, 0.1)
179
+ assert_in_delta(0, method.wait_time, 0.01)
180
+ assert_in_delta(5, method.children_time, 0.05)
181
+
182
+ assert_equal(2, method.callers.length)
183
+ call_info = method.callers[0]
184
+ assert_equal('Integer#times', call_info.parent.full_name)
185
+
186
+ call_info = method.callers[1]
187
+ assert_equal('SimpleRecursion#render_partial', call_info.parent.full_name)
188
+
189
+ assert_equal(3, method.callees.length)
190
+ call_info = method.callees[0]
191
+ assert_equal('Kernel#sleep', call_info.target.full_name)
192
+
193
+ call_info = method.callees[1]
194
+ assert_equal('SimpleRecursion#render_partial', call_info.target.full_name)
195
+
196
+ call_info = method.callees[2]
197
+ assert_equal('Integer#times', call_info.target.full_name)
198
+
199
+ method = methods[4]
200
+ assert_equal('Kernel#sleep', method.full_name)
201
+ assert_equal(5, method.called)
202
+ refute(method.recursive?)
203
+ assert_in_delta(5, method.total_time, 0.1)
204
+ assert_in_delta(5, method.self_time, 0.1)
205
+ assert_in_delta(0, method.wait_time, 0.01)
206
+ assert_in_delta(0, method.children_time, 0.01)
207
+
208
+ assert_equal(0, method.callees.length)
209
+ end
210
+ end