ruby-prof 1.1.0 → 1.2.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +12 -1
  3. data/bin/ruby-prof +100 -152
  4. data/ext/ruby_prof/rp_aggregate_call_tree.c +41 -0
  5. data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
  6. data/ext/ruby_prof/rp_allocation.c +31 -51
  7. data/ext/ruby_prof/rp_allocation.h +2 -2
  8. data/ext/ruby_prof/rp_call_tree.c +353 -0
  9. data/ext/ruby_prof/rp_call_tree.h +43 -0
  10. data/ext/ruby_prof/rp_call_trees.c +266 -0
  11. data/ext/ruby_prof/rp_call_trees.h +29 -0
  12. data/ext/ruby_prof/rp_measure_allocations.c +11 -13
  13. data/ext/ruby_prof/rp_measure_process_time.c +11 -13
  14. data/ext/ruby_prof/rp_measure_wall_time.c +17 -15
  15. data/ext/ruby_prof/rp_measurement.c +27 -36
  16. data/ext/ruby_prof/rp_measurement.h +6 -6
  17. data/ext/ruby_prof/rp_method.c +88 -248
  18. data/ext/ruby_prof/rp_method.h +12 -19
  19. data/ext/ruby_prof/rp_profile.c +277 -270
  20. data/ext/ruby_prof/rp_profile.h +0 -1
  21. data/ext/ruby_prof/rp_stack.c +113 -105
  22. data/ext/ruby_prof/rp_stack.h +15 -18
  23. data/ext/ruby_prof/rp_thread.c +115 -107
  24. data/ext/ruby_prof/rp_thread.h +9 -8
  25. data/ext/ruby_prof/ruby_prof.c +27 -23
  26. data/ext/ruby_prof/ruby_prof.h +9 -0
  27. data/ext/ruby_prof/vc/ruby_prof.vcxproj +11 -7
  28. data/lib/ruby-prof.rb +2 -3
  29. data/lib/ruby-prof/assets/call_stack_printer.html.erb +4 -7
  30. data/lib/ruby-prof/assets/graph_printer.html.erb +5 -6
  31. data/lib/ruby-prof/{call_info.rb → call_tree.rb} +6 -6
  32. data/lib/ruby-prof/call_tree_visitor.rb +36 -0
  33. data/lib/ruby-prof/measurement.rb +5 -2
  34. data/lib/ruby-prof/method_info.rb +3 -15
  35. data/lib/ruby-prof/printers/call_info_printer.rb +12 -10
  36. data/lib/ruby-prof/printers/call_stack_printer.rb +19 -22
  37. data/lib/ruby-prof/printers/call_tree_printer.rb +1 -1
  38. data/lib/ruby-prof/printers/dot_printer.rb +3 -3
  39. data/lib/ruby-prof/printers/graph_printer.rb +3 -4
  40. data/lib/ruby-prof/printers/multi_printer.rb +2 -2
  41. data/lib/ruby-prof/rack.rb +3 -0
  42. data/lib/ruby-prof/thread.rb +3 -18
  43. data/lib/ruby-prof/version.rb +1 -1
  44. data/ruby-prof.gemspec +7 -0
  45. data/test/alias_test.rb +42 -45
  46. data/test/basic_test.rb +0 -86
  47. data/test/{call_info_visitor_test.rb → call_tree_visitor_test.rb} +6 -5
  48. data/test/call_trees_test.rb +66 -0
  49. data/test/exclude_methods_test.rb +17 -12
  50. data/test/fiber_test.rb +203 -6
  51. data/test/gc_test.rb +32 -23
  52. data/test/inverse_call_tree_test.rb +175 -0
  53. data/test/line_number_test.rb +64 -67
  54. data/test/marshal_test.rb +7 -11
  55. data/test/measure_allocations_test.rb +224 -234
  56. data/test/measure_allocations_trace_test.rb +224 -234
  57. data/test/measure_memory_trace_test.rb +814 -469
  58. data/test/measure_process_time_test.rb +0 -64
  59. data/test/measure_times.rb +2 -0
  60. data/test/measure_wall_time_test.rb +34 -58
  61. data/test/pause_resume_test.rb +19 -10
  62. data/test/prime.rb +1 -3
  63. data/test/prime_script.rb +6 -0
  64. data/test/printers_test.rb +1 -1
  65. data/test/recursive_test.rb +50 -54
  66. data/test/start_stop_test.rb +19 -19
  67. data/test/test_helper.rb +3 -15
  68. data/test/thread_test.rb +11 -11
  69. data/test/unique_call_path_test.rb +25 -95
  70. metadata +19 -9
  71. data/ext/ruby_prof/rp_call_info.c +0 -271
  72. data/ext/ruby_prof/rp_call_info.h +0 -35
  73. data/lib/ruby-prof/call_info_visitor.rb +0 -38
  74. data/test/parser_timings.rb +0 -24
@@ -43,13 +43,12 @@ class ExcludeMethodsTest < TestCase
43
43
  obj = ExcludeMethodsClass.new
44
44
  prf = RubyProf::Profile.new
45
45
 
46
- result = prf.profile { 5.times {obj.a} }
46
+ result = prf.profile {obj.a}
47
47
  methods = result.threads.first.methods.sort.reverse
48
-
49
48
  assert_equal(10, methods.count)
50
49
  assert_equal('ExcludeMethodsTest#test_methods_can_be_profiled', methods[0].full_name)
51
- assert_equal('Integer#times', methods[1].full_name)
52
- assert_equal('ExcludeMethodsClass#a', methods[2].full_name)
50
+ assert_equal('ExcludeMethodsClass#a', methods[1].full_name)
51
+ assert_equal('Integer#times', methods[2].full_name)
53
52
  assert_equal('ExcludeMethodsClass#b', methods[3].full_name)
54
53
  assert_equal('<Class::ExcludeMethodsClass>#e', methods[4].full_name)
55
54
  assert_equal('<Class::ExcludeMethodsClass>#f', methods[5].full_name)
@@ -65,7 +64,7 @@ class ExcludeMethodsTest < TestCase
65
64
 
66
65
  prf.exclude_methods!(Integer, :times)
67
66
 
68
- result = prf.profile { 5.times {obj.a} }
67
+ result = prf.profile {obj.a}
69
68
  methods = result.threads.first.methods.sort.reverse
70
69
 
71
70
  assert_equal(9, methods.count)
@@ -88,7 +87,7 @@ class ExcludeMethodsTest < TestCase
88
87
  prf.exclude_methods!(ExcludeMethodsClass.singleton_class, :f)
89
88
  prf.exclude_methods!(ExcludeMethodsModule.singleton_class, :d)
90
89
 
91
- result = prf.profile { 5.times {obj.a} }
90
+ result = prf.profile {obj.a}
92
91
  methods = result.threads.first.methods.sort.reverse
93
92
 
94
93
  assert_equal(7, methods.count)
@@ -107,13 +106,19 @@ class ExcludeMethodsTest < TestCase
107
106
 
108
107
  prf.exclude_common_methods!
109
108
 
110
- result = prf.profile { 5.times {obj.a} }
109
+ result = prf.profile {obj.a}
111
110
  methods = result.threads.first.methods.sort.reverse
112
111
 
113
112
  assert_equal(9, methods.count)
114
113
  assert_equal('ExcludeMethodsTest#test_exclude_common_methods1', methods[0].full_name)
115
114
  assert_equal('ExcludeMethodsClass#a', methods[1].full_name)
116
115
  assert_equal('ExcludeMethodsClass#b', methods[2].full_name)
116
+ assert_equal('<Class::ExcludeMethodsClass>#e', methods[3].full_name)
117
+ assert_equal('<Class::ExcludeMethodsClass>#f', methods[4].full_name)
118
+ assert_equal('Kernel#sleep', methods[5].full_name)
119
+ assert_equal('ExcludeMethodsModule#c', methods[6].full_name)
120
+ assert_equal('<Module::ExcludeMethodsModule>#d', methods[7].full_name)
121
+ assert_equal('Kernel#class', methods[8].full_name)
117
122
  end
118
123
 
119
124
  def test_exclude_common_methods2
@@ -130,14 +135,14 @@ class ExcludeMethodsTest < TestCase
130
135
 
131
136
  private
132
137
 
133
- def assert_method_has_been_eliminated(result, eliminated_method)
138
+ def assert_method_has_been_excluded(result, excluded_method)
134
139
  result.threads.each do |thread|
135
140
  thread.methods.each do |method|
136
- method.call_infos.each do |ci|
137
- assert(ci.target != eliminated_method, "broken self")
138
- assert(ci.parent.target != eliminated_method, "broken parent") if ci.parent
141
+ method.call_trees.each do |ci|
142
+ assert(ci.target != excluded_method, "broken self")
143
+ assert(ci.parent.target != excluded_method, "broken parent") if ci.parent
139
144
  ci.children.each do |callee|
140
- assert(callee.target != eliminated_method, "broken kid")
145
+ assert(callee.target != excluded_method, "broken kid")
141
146
  end
142
147
  end
143
148
  end
@@ -37,6 +37,7 @@ class FiberTest < TestCase
37
37
 
38
38
  def test_fibers
39
39
  result = RubyProf.profile { fiber_test }
40
+
40
41
  profiled_fiber_ids = result.threads.map(&:fiber_id)
41
42
  assert_equal(2, result.threads.length)
42
43
  assert_equal([@thread_id], result.threads.map(&:id).uniq)
@@ -45,15 +46,211 @@ class FiberTest < TestCase
45
46
  assert profiled_fiber_ids.include?(@root_fiber)
46
47
  assert(root_fiber_profile = result.threads.detect{|t| t.fiber_id == @root_fiber})
47
48
  assert(enum_fiber_profile = result.threads.detect{|t| t.fiber_id != @root_fiber})
49
+ assert_in_delta(0.33, root_fiber_profile.total_time, 0.05)
50
+ assert_in_delta(0.33, enum_fiber_profile.total_time, 0.05)
48
51
 
49
- assert_in_delta(0.3, root_fiber_profile.total_time, 0.05)
50
- assert_in_delta(0.2, enum_fiber_profile.total_time, 0.05)
52
+ methods = result.threads[0].methods.sort.reverse
53
+ assert_equal(12, methods.count)
51
54
 
52
- assert(method_next = root_fiber_profile.methods.detect{|m| m.full_name == "Enumerator#next"})
53
- assert(method_each = enum_fiber_profile.methods.detect{|m| m.full_name == "Enumerator#each"})
55
+ method = methods[0]
56
+ assert_equal('FiberTest#test_fibers', method.full_name)
57
+ assert_equal(1, method.called)
58
+ assert_in_delta(0.33, method.total_time, 0.05)
59
+ assert_in_delta(0, method.self_time, 0.05)
60
+ assert_in_delta(0, method.wait_time, 0.05)
61
+ assert_in_delta(0.33, method.children_time, 0.05)
54
62
 
55
- assert_in_delta(0.2, method_next.total_time, 0.05)
56
- assert_in_delta(0.2, method_each.total_time, 0.05)
63
+ method = methods[1]
64
+ assert_equal('FiberTest#fiber_test', method.full_name)
65
+ assert_equal(1, method.called)
66
+ assert_in_delta(0.33, method.total_time, 0.05)
67
+ assert_in_delta(0, method.self_time, 0.05)
68
+ assert_in_delta(0, method.wait_time, 0.05)
69
+ assert_in_delta(0.33, method.children_time, 0.05)
70
+
71
+ method = methods[2]
72
+ assert_equal('Enumerator#next', method.full_name)
73
+ assert_equal(3, method.called)
74
+ assert_in_delta(0.22, method.total_time, 0.05)
75
+ assert_in_delta(0, method.self_time, 0.05)
76
+ assert_in_delta(0.22, method.wait_time, 0.05)
77
+ assert_in_delta(0, method.children_time, 0.05)
78
+
79
+ method = methods[3]
80
+ assert_equal('Kernel#sleep', method.full_name)
81
+ assert_equal(1, method.called)
82
+ assert_in_delta(0.11, method.total_time, 0.05)
83
+ assert_in_delta(0.11, method.self_time, 0.05)
84
+ assert_in_delta(0, method.wait_time, 0.05)
85
+ assert_in_delta(0, method.children_time, 0.05)
86
+
87
+ # Since these methods have such short times their order is a bit indeterminate
88
+ method = methods.detect {|method| method.full_name == 'Class#new'}
89
+ assert_equal('Class#new', method.full_name)
90
+ assert_equal(1, method.called)
91
+ assert_in_delta(0, method.total_time, 0.05)
92
+ assert_in_delta(0, method.self_time, 0.05)
93
+ assert_in_delta(0, method.wait_time, 0.05)
94
+ assert_in_delta(0, method.children_time, 0.05)
95
+
96
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
97
+ method = methods.detect {|method| method.full_name == 'Set#<<'}
98
+ assert_equal('Set#<<', method.full_name)
99
+ assert_equal(1, method.called)
100
+ assert_in_delta(0, method.total_time, 0.05)
101
+ assert_in_delta(0, method.self_time, 0.05)
102
+ assert_in_delta(0, method.wait_time, 0.05)
103
+ assert_in_delta(0, method.children_time, 0.05)
104
+ end
105
+
106
+ method = methods.detect {|method| method.full_name == 'Module#==='}
107
+ assert_equal('Module#===', method.full_name)
108
+ assert_equal(1, method.called)
109
+ assert_in_delta(0, method.total_time, 0.05)
110
+ assert_in_delta(0, method.self_time, 0.05)
111
+ assert_in_delta(0, method.wait_time, 0.05)
112
+ assert_in_delta(0, method.children_time, 0.05)
113
+
114
+ method = methods.detect {|method| method.full_name == 'Kernel#object_id'}
115
+ assert_equal('Kernel#object_id', method.full_name)
116
+ assert_equal(1, method.called)
117
+ assert_in_delta(0, method.total_time, 0.05)
118
+ assert_in_delta(0, method.self_time, 0.05)
119
+ assert_in_delta(0, method.wait_time, 0.05)
120
+ assert_in_delta(0, method.children_time, 0.05)
121
+
122
+ method = methods.detect {|method| method.full_name == '<Class::Fiber>#current'}
123
+ assert_equal('<Class::Fiber>#current', method.full_name)
124
+ assert_equal(1, method.called)
125
+ assert_in_delta(0, method.total_time, 0.05)
126
+ assert_in_delta(0, method.self_time, 0.05)
127
+ assert_in_delta(0, method.wait_time, 0.05)
128
+ assert_in_delta(0, method.children_time, 0.05)
129
+
130
+ method = methods.detect {|method| method.full_name == 'Exception#exception'}
131
+ assert_equal('Exception#exception', method.full_name)
132
+ assert_equal(1, method.called)
133
+ assert_in_delta(0, method.total_time, 0.05)
134
+ assert_in_delta(0, method.self_time, 0.05)
135
+ assert_in_delta(0, method.wait_time, 0.05)
136
+ assert_in_delta(0, method.children_time, 0.05)
137
+
138
+ method = methods.detect {|method| method.full_name == 'Exception#backtrace'}
139
+ assert_equal('Exception#backtrace', method.full_name)
140
+ assert_equal(1, method.called)
141
+ assert_in_delta(0, method.total_time, 0.05)
142
+ assert_in_delta(0, method.self_time, 0.05)
143
+ assert_in_delta(0, method.wait_time, 0.05)
144
+ assert_in_delta(0, method.children_time, 0.05)
145
+
146
+ method = methods.detect {|method| method.full_name == 'Enumerator#initialize'}
147
+ assert_equal('Enumerator#initialize', method.full_name)
148
+ assert_equal(1, method.called)
149
+ assert_in_delta(0, method.total_time, 0.05)
150
+ assert_in_delta(0, method.self_time, 0.05)
151
+ assert_in_delta(0, method.wait_time, 0.05)
152
+ assert_in_delta(0, method.children_time, 0.05)
153
+
154
+ methods = result.threads[1].methods.sort.reverse
155
+
156
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6.0')
157
+ assert_equal(10, methods.count)
158
+ else
159
+ assert_equal(11, methods.count)
160
+ end
161
+
162
+ method = methods[0]
163
+ assert_equal('RubyProf::Profile#_inserted_parent_', method.full_name)
164
+ assert_equal(1, method.called)
165
+ assert_in_delta(0.33, method.total_time, 0.05)
166
+ assert_in_delta(0, method.self_time, 0.05)
167
+ assert_in_delta(0.11, method.wait_time, 0.05)
168
+ assert_in_delta(0.22, method.children_time, 0.05)
169
+
170
+ method = methods[1]
171
+ assert_equal('Enumerator#each', method.full_name)
172
+ assert_equal(1, method.called)
173
+ assert_in_delta(0.22, method.total_time, 0.05)
174
+ assert_in_delta(0, method.self_time, 0.05)
175
+ assert_in_delta(0, method.wait_time, 0.05)
176
+ assert_in_delta(0.22, method.children_time, 0.05)
177
+
178
+ method = methods[2]
179
+ assert_equal('Enumerator::Generator#each', method.full_name)
180
+ assert_equal(1, method.called)
181
+ assert_in_delta(0.22, method.total_time, 0.05)
182
+ assert_in_delta(0, method.self_time, 0.05)
183
+ assert_in_delta(0, method.wait_time, 0.05)
184
+ assert_in_delta(0.22, method.children_time, 0.05)
185
+
186
+ method = methods[3]
187
+ assert_equal('Array#each', method.full_name)
188
+ assert_equal(1, method.called)
189
+ assert_in_delta(0.22, method.total_time, 0.05)
190
+ assert_in_delta(0, method.self_time, 0.05)
191
+ assert_in_delta(0, method.wait_time, 0.05)
192
+ assert_in_delta(0.22, method.children_time, 0.05)
193
+
194
+ method = methods[4]
195
+ assert_equal('Kernel#sleep', method.full_name)
196
+ assert_equal(2, method.called)
197
+ assert_in_delta(0.22, method.total_time, 0.05)
198
+ assert_in_delta(0.22, method.self_time, 0.05)
199
+ assert_in_delta(0, method.wait_time, 0.05)
200
+ assert_in_delta(0, method.children_time, 0.05)
201
+
202
+ # Since these methods have such short times their order is a bit indeterminate
203
+ method = methods.detect {|method| method.full_name == 'Exception#initialize'}
204
+ assert_equal('Exception#initialize', method.full_name)
205
+ assert_equal(1, method.called)
206
+ assert_in_delta(0, method.total_time, 0.05)
207
+ assert_in_delta(0, method.self_time, 0.05)
208
+ assert_in_delta(0, method.wait_time, 0.05)
209
+ assert_in_delta(0, method.children_time, 0.05)
210
+
211
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
212
+ method = methods.detect {|method| method.full_name == 'Set#<<'}
213
+ assert_equal('Set#<<', method.full_name)
214
+ assert_equal(2, method.called)
215
+ assert_in_delta(0, method.total_time, 0.05)
216
+ assert_in_delta(0, method.self_time, 0.05)
217
+ assert_in_delta(0, method.wait_time, 0.05)
218
+ assert_in_delta(0, method.children_time, 0.05)
219
+ end
220
+
221
+ method = methods.detect {|method| method.full_name == 'Kernel#object_id'}
222
+ assert_equal('Kernel#object_id', method.full_name)
223
+ assert_equal(2, method.called)
224
+ assert_in_delta(0, method.total_time, 0.05)
225
+ assert_in_delta(0, method.self_time, 0.05)
226
+ assert_in_delta(0, method.wait_time, 0.05)
227
+ assert_in_delta(0, method.children_time, 0.05)
228
+
229
+ method = methods.detect {|method| method.full_name == 'Enumerator::Yielder#yield'}
230
+ assert_equal('Enumerator::Yielder#yield', method.full_name)
231
+ assert_equal(2, method.called)
232
+ assert_in_delta(0, method.total_time, 0.05)
233
+ assert_in_delta(0, method.self_time, 0.05)
234
+ assert_in_delta(0, method.wait_time, 0.05)
235
+ assert_in_delta(0, method.children_time, 0.05)
236
+
237
+ method = methods.detect {|method| method.full_name == '<Class::Fiber>#current'}
238
+ assert_equal('<Class::Fiber>#current', method.full_name)
239
+ assert_equal(2, method.called)
240
+ assert_in_delta(0, method.total_time, 0.05)
241
+ assert_in_delta(0, method.self_time, 0.05)
242
+ assert_in_delta(0, method.wait_time, 0.05)
243
+ assert_in_delta(0, method.children_time, 0.05)
244
+
245
+ #if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7.0')
246
+ # method = methods.detect {|method| method.full_name == 'Numeric#eql?'}
247
+ # assert_equal('Numeric#eql?', method.full_name)
248
+ # assert_equal(1, method.called)
249
+ # assert_in_delta(0, method.total_time, 0.05)
250
+ # assert_in_delta(0, method.self_time, 0.05)
251
+ # assert_in_delta(0, method.wait_time, 0.05)
252
+ # assert_in_delta(0, method.children_time, 0.05)
253
+ #end
57
254
  end
58
255
 
59
256
  def test_merged_fibers
@@ -4,6 +4,14 @@
4
4
  require File.expand_path('../test_helper', __FILE__)
5
5
 
6
6
  class GcTest < TestCase
7
+ def setup
8
+ GC.stress = true
9
+ end
10
+
11
+ def teardown
12
+ GC.stress = false
13
+ end
14
+
7
15
  def some_method
8
16
  Array.new(3 * 4)
9
17
  end
@@ -15,9 +23,8 @@ class GcTest < TestCase
15
23
  end
16
24
 
17
25
  def test_hold_onto_thread
18
- threads = 1000.times.reduce(Array.new) do |array, i|
26
+ threads = 5.times.reduce(Array.new) do |array, i|
19
27
  array.concat(run_profile.threads)
20
- GC.start
21
28
  array
22
29
  end
23
30
 
@@ -30,48 +37,49 @@ class GcTest < TestCase
30
37
  assert(true)
31
38
  end
32
39
 
33
- def test_hold_onto_method
34
- methods = 1000.times.reduce(Array.new) do |array, i|
35
- array.concat(run_profile.threads.map(&:methods).flatten)
36
- GC.start
40
+ def test_hold_onto_root_call_info
41
+ call_trees = 5.times.reduce(Array.new) do |array, i|
42
+ array.concat(run_profile.threads.map(&:call_tree))
37
43
  array
38
44
  end
39
45
 
40
- methods.each do |method|
46
+ call_trees.each do |call_tree|
41
47
  error = assert_raises(RuntimeError) do
42
- method.method_name
48
+ call_tree.source_file
43
49
  end
44
50
  assert_match(/has already been freed/, error.message)
45
51
  end
46
52
  assert(true)
47
53
  end
48
54
 
49
- def test_hold_onto_parent_callers
50
- call_infos = 1000.times.reduce(Array.new) do |array, i|
51
- array.concat(run_profile.threads.map(&:methods).flatten.map(&:callers).flatten)
52
- GC.start
55
+ def test_hold_onto_method
56
+ methods = 5.times.reduce(Array.new) do |array, i|
57
+ profile = run_profile
58
+ methods_2 = profile.threads.map(&:methods).flatten
59
+ array.concat(methods_2)
53
60
  array
54
61
  end
55
62
 
56
- call_infos.each do |call_info|
63
+ methods.each do |method|
57
64
  error = assert_raises(RuntimeError) do
58
- call_info.source_file
65
+ method.method_name
59
66
  end
60
67
  assert_match(/has already been freed/, error.message)
61
68
  end
62
69
  assert(true)
63
70
  end
64
71
 
65
- def test_hold_onto_parent_callees
66
- call_infos = 1000.times.reduce(Array.new) do |array, i|
67
- array.concat(run_profile.threads.map(&:methods).flatten.map(&:callees).flatten)
68
- GC.start
72
+ def test_hold_onto_call_trees
73
+ method_call_infos = 5.times.reduce(Array.new) do |array, i|
74
+ profile = run_profile
75
+ call_trees = profile.threads.map(&:methods).flatten.map(&:call_trees).flatten
76
+ array.concat(call_trees)
69
77
  array
70
78
  end
71
79
 
72
- call_infos.each do |call_info|
80
+ method_call_infos.each do |call_trees|
73
81
  error = assert_raises(RuntimeError) do
74
- call_info.source_file
82
+ call_trees.call_trees
75
83
  end
76
84
  assert_match(/has already been freed/, error.message)
77
85
  end
@@ -79,9 +87,10 @@ class GcTest < TestCase
79
87
  end
80
88
 
81
89
  def test_hold_onto_measurements
82
- measurements = 1000.times.reduce(Array.new) do |array, i|
83
- array.concat(run_profile.threads.map(&:methods).flatten.map(&:callers).flatten.map(&:measurement))
84
- GC.start
90
+ measurements = 5.times.reduce(Array.new) do |array, i|
91
+ profile = run_profile
92
+ measurements = profile.threads.map(&:methods).flatten.map(&:measurement)
93
+ array.concat(measurements)
85
94
  array
86
95
  end
87
96
 
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ class InverseCallTreeTest < TestCase
7
+ INVERSE_DEPTH = 5
8
+
9
+ def setup
10
+ # Need to use wall time for this test due to the sleep calls
11
+ RubyProf::measure_mode = RubyProf::WALL_TIME
12
+ end
13
+
14
+ INVERSE_DEPTH.times do |i|
15
+ if i == 0
16
+ define_method("method_#{i}") do
17
+ sleep_amount = (i + 1) * 0.05
18
+ RubyProf.start
19
+ sleep(sleep_amount)
20
+ end
21
+ else
22
+ define_method("method_#{i}") do
23
+ method_name = "method_#{i-1}"
24
+ sleep_amount = (i + 1) * 0.05
25
+ self.send(method_name.to_sym)
26
+ sleep(sleep_amount)
27
+ end
28
+ end
29
+ end
30
+
31
+ def test_inverse
32
+ method_name = "method_#{INVERSE_DEPTH - 1}"
33
+ self.send(method_name.to_sym)
34
+ result = profile = RubyProf.stop
35
+
36
+ assert_equal(1, profile.threads.count)
37
+
38
+ thread = profile.threads.first
39
+ assert_in_delta(0.79, thread.total_time, 0.05)
40
+
41
+ assert_equal(7, thread.methods.length)
42
+ methods = thread.methods.sort.reverse
43
+
44
+ # InverseCallTreeTest#test_inverse
45
+ method = methods[0]
46
+ assert_equal('InverseCallTreeTest#test_inverse', method.full_name)
47
+ assert_equal(34, method.line)
48
+
49
+ assert_equal(0, method.call_trees.callers.count)
50
+
51
+ assert_equal(1, method.call_trees.callees.count)
52
+ call_tree = method.call_trees.callees[0]
53
+ assert_equal('InverseCallTreeTest#method_4', call_tree.target.full_name)
54
+ assert_equal(26, call_tree.line)
55
+
56
+ # InverseCallTreeTest#method_4
57
+ method = methods[1]
58
+ assert_equal('InverseCallTreeTest#method_4', method.full_name)
59
+ assert_equal(26, method.line)
60
+
61
+ assert_equal(1, method.call_trees.callers.count)
62
+ call_tree = method.call_trees.callers[0]
63
+ assert_equal('InverseCallTreeTest#test_inverse', call_tree.parent.target.full_name)
64
+ assert_equal(26, call_tree.line)
65
+
66
+ assert_equal(2, method.call_trees.callees.count)
67
+ call_tree = method.call_trees.callees[0]
68
+ assert_equal('InverseCallTreeTest#method_3', call_tree.target.full_name)
69
+ assert_equal(26, call_tree.line)
70
+
71
+ # Kernel#sleep
72
+ method = methods[2]
73
+ assert_equal('Kernel#sleep', method.full_name)
74
+ assert_equal(0, method.line)
75
+
76
+ assert_equal(5, method.call_trees.callers.count)
77
+ call_tree = method.call_trees.callers[0]
78
+ assert_equal('InverseCallTreeTest#method_0', call_tree.parent.target.full_name)
79
+ assert_equal(19, call_tree.line)
80
+
81
+ call_tree = method.call_trees.callers[1]
82
+ assert_equal('InverseCallTreeTest#method_1', call_tree.parent.target.full_name)
83
+ assert_equal(26, call_tree.line)
84
+
85
+ call_tree = method.call_trees.callers[2]
86
+ assert_equal('InverseCallTreeTest#method_2', call_tree.parent.target.full_name)
87
+ assert_equal(26, call_tree.line)
88
+ call_tree = method.call_trees.callers[3]
89
+
90
+ assert_equal('InverseCallTreeTest#method_3', call_tree.parent.target.full_name)
91
+ assert_equal(26, call_tree.line)
92
+
93
+ call_tree = method.call_trees.callers[4]
94
+ assert_equal('InverseCallTreeTest#method_4', call_tree.parent.target.full_name)
95
+ assert_equal(26, call_tree.line)
96
+
97
+ assert_equal(0, method.call_trees.callees.count)
98
+
99
+ # InverseCallTreeTest#method_3
100
+ method = methods[3]
101
+ assert_equal('InverseCallTreeTest#method_3', method.full_name)
102
+ assert_equal(26, method.line)
103
+
104
+ assert_equal(1, method.call_trees.callers.count)
105
+ call_tree = method.call_trees.callers[0]
106
+ assert_equal('InverseCallTreeTest#method_4', call_tree.parent.target.full_name)
107
+ assert_equal(26, call_tree.line)
108
+
109
+ assert_equal(2, method.call_trees.callees.count)
110
+ call_tree = method.call_trees.callees[0]
111
+ assert_equal('InverseCallTreeTest#method_2', call_tree.target.full_name)
112
+ assert_equal(26, call_tree.line)
113
+
114
+ call_tree = method.call_trees.callees[1]
115
+ assert_equal('Kernel#sleep', call_tree.target.full_name)
116
+ assert_equal(26, call_tree.line)
117
+
118
+ # InverseCallTreeTest#method_2
119
+ method = methods[4]
120
+ assert_equal('InverseCallTreeTest#method_2', method.full_name)
121
+ assert_equal(26, method.line)
122
+
123
+ assert_equal(1, method.call_trees.callers.count)
124
+ call_tree = method.call_trees.callers[0]
125
+ assert_equal('InverseCallTreeTest#method_3', call_tree.parent.target.full_name)
126
+ assert_equal(26, call_tree.line)
127
+
128
+ assert_equal(2, method.call_trees.callees.count)
129
+ call_tree = method.call_trees.callees[0]
130
+ assert_equal('InverseCallTreeTest#method_1', call_tree.target.full_name)
131
+ assert_equal(26, call_tree.line)
132
+
133
+ call_tree = method.call_trees.callees[1]
134
+ assert_equal('Kernel#sleep', call_tree.target.full_name)
135
+ assert_equal(26, call_tree.line)
136
+
137
+ call_tree = method.call_trees.callees[1]
138
+ assert_equal('Kernel#sleep', call_tree.target.full_name)
139
+ assert_equal(26, call_tree.line)
140
+
141
+ # InverseCallTreeTest#method_1
142
+ method = methods[5]
143
+ assert_equal('InverseCallTreeTest#method_1', method.full_name)
144
+ assert_equal(26, method.line)
145
+
146
+ assert_equal(1, method.call_trees.callers.count)
147
+ call_tree = method.call_trees.callers[0]
148
+ assert_equal('InverseCallTreeTest#method_2', call_tree.parent.target.full_name)
149
+ assert_equal(26, call_tree.line)
150
+
151
+ assert_equal(2, method.call_trees.callees.count)
152
+ call_tree = method.call_trees.callees[0]
153
+ assert_equal('InverseCallTreeTest#method_0', call_tree.target.full_name)
154
+ assert_equal(19, call_tree.line)
155
+
156
+ call_tree = method.call_trees.callees[1]
157
+ assert_equal('Kernel#sleep', call_tree.target.full_name)
158
+ assert_equal(26, call_tree.line)
159
+
160
+ # InverseCallTreeTest#method_0
161
+ method = methods[6]
162
+ assert_equal('InverseCallTreeTest#method_0', method.full_name)
163
+ assert_equal(19, method.line)
164
+
165
+ assert_equal(1, method.call_trees.callers.count)
166
+ call_tree = method.call_trees.callers[0]
167
+ assert_equal('InverseCallTreeTest#method_1', call_tree.parent.target.full_name)
168
+ assert_equal(19, call_tree.line)
169
+
170
+ assert_equal(1, method.call_trees.callees.count)
171
+ call_tree = method.call_trees.callees[0]
172
+ assert_equal('Kernel#sleep', call_tree.target.full_name)
173
+ assert_equal(19, call_tree.line)
174
+ end
175
+ end