airbnb-ruby-prof 0.0.1

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 (116) hide show
  1. data/CHANGES +483 -0
  2. data/LICENSE +25 -0
  3. data/README.rdoc +426 -0
  4. data/Rakefile +51 -0
  5. data/bin/ruby-prof +279 -0
  6. data/bin/ruby-prof-check-trace +45 -0
  7. data/examples/flat.txt +50 -0
  8. data/examples/graph.dot +84 -0
  9. data/examples/graph.html +823 -0
  10. data/examples/graph.txt +139 -0
  11. data/examples/multi.flat.txt +23 -0
  12. data/examples/multi.graph.html +760 -0
  13. data/examples/multi.grind.dat +114 -0
  14. data/examples/multi.stack.html +547 -0
  15. data/examples/stack.html +547 -0
  16. data/ext/ruby_prof/extconf.rb +67 -0
  17. data/ext/ruby_prof/rp_call_info.c +374 -0
  18. data/ext/ruby_prof/rp_call_info.h +59 -0
  19. data/ext/ruby_prof/rp_fast_call_tree_printer.c +247 -0
  20. data/ext/ruby_prof/rp_fast_call_tree_printer.h +10 -0
  21. data/ext/ruby_prof/rp_measure.c +71 -0
  22. data/ext/ruby_prof/rp_measure.h +56 -0
  23. data/ext/ruby_prof/rp_measure_allocations.c +74 -0
  24. data/ext/ruby_prof/rp_measure_cpu_time.c +134 -0
  25. data/ext/ruby_prof/rp_measure_gc_runs.c +71 -0
  26. data/ext/ruby_prof/rp_measure_gc_time.c +58 -0
  27. data/ext/ruby_prof/rp_measure_memory.c +75 -0
  28. data/ext/ruby_prof/rp_measure_process_time.c +69 -0
  29. data/ext/ruby_prof/rp_measure_wall_time.c +43 -0
  30. data/ext/ruby_prof/rp_method.c +717 -0
  31. data/ext/ruby_prof/rp_method.h +79 -0
  32. data/ext/ruby_prof/rp_stack.c +221 -0
  33. data/ext/ruby_prof/rp_stack.h +81 -0
  34. data/ext/ruby_prof/rp_thread.c +312 -0
  35. data/ext/ruby_prof/rp_thread.h +36 -0
  36. data/ext/ruby_prof/ruby_prof.c +800 -0
  37. data/ext/ruby_prof/ruby_prof.h +64 -0
  38. data/ext/ruby_prof/vc/ruby_prof.sln +32 -0
  39. data/ext/ruby_prof/vc/ruby_prof_18.vcxproj +108 -0
  40. data/ext/ruby_prof/vc/ruby_prof_19.vcxproj +110 -0
  41. data/ext/ruby_prof/vc/ruby_prof_20.vcxproj +110 -0
  42. data/lib/ruby-prof.rb +63 -0
  43. data/lib/ruby-prof/aggregate_call_info.rb +76 -0
  44. data/lib/ruby-prof/assets/call_stack_printer.css.html +117 -0
  45. data/lib/ruby-prof/assets/call_stack_printer.js.html +385 -0
  46. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  47. data/lib/ruby-prof/assets/flame_graph_printer.lib.css.html +149 -0
  48. data/lib/ruby-prof/assets/flame_graph_printer.lib.js.html +707 -0
  49. data/lib/ruby-prof/assets/flame_graph_printer.page.js.html +56 -0
  50. data/lib/ruby-prof/assets/flame_graph_printer.tmpl.html.erb +39 -0
  51. data/lib/ruby-prof/call_info.rb +111 -0
  52. data/lib/ruby-prof/call_info_visitor.rb +40 -0
  53. data/lib/ruby-prof/compatibility.rb +186 -0
  54. data/lib/ruby-prof/method_info.rb +109 -0
  55. data/lib/ruby-prof/printers/abstract_printer.rb +85 -0
  56. data/lib/ruby-prof/printers/call_info_printer.rb +41 -0
  57. data/lib/ruby-prof/printers/call_stack_printer.rb +260 -0
  58. data/lib/ruby-prof/printers/call_tree_printer.rb +130 -0
  59. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  60. data/lib/ruby-prof/printers/fast_call_tree_printer.rb +87 -0
  61. data/lib/ruby-prof/printers/flame_graph_html_printer.rb +59 -0
  62. data/lib/ruby-prof/printers/flame_graph_json_printer.rb +157 -0
  63. data/lib/ruby-prof/printers/flat_printer.rb +70 -0
  64. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +64 -0
  65. data/lib/ruby-prof/printers/graph_html_printer.rb +244 -0
  66. data/lib/ruby-prof/printers/graph_printer.rb +116 -0
  67. data/lib/ruby-prof/printers/multi_printer.rb +58 -0
  68. data/lib/ruby-prof/profile.rb +22 -0
  69. data/lib/ruby-prof/profile/exclude_common_methods.rb +201 -0
  70. data/lib/ruby-prof/rack.rb +95 -0
  71. data/lib/ruby-prof/task.rb +147 -0
  72. data/lib/ruby-prof/thread.rb +35 -0
  73. data/lib/ruby-prof/version.rb +4 -0
  74. data/lib/ruby-prof/walker.rb +95 -0
  75. data/lib/unprof.rb +10 -0
  76. data/ruby-prof.gemspec +56 -0
  77. data/test/aggregate_test.rb +136 -0
  78. data/test/basic_test.rb +128 -0
  79. data/test/block_test.rb +74 -0
  80. data/test/call_info_test.rb +78 -0
  81. data/test/call_info_visitor_test.rb +31 -0
  82. data/test/duplicate_names_test.rb +32 -0
  83. data/test/dynamic_method_test.rb +55 -0
  84. data/test/enumerable_test.rb +21 -0
  85. data/test/exceptions_test.rb +16 -0
  86. data/test/exclude_methods_test.rb +146 -0
  87. data/test/exclude_threads_test.rb +53 -0
  88. data/test/fiber_test.rb +79 -0
  89. data/test/issue137_test.rb +63 -0
  90. data/test/line_number_test.rb +71 -0
  91. data/test/measure_allocations_test.rb +26 -0
  92. data/test/measure_cpu_time_test.rb +213 -0
  93. data/test/measure_gc_runs_test.rb +32 -0
  94. data/test/measure_gc_time_test.rb +36 -0
  95. data/test/measure_memory_test.rb +33 -0
  96. data/test/measure_process_time_test.rb +63 -0
  97. data/test/measure_wall_time_test.rb +255 -0
  98. data/test/module_test.rb +45 -0
  99. data/test/multi_measure_test.rb +38 -0
  100. data/test/multi_printer_test.rb +83 -0
  101. data/test/no_method_class_test.rb +15 -0
  102. data/test/pause_resume_test.rb +166 -0
  103. data/test/prime.rb +54 -0
  104. data/test/printers_test.rb +255 -0
  105. data/test/printing_recursive_graph_test.rb +127 -0
  106. data/test/rack_test.rb +93 -0
  107. data/test/recursive_test.rb +212 -0
  108. data/test/singleton_test.rb +38 -0
  109. data/test/stack_printer_test.rb +65 -0
  110. data/test/stack_test.rb +138 -0
  111. data/test/start_stop_test.rb +112 -0
  112. data/test/test_helper.rb +264 -0
  113. data/test/thread_test.rb +187 -0
  114. data/test/unique_call_path_test.rb +202 -0
  115. data/test/yarv_test.rb +55 -0
  116. metadata +211 -0
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ # Test data
7
+ # A
8
+ # / \
9
+ # B C
10
+ # \
11
+ # B
12
+
13
+ class STPT
14
+ def a
15
+ 100.times{b}
16
+ 300.times{c}
17
+ c;c;c
18
+ end
19
+
20
+ def b
21
+ sleep 0
22
+ end
23
+
24
+ def c
25
+ 5.times{b}
26
+ end
27
+ end
28
+
29
+ class StackPrinterTest < TestCase
30
+ def setup
31
+ # Need to use wall time for this test due to the sleep calls
32
+ RubyProf::measure_mode = RubyProf::WALL_TIME
33
+ end
34
+
35
+ def test_stack_can_be_printed
36
+ start_time = Time.now
37
+ RubyProf.start
38
+ 5.times{STPT.new.a}
39
+ result = RubyProf.stop
40
+ end_time = Time.now
41
+ expected_time = end_time - start_time
42
+
43
+ file_contents = nil
44
+ assert_nothing_raised { file_contents = print(result) }
45
+ # TODO: why are thread ids negative on travis-ci.org (32 bit build maybe?)
46
+ re = /Thread: (-?\d+)(, Fiber: (-?\d+))? \(100\.00% ~ ([\.0-9]+)\)/
47
+ assert_match(re, file_contents)
48
+ file_contents =~ re
49
+ actual_time = $4.to_f
50
+ assert_in_delta(expected_time, actual_time, 0.1)
51
+ end
52
+
53
+ private
54
+ def print(result)
55
+ test = caller.first =~ /in `(.*)'/ ? $1 : "test"
56
+ testfile_name = "#{RubyProf.tmpdir}/ruby_prof_#{test}.html"
57
+ # puts "printing to #{testfile_name}"
58
+ printer = RubyProf::CallStackPrinter.new(result)
59
+ File.open(testfile_name, "w") {|f| printer.print(f, :threshold => 0, :min_percent => 0, :title => "ruby_prof #{test}")}
60
+ system("open '#{testfile_name}'") if RUBY_PLATFORM =~ /darwin/ && ENV['SHOW_RUBY_PROF_PRINTER_OUTPUT']=="1"
61
+ assert File.exist?(testfile_name), "#{testfile_name} does not exist"
62
+ assert File.readable?(testfile_name), "#{testfile_name} is no readable"
63
+ File.read(testfile_name)
64
+ end
65
+ end
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ # Test data
7
+ # A
8
+ # / \
9
+ # B C
10
+ # \
11
+ # B
12
+
13
+ class StackClass
14
+ def a
15
+ sleep 1
16
+ b
17
+ c
18
+ end
19
+
20
+ def b
21
+ sleep 2
22
+ end
23
+
24
+ def c
25
+ sleep 3
26
+ b
27
+ end
28
+ end
29
+
30
+ class StackTest < TestCase
31
+ def setup
32
+ # Need to use wall time for this test due to the sleep calls
33
+ RubyProf::measure_mode = RubyProf::WALL_TIME
34
+ end
35
+
36
+ def test_call_sequence
37
+ c = StackClass.new
38
+ result = RubyProf.profile do
39
+ c.a
40
+ end
41
+
42
+ # Length should be 5:
43
+ # StackTest#test_call_sequence
44
+ # StackClass#a
45
+ # Kernel#sleep
46
+ # StackClass#c
47
+ # StackClass#b
48
+
49
+ methods = result.threads.first.methods.sort.reverse
50
+ assert_equal(5, methods.length)
51
+
52
+ # Check StackTest#test_call_sequence
53
+ method = methods[0]
54
+ assert_equal('StackTest#test_call_sequence', method.full_name)
55
+ assert_equal(1, method.called)
56
+ assert_in_delta(8, method.total_time, 0.25)
57
+ assert_in_delta(0, method.wait_time, 0.01)
58
+ assert_in_delta(0, method.self_time, 0.01)
59
+ assert_in_delta(8, method.children_time, 0.25)
60
+ assert_equal(1, method.call_infos.length)
61
+
62
+ call_info = method.call_infos[0]
63
+ assert_equal('StackTest#test_call_sequence', call_info.call_sequence)
64
+ assert_equal(1, call_info.children.length)
65
+
66
+ # Check StackClass#a
67
+ method = methods[1]
68
+ assert_equal('StackClass#a', method.full_name)
69
+ assert_equal(1, method.called)
70
+ assert_in_delta(8, method.total_time, 0.15)
71
+ assert_in_delta(0, method.wait_time, 0.01)
72
+ assert_in_delta(0, method.self_time, 0.01)
73
+ assert_in_delta(8, method.children_time, 0.05)
74
+ assert_equal(1, method.call_infos.length)
75
+
76
+ call_info = method.call_infos[0]
77
+ assert_equal('StackTest#test_call_sequence->StackClass#a', call_info.call_sequence)
78
+ assert_equal(3, call_info.children.length)
79
+
80
+ # Check Kernel#sleep
81
+ method = methods[2]
82
+ assert_equal('Kernel#sleep', method.full_name)
83
+ assert_equal(4, method.called)
84
+ assert_in_delta(8, method.total_time, 0.05)
85
+ assert_in_delta(0, method.wait_time, 0.01)
86
+ assert_in_delta(8, method.self_time, 0.05)
87
+ assert_in_delta(0, method.children_time, 0.05)
88
+ assert_equal(4, method.call_infos.length)
89
+
90
+ call_info = method.call_infos[0]
91
+ assert_equal('StackTest#test_call_sequence->StackClass#a->Kernel#sleep', call_info.call_sequence)
92
+ assert_equal(0, call_info.children.length)
93
+
94
+ call_info = method.call_infos[1]
95
+ assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#b->Kernel#sleep', call_info.call_sequence)
96
+ assert_equal(0, call_info.children.length)
97
+
98
+ call_info = method.call_infos[2]
99
+ assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#c->Kernel#sleep', call_info.call_sequence)
100
+ assert_equal(0, call_info.children.length)
101
+
102
+ call_info = method.call_infos[3]
103
+ assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#c->StackClass#b->Kernel#sleep', call_info.call_sequence)
104
+ assert_equal(0, call_info.children.length)
105
+
106
+ # Check StackClass#c
107
+ method = methods[3]
108
+ assert_equal('StackClass#c', method.full_name)
109
+ assert_equal(1, method.called)
110
+ assert_in_delta(5, method.total_time, 0.05)
111
+ assert_in_delta(0, method.wait_time, 0.01)
112
+ assert_in_delta(0, method.self_time, 0.01)
113
+ assert_in_delta(5, method.children_time, 0.05)
114
+ assert_equal(1, method.call_infos.length)
115
+
116
+ call_info = method.call_infos[0]
117
+ assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#c', call_info.call_sequence)
118
+ assert_equal(2, call_info.children.length)
119
+
120
+ # Check StackClass#b
121
+ method = methods[4]
122
+ assert_equal('StackClass#b', method.full_name)
123
+ assert_equal(2, method.called)
124
+ assert_in_delta(4, method.total_time, 0.05)
125
+ assert_in_delta(0, method.wait_time, 0.01)
126
+ assert_in_delta(0, method.self_time, 0.01)
127
+ assert_in_delta(4, method.children_time, 0.05)
128
+ assert_equal(2, method.call_infos.length)
129
+
130
+ call_info = method.call_infos[0]
131
+ assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#b', call_info.call_sequence)
132
+ assert_equal(1, call_info.children.length)
133
+
134
+ call_info = method.call_infos[1]
135
+ assert_equal('StackTest#test_call_sequence->StackClass#a->StackClass#c->StackClass#b', call_info.call_sequence)
136
+ assert_equal(1, call_info.children.length)
137
+ end
138
+ end
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ class StartStopTest < TestCase
7
+ def setup
8
+ # Need to use wall time for this test due to the sleep calls
9
+ RubyProf::measure_mode = RubyProf::WALL_TIME
10
+ end
11
+
12
+ def method1
13
+ RubyProf.start
14
+ method2
15
+ end
16
+
17
+ def method2
18
+ method3
19
+ end
20
+
21
+ def method3
22
+ sleep(2)
23
+ @result = RubyProf.stop
24
+ end
25
+
26
+ def test_extra_stop_should_raise
27
+ RubyProf.start
28
+ assert_raises(RuntimeError) do
29
+ RubyProf.start
30
+ end
31
+
32
+ assert_raises(RuntimeError) do
33
+ RubyProf.profile {}
34
+ end
35
+
36
+ RubyProf.stop # ok
37
+ assert_raises(RuntimeError) do
38
+ RubyProf.stop
39
+ end
40
+ end
41
+
42
+
43
+ def test_different_methods
44
+ method1
45
+
46
+ # Ruby prof should be stopped
47
+ assert_equal(false, RubyProf.running?)
48
+
49
+
50
+ # Length should be 4:
51
+ # StartStopTest#method1
52
+ # StartStopTest#method2
53
+ # StartStopTest#method3
54
+ # Kernel#sleep
55
+
56
+ methods = @result.threads.first.methods.sort.reverse
57
+ assert_equal(4, methods.length)
58
+
59
+ # Check StackTest#test_call_sequence
60
+ method = methods[0]
61
+ assert_equal('StartStopTest#method1', method.full_name)
62
+ assert_equal(1, method.called)
63
+ assert_in_delta(2, method.total_time, 0.05)
64
+ assert_in_delta(0, method.wait_time, 0.02)
65
+ assert_in_delta(0, method.self_time, 0.02)
66
+ assert_in_delta(2, method.children_time, 0.05)
67
+ assert_equal(1, method.call_infos.length)
68
+
69
+ call_info = method.call_infos[0]
70
+ assert_equal('StartStopTest#method1', call_info.call_sequence)
71
+ assert_equal(1, call_info.children.length)
72
+
73
+ method = methods[1]
74
+ assert_equal('StartStopTest#method2', method.full_name)
75
+ assert_equal(1, method.called)
76
+ assert_in_delta(2, method.total_time, 0.05)
77
+ assert_in_delta(0, method.wait_time, 0.02)
78
+ assert_in_delta(0, method.self_time, 0.02)
79
+ assert_in_delta(2, method.children_time, 0.05)
80
+ assert_equal(1, method.call_infos.length)
81
+
82
+ call_info = method.call_infos[0]
83
+ assert_equal('StartStopTest#method1->StartStopTest#method2', call_info.call_sequence)
84
+ assert_equal(1, call_info.children.length)
85
+
86
+ method = methods[2]
87
+ assert_equal('StartStopTest#method3', method.full_name)
88
+ assert_equal(1, method.called)
89
+ assert_in_delta(2, method.total_time, 0.02)
90
+ assert_in_delta(0, method.wait_time, 0.02)
91
+ assert_in_delta(0, method.self_time, 0.02)
92
+ assert_in_delta(2, method.children_time, 0.02)
93
+ assert_equal(1, method.call_infos.length)
94
+
95
+ call_info = method.call_infos[0]
96
+ assert_equal('StartStopTest#method1->StartStopTest#method2->StartStopTest#method3', call_info.call_sequence)
97
+ assert_equal(1, call_info.children.length)
98
+
99
+ method = methods[3]
100
+ assert_equal('Kernel#sleep', method.full_name)
101
+ assert_equal(1, method.called)
102
+ assert_in_delta(2, method.total_time, 0.02)
103
+ assert_in_delta(0, method.wait_time, 0.02)
104
+ assert_in_delta(2, method.self_time, 0.02)
105
+ assert_in_delta(0, method.children_time, 0.02)
106
+ assert_equal(1, method.call_infos.length)
107
+
108
+ call_info = method.call_infos[0]
109
+ assert_equal('StartStopTest#method1->StartStopTest#method2->StartStopTest#method3->Kernel#sleep', call_info.call_sequence)
110
+ assert_equal(0, call_info.children.length)
111
+ end
112
+ end
@@ -0,0 +1,264 @@
1
+ # encoding: UTF-8
2
+
3
+ # Make RubyMine happy
4
+ require "rubygems"
5
+ gem "minitest"
6
+
7
+ if ENV["RM_INFO"] || ENV["TEAMCITY_VERSION"]
8
+ if RUBY_PLATFORM =~ /(win32|mingw)/
9
+ gem "win32console"
10
+ end
11
+ gem "minitest-reporters"
12
+ require 'minitest/reporters'
13
+ MiniTest::Reporters.use!
14
+ end
15
+
16
+ require "minitest/pride" if RUBY_VERSION == "1.9.3"
17
+
18
+ # To make testing/debugging easier, test within this source tree versus an installed gem
19
+ dir = File.dirname(__FILE__)
20
+ root = File.expand_path(File.join(dir, '..'))
21
+ lib = File.expand_path(File.join(root, 'lib'))
22
+ ext = File.expand_path(File.join(root, 'ext', 'ruby_prof'))
23
+
24
+ $LOAD_PATH << lib
25
+ $LOAD_PATH << ext
26
+
27
+ require 'ruby-prof'
28
+ require 'minitest/autorun'
29
+
30
+ class TestCase < Minitest::Test
31
+ # I know this sucks, but ...
32
+ def assert_nothing_raised(*)
33
+ yield
34
+ end
35
+
36
+ def before_setup
37
+ # make sure to exclude all threads except the one running the test
38
+ # minitest allocates a thread pool and they would otherwise show
39
+ # up in the profile data, breaking tests randomly
40
+ RubyProf.exclude_threads = Thread.list.select{|t| t != Thread.current}
41
+ end
42
+
43
+ def after_teardown
44
+ # reset exclude threads after testing
45
+ RubyProf.exclude_threads = nil
46
+ end
47
+ end
48
+
49
+ require File.expand_path('../prime', __FILE__)
50
+
51
+ # Some classes used in measurement tests
52
+ module RubyProf
53
+ class C1
54
+ def C1.hello
55
+ sleep(0.1)
56
+ end
57
+
58
+ def hello
59
+ sleep(0.2)
60
+ end
61
+ end
62
+
63
+ module M1
64
+ def hello
65
+ sleep(0.3)
66
+ end
67
+ end
68
+
69
+ class C2
70
+ include M1
71
+ extend M1
72
+ end
73
+
74
+ class C3
75
+ def hello
76
+ sleep(0.4)
77
+ end
78
+ end
79
+
80
+ module M4
81
+ def hello
82
+ sleep(0.5)
83
+ end
84
+ end
85
+
86
+ module M5
87
+ include M4
88
+ def goodbye
89
+ hello
90
+ end
91
+ end
92
+
93
+ class C6
94
+ include M5
95
+ def test
96
+ goodbye
97
+ end
98
+ end
99
+
100
+ class C7
101
+ def self.busy_wait
102
+ t = Time.now.to_f
103
+ while Time.now.to_f - t < 0.1; end
104
+ end
105
+
106
+ def self.sleep_wait
107
+ sleep 0.1
108
+ end
109
+
110
+ def busy_wait
111
+ t = Time.now.to_f
112
+ while Time.now.to_f - t < 0.2; end
113
+ end
114
+
115
+ def sleep_wait
116
+ sleep 0.2
117
+ end
118
+ end
119
+
120
+ module M7
121
+ def busy_wait
122
+ t = Time.now.to_f
123
+ while Time.now.to_f - t < 0.3; end
124
+ end
125
+
126
+ def sleep_wait
127
+ sleep 0.3
128
+ end
129
+ end
130
+
131
+ class C8
132
+ include M7
133
+ extend M7
134
+ end
135
+
136
+ def self.ruby_major_version
137
+ match = RUBY_VERSION.match(/(\d)\.(\d)/)
138
+ return Integer(match[1])
139
+ end
140
+
141
+ def self.ruby_minor_version
142
+ match = RUBY_VERSION.match(/(\d)\.(\d)/)
143
+ return Integer(match[2])
144
+ end
145
+
146
+ def self.parent_object
147
+ if ruby_major_version == 1 && ruby_minor_version == 8
148
+ Object
149
+ else
150
+ BasicObject
151
+ end
152
+ end
153
+
154
+ def self.ruby_2?
155
+ ruby_major_version == 2
156
+ end
157
+
158
+ # store printer output in this directory
159
+ def self.tmpdir
160
+ File.expand_path('../../tmp', __FILE__)
161
+ end
162
+ end
163
+
164
+ module MemoryTestHelper
165
+ def memory_test_helper
166
+ result = RubyProf.profile {Array.new}
167
+ total = result.threads.first.methods.inject(0) { |sum, m| sum + m.total_time }
168
+ assert(total < 1_000_000, 'Total should not have subtract overflow error')
169
+ total
170
+ end
171
+ end
172
+
173
+ module PrinterTestHelper
174
+ Metrics = Struct.new(:name, :total, :self_t, :wait, :child, :calls)
175
+ class Metrics
176
+ def pp
177
+ "%s[total: %.2f, self: %.2f, wait: %.2f, child: %.2f, calls: %s]" %
178
+ [name, total, self_t, wait, child, calls]
179
+ end
180
+ end
181
+
182
+ Entry = Struct.new(:total_p, :self_p, :metrics, :parents, :children)
183
+ class Entry
184
+ def child(name)
185
+ children.detect{|m| m.name == name}
186
+ end
187
+
188
+ def parent(name)
189
+ parents.detect{|m| m.name == name}
190
+ end
191
+
192
+ def pp
193
+ res = ""
194
+ res << "NODE (total%%: %.2f, self%%: %.2f) %s\n" % [total_p, self_p, metrics.pp]
195
+ res << " PARENTS:\n"
196
+ parents.each {|m| res << " " + m.pp << "\n"}
197
+ res << " CHILDREN:\n"
198
+ children.each {|m| res << " " + m.pp << "\n"}
199
+ res
200
+ end
201
+ end
202
+
203
+ class MetricsArray < Array
204
+ def metrics_for(name)
205
+ detect {|e| e.metrics.name == name}
206
+ end
207
+
208
+ def pp(io = STDOUT)
209
+ entries = map do |e|
210
+ begin
211
+ e.pp
212
+ rescue
213
+ puts $!.message + e.inspect
214
+ ""
215
+ end
216
+ end
217
+ io.puts entries.join("--------------------------------------------------\n")
218
+ end
219
+
220
+ def self.parse(str)
221
+ res = new
222
+ entry = nil
223
+ relatives = []
224
+ state = :preamble
225
+
226
+ str.each_line do |l|
227
+ line = l.chomp.strip
228
+ if line =~ /-----/
229
+ if state == :preamble
230
+ state = :parsing_parents
231
+ entry = Entry.new
232
+ elsif state == :parsing_parents
233
+ entry = Entry.new
234
+ elsif state == :parsing_children
235
+ entry.children = relatives
236
+ res << entry
237
+ entry = Entry.new
238
+ relatives = []
239
+ state = :parsing_parents
240
+ end
241
+ elsif line =~ /^\s*$/ || line =~ /indicates recursively called methods/
242
+ next
243
+ elsif state != :preamble
244
+ elements = line.split(/\s+/)
245
+ method = elements.pop
246
+ numbers = elements[0..-2].map(&:to_f)
247
+ metrics = Metrics.new(method, *numbers[-4..-1], elements[-1])
248
+ if numbers.size == 6
249
+ entry.metrics = metrics
250
+ entry.total_p = numbers[0]
251
+ entry.self_p = numbers[1]
252
+ entry.parents = relatives
253
+ entry.children = relatives = []
254
+ state = :parsing_children
255
+ res << entry
256
+ else
257
+ relatives << metrics
258
+ end
259
+ end
260
+ end
261
+ res
262
+ end
263
+ end
264
+ end