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,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ class MultiMeasureTest < TestCase
6
+ def foo
7
+ end
8
+
9
+ def test_measure_modes
10
+ measure_modes = [RubyProf::PROCESS_TIME, RubyProf::WALL_TIME]
11
+
12
+ profile = RubyProf::Profile.new(measure_modes: measure_modes)
13
+
14
+ assert_equal measure_modes, profile.measure_modes
15
+ end
16
+
17
+ def test_measure_values
18
+ measure_modes = [RubyProf::PROCESS_TIME, RubyProf::WALL_TIME]
19
+
20
+ profile = RubyProf::Profile.new(measure_modes: measure_modes)
21
+
22
+ profile.start
23
+
24
+ foo
25
+
26
+ profile.stop
27
+
28
+ call_info = profile.threads.first.methods.first.call_infos.first
29
+
30
+ measure_values = call_info.measure_values
31
+
32
+ assert_equal 2, measure_values.size
33
+
34
+ measure_values.each do |val|
35
+ assert_equal 3, val.size
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,83 @@
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 MSTPT
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 MultiPrinterTest < 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_all_profiles_can_be_created
36
+ start_time = Time.now
37
+ RubyProf.start
38
+ 5.times{MSTPT.new.a}
39
+ result = RubyProf.stop
40
+ end_time = Time.now
41
+ expected_time = end_time - start_time
42
+ stack = graph = nil
43
+ assert_nothing_raised { stack, graph = print(result) }
44
+ re = Regexp.new('
45
+ \s*<table>
46
+ \s*<tr>
47
+ \s*<th>Thread ID</th>
48
+ \s*(<th>Fiber ID</th>)?
49
+ \s*<th>Total Time</th>
50
+ \s*</tr>
51
+ \s*
52
+ \s*<tr>
53
+ \s*(<td>([\.0-9]+)</td>)?
54
+ \s*<td><a href="#-?\d+">-?\d+</a></td>
55
+ \s*<td>([\.0-9e]+)</td>
56
+ \s*</tr>
57
+ \s*
58
+ \s*</table>')
59
+ assert_match(re, graph)
60
+ graph =~ re
61
+ display_time = $4.to_f
62
+ assert_in_delta expected_time, display_time, 0.001
63
+ end
64
+
65
+ private
66
+
67
+ def print(result)
68
+ test = caller.first =~ /in `(.*)'/ ? $1 : "test"
69
+ path = RubyProf.tmpdir
70
+ profile = "ruby_prof_#{test}"
71
+ printer = RubyProf::MultiPrinter.new(result)
72
+ printer.print(:path => path, :profile => profile,
73
+ :threshold => 0, :min_percent => 0, :title => "ruby_prof #{test}")
74
+ if RUBY_PLATFORM =~ /darwin/ && ENV['SHOW_RUBY_PROF_PRINTER_OUTPUT']=="1"
75
+ system("open '#{printer.stack_profile}'")
76
+ end
77
+ # if GC.respond_to?(:dump_file_and_line_info)
78
+ # GC.start
79
+ # GC.dump_file_and_line_info("heap.dump")
80
+ # end
81
+ [File.read(printer.stack_profile), File.read(printer.graph_profile)]
82
+ end
83
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ # Make sure this works with no class or method
7
+ result = RubyProf.profile do
8
+ sleep 1
9
+ end
10
+
11
+ methods = result.threads.first.methods
12
+ global_method = methods.sort_by {|method| method.full_name}.first
13
+ if global_method.full_name != 'Kernel#sleep'
14
+ raise(RuntimeError, "Wrong method name. Expected: Global#[No method]. Actual: #{global_method.full_name}")
15
+ end
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ class PauseResumeTest < 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 test_pause_resume
13
+ # Measured
14
+ RubyProf.start
15
+ RubyProf::C1.hello
16
+
17
+ # Not measured
18
+ RubyProf.pause
19
+ sleep 1
20
+ RubyProf::C1.hello
21
+
22
+ # Measured
23
+ RubyProf.resume
24
+ RubyProf::C1.hello
25
+
26
+ result = RubyProf.stop
27
+
28
+ # Length should be 3:
29
+ # PauseResumeTest#test_pause_resume
30
+ # <Class::RubyProf::C1>#hello
31
+ # Kernel#sleep
32
+
33
+ methods = result.threads.first.methods.sort_by {|method_info| method_info.full_name}
34
+ # remove methods called by pause/resume
35
+ called_methods = ['Array#include?', 'Fixnum#==', 'Kernel#respond_to?', 'Kernel#respond_to_missing?']
36
+ methods.reject!{|m| called_methods.include?(m.full_name) }
37
+ # TODO: fix pause/resume to not include those methods in the first place
38
+ assert_equal(3, methods.length)
39
+
40
+ # Check the names
41
+ assert_equal('<Class::RubyProf::C1>#hello', methods[0].full_name)
42
+ assert_equal('Kernel#sleep', methods[1].full_name)
43
+ assert_equal('PauseResumeTest#test_pause_resume', methods[2].full_name)
44
+
45
+ # Check times
46
+ assert_in_delta(0.2, methods[0].total_time, 0.02)
47
+ assert_in_delta(0, methods[0].wait_time, 0.02)
48
+ assert_in_delta(0, methods[0].self_time, 0.02)
49
+
50
+ assert_in_delta(0.2, methods[1].total_time, 0.02)
51
+ assert_in_delta(0, methods[1].wait_time, 0.02)
52
+ assert_in_delta(0.2, methods[1].self_time, 0.02)
53
+
54
+ assert_in_delta(0.2, methods[2].total_time, 0.02)
55
+ assert_in_delta(0, methods[2].wait_time, 0.02)
56
+ assert_in_delta(0, methods[2].self_time, 0.02)
57
+ end
58
+
59
+ # pause/resume in the same frame
60
+ def test_pause_resume_1
61
+ profile = RubyProf::Profile.new({})
62
+
63
+ profile.start
64
+ method_1a
65
+
66
+ profile.pause
67
+ method_1b
68
+
69
+ profile.resume
70
+ method_1c
71
+
72
+ result = profile.stop
73
+ assert_in_delta(0.6, result.threads[0].methods.select{|m| m.full_name =~ /test_pause_resume_1$/}[0].total_time, 0.05)
74
+ end
75
+ def method_1a; sleep 0.2 end
76
+ def method_1b; sleep 1 end
77
+ def method_1c; sleep 0.4 end
78
+
79
+ # pause in parent frame, resume in child
80
+ def test_pause_resume_2
81
+ profile = RubyProf::Profile.new({})
82
+
83
+ profile.start
84
+ method_2a
85
+
86
+ profile.pause
87
+ sleep 0.5
88
+ method_2b(profile)
89
+
90
+ result = profile.stop
91
+ assert_in_delta(0.6, result.threads[0].methods.select{|m| m.full_name =~ /test_pause_resume_2$/}[0].total_time, 0.05)
92
+ end
93
+ def method_2a; sleep 0.2 end
94
+ def method_2b(profile); sleep 0.5; profile.resume; sleep 0.4 end
95
+
96
+ # pause in child frame, resume in parent
97
+ def test_pause_resume_3
98
+ profile = RubyProf::Profile.new({})
99
+
100
+ profile.start
101
+ method_3a(profile)
102
+
103
+ sleep 0.5
104
+ profile.resume
105
+ method_3b
106
+
107
+ result = profile.stop
108
+ assert_in_delta(0.6, result.threads[0].methods.select{|m| m.full_name =~ /test_pause_resume_3$/}[0].total_time, 0.05)
109
+ end
110
+ def method_3a(profile); sleep 0.2; profile.pause; sleep 0.5 end
111
+ def method_3b; sleep 0.4 end
112
+
113
+ def test_pause_seq
114
+ profile = RubyProf::Profile.new({})
115
+ profile.start ; assert !profile.paused?
116
+ profile.pause ; assert profile.paused?
117
+ profile.resume; assert !profile.paused?
118
+ profile.pause ; assert profile.paused?
119
+ profile.pause ; assert profile.paused?
120
+ profile.resume; assert !profile.paused?
121
+ profile.resume; assert !profile.paused?
122
+ profile.stop ; assert !profile.paused?
123
+ end
124
+
125
+ def test_pause_block
126
+ profile = RubyProf::Profile.new({})
127
+ profile.start
128
+ profile.pause
129
+ assert profile.paused?
130
+
131
+ times_block_invoked = 0
132
+ retval= profile.resume{
133
+ times_block_invoked += 1
134
+ 120 + times_block_invoked
135
+ }
136
+ assert_equal 1, times_block_invoked
137
+ assert profile.paused?
138
+
139
+ assert_equal 121, retval, "resume() should return the result of the given block."
140
+
141
+ profile.stop
142
+ end
143
+
144
+ def test_pause_block_with_error
145
+ profile = RubyProf::Profile.new({})
146
+ profile.start
147
+ profile.pause
148
+ assert profile.paused?
149
+
150
+ begin
151
+ profile.resume{ raise }
152
+ flunk 'Exception expected.'
153
+ rescue
154
+ assert profile.paused?
155
+ end
156
+
157
+ profile.stop
158
+ end
159
+
160
+ def test_resume_when_not_paused
161
+ profile = RubyProf::Profile.new({})
162
+ profile.start ; assert !profile.paused?
163
+ profile.resume; assert !profile.paused?
164
+ profile.stop ; assert !profile.paused?
165
+ end
166
+ end
@@ -0,0 +1,54 @@
1
+ # A silly little test program that finds prime numbers. It
2
+ # is intentionally badly designed to show off the use
3
+ # of ruby-prof.
4
+ #
5
+ # Source from http://people.cs.uchicago.edu/~bomb154/154/maclabs/profilers-lab/
6
+
7
+ def make_random_array(length, maxnum)
8
+ result = Array.new(length)
9
+ result.each_index do |i|
10
+ result[i] = rand(maxnum)
11
+ end
12
+
13
+ result
14
+ end
15
+
16
+ def is_prime(x)
17
+ y = 2
18
+ y.upto(x-1) do |i|
19
+ return false if (x % i) == 0
20
+ end
21
+ true
22
+ end
23
+
24
+ def find_primes(arr)
25
+ result = arr.select do |value|
26
+ is_prime(value)
27
+ end
28
+ result
29
+ end
30
+
31
+ def find_largest(primes)
32
+ largest = primes.first
33
+
34
+ # Intentionally use upto for example purposes
35
+ # (upto is also called from is_prime)
36
+ 0.upto(primes.length-1) do |i|
37
+ prime = primes[i]
38
+ if prime > largest
39
+ largest = prime
40
+ end
41
+ end
42
+ largest
43
+ end
44
+
45
+ def run_primes(length=10, maxnum=1000)
46
+ # Create random numbers
47
+ random_array = make_random_array(length, maxnum)
48
+
49
+ # Find the primes
50
+ primes = find_primes(random_array)
51
+
52
+ # Find the largest primes
53
+ find_largest(primes)
54
+ end
@@ -0,0 +1,255 @@
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
+ # -- Tests ----
9
+ class PrintersTest < TestCase
10
+ def setup
11
+ # WALL_TIME so we can use sleep in our test and get same measurements on linux and windows
12
+ RubyProf::measure_mode = RubyProf::WALL_TIME
13
+ @result = RubyProf.profile do
14
+ run_primes(1000, 5000)
15
+ end
16
+ end
17
+
18
+ def test_printers
19
+ assert_nothing_raised do
20
+ output = ENV['SHOW_RUBY_PROF_PRINTER_OUTPUT'] == "1" ? STDOUT : StringIO.new('')
21
+
22
+ printer = RubyProf::CallInfoPrinter.new(@result)
23
+ printer.print(output)
24
+
25
+ printer = RubyProf::CallTreePrinter.new(@result)
26
+ printer.print()
27
+
28
+ printer = RubyProf::FlatPrinter.new(@result)
29
+ printer.print(output)
30
+
31
+ printer = RubyProf::FlatPrinterWithLineNumbers.new(@result)
32
+ printer.print(output)
33
+
34
+ printer = RubyProf::GraphHtmlPrinter.new(@result)
35
+ printer.print(output)
36
+
37
+ printer = RubyProf::GraphPrinter.new(@result)
38
+ printer.print(output)
39
+ end
40
+ end
41
+
42
+ def test_print_to_files
43
+ assert_nothing_raised do
44
+ output_dir = 'examples2'
45
+
46
+ if ENV['SAVE_NEW_PRINTER_EXAMPLES']
47
+ output_dir = 'examples'
48
+ end
49
+ FileUtils.mkdir_p output_dir
50
+
51
+ printer = RubyProf::DotPrinter.new(@result)
52
+ File.open("#{output_dir}/graph.dot", "w") {|f| printer.print(f)}
53
+
54
+ printer = RubyProf::CallStackPrinter.new(@result)
55
+ File.open("#{output_dir}/stack.html", "w") {|f| printer.print(f, :application => "primes")}
56
+
57
+ printer = RubyProf::MultiPrinter.new(@result)
58
+ printer.print(:path => "#{output_dir}", :profile => "multi", :application => "primes")
59
+ for file in ['graph.dot', 'multi.flat.txt', 'multi.graph.html', "multi.callgrind.out.#{$$}", 'multi.stack.html', 'stack.html']
60
+ existant_file = output_dir + '/' + file
61
+ assert File.size(existant_file) > 0
62
+ end
63
+ end
64
+ end
65
+
66
+ def test_flat_string
67
+ output = helper_test_flat_string(RubyProf::FlatPrinter)
68
+ refute_match(/prime.rb/, output)
69
+ end
70
+
71
+ def helper_test_flat_string(klass)
72
+ output = ''
73
+
74
+ printer = klass.new(@result)
75
+ printer.print(output)
76
+
77
+ assert_match(/Thread ID: -?\d+/i, output)
78
+ assert_match(/Fiber ID: -?\d+/i, output)
79
+ assert_match(/Total: \d+\.\d+/i, output)
80
+ assert_match(/Object#run_primes/i, output)
81
+ output
82
+ end
83
+
84
+ def test_flat_string_with_numbers
85
+ output = helper_test_flat_string RubyProf::FlatPrinterWithLineNumbers
86
+ assert_match(/prime.rb/, output)
87
+ refute_match(/ruby_runtime:0/, output)
88
+ assert_match(/called from/, output)
89
+
90
+ # should combine common parents
91
+ # 1.9 inlines it's Fixnum#- so we don't see as many
92
+ assert_equal(2, output.scan(/Object#is_prime/).length)
93
+ refute_match(/\.\/test\/prime.rb/, output) # don't use relative paths
94
+ end
95
+
96
+ def test_graph_html_string
97
+ output = ''
98
+ printer = RubyProf::GraphHtmlPrinter.new(@result)
99
+ printer.print(output)
100
+
101
+ assert_match(/DTD HTML 4\.01/i, output)
102
+ assert_match( %r{<th>Total Time</th>}i, output)
103
+ assert_match(/Object#run_primes/i, output)
104
+ end
105
+
106
+ def test_graph_string
107
+ output = ''
108
+ printer = RubyProf::GraphPrinter.new(@result)
109
+ printer.print(output)
110
+
111
+ assert_match(/Thread ID: -?\d+/i, output)
112
+ assert_match(/Fiber ID: -?\d+/i, output)
113
+ assert_match(/Total Time: \d+\.\d+/i, output)
114
+ assert_match(/Object#run_primes/i, output)
115
+ end
116
+
117
+ def test_call_tree_string
118
+ printer = RubyProf::CallTreePrinter.new(@result)
119
+ printer.print(:profile => "lolcat", :path => RubyProf.tmpdir)
120
+ main_output_file_name = File.join(RubyProf.tmpdir, "lolcat.callgrind.out.#{$$}")
121
+ assert(File.exist?(main_output_file_name))
122
+ output = File.read(main_output_file_name)
123
+ assert_match(/fn=Object::find_primes/i, output)
124
+ assert_match(/events: wall_time/i, output)
125
+ refute_match(/d\d\d\d\d\d/, output) # old bug looked [in error] like Object::run_primes(d5833116)
126
+ end
127
+
128
+ def do_nothing
129
+ start = Time.now
130
+ while(Time.now == start)
131
+ end
132
+ end
133
+
134
+ def test_all_with_small_percentiles
135
+ result = RubyProf.profile do
136
+ sleep 2
137
+ do_nothing
138
+ end
139
+
140
+ # RubyProf::CallTreePrinter doesn't "do" a min_percent
141
+ # RubyProf::FlatPrinter only outputs if self time > percent...
142
+ # RubyProf::FlatPrinterWithLineNumbers same
143
+ for klass in [ RubyProf::GraphPrinter, RubyProf::GraphHtmlPrinter]
144
+ printer = klass.new(result)
145
+ out = ''
146
+ printer.print(out, :min_percent => 0.00000001)
147
+ assert_match(/do_nothing/, out)
148
+ end
149
+
150
+ end
151
+
152
+ def test_flat_result_sorting_by_self_time_is_default
153
+ printer = RubyProf::FlatPrinter.new(@result)
154
+
155
+ printer.print(output = '')
156
+ self_times = flat_output_nth_column_values(output, 3)
157
+
158
+ assert_sorted self_times
159
+ end
160
+
161
+ def test_flat_result_sorting
162
+ printer = RubyProf::FlatPrinter.new(@result)
163
+
164
+ sort_method_with_column_number = {:total_time => 2, :self_time => 3, :wait_time => 4, :children_time => 5}
165
+
166
+ sort_method_with_column_number.each_pair do |sort_method, n|
167
+ printer.print(output = '', :sort_method => sort_method)
168
+ times = flat_output_nth_column_values(output, n)
169
+ assert_sorted times
170
+ end
171
+ end
172
+
173
+ def test_flat_result_with_line_numbers_sorting_by_self_time_is_default
174
+ printer = RubyProf::FlatPrinterWithLineNumbers.new(@result)
175
+
176
+ printer.print(output = '')
177
+ self_times = flat_output_nth_column_values(output, 3)
178
+
179
+ assert_sorted self_times
180
+ end
181
+
182
+ def test_flat_with_line_numbers_result_sorting
183
+ printer = RubyProf::FlatPrinterWithLineNumbers.new(@result)
184
+
185
+ sort_method_with_column_number = {:total_time => 2, :self_time => 3, :wait_time => 4, :children_time => 5}
186
+
187
+ sort_method_with_column_number.each_pair do |sort_method, n|
188
+ printer.print(output = '', :sort_method => sort_method)
189
+ times = flat_output_nth_column_values(output, n)
190
+ assert_sorted times
191
+ end
192
+ end
193
+
194
+ def test_graph_result_sorting_by_total_time_is_default
195
+ printer = RubyProf::GraphPrinter.new(@result)
196
+ printer.print(output = '')
197
+ total_times = graph_output_nth_column_values(output, 3)
198
+
199
+ assert_sorted total_times
200
+ end
201
+
202
+ def test_graph_results_sorting
203
+ printer = RubyProf::GraphPrinter.new(@result)
204
+
205
+ sort_method_with_column_number = {:total_time => 3, :self_time => 4, :wait_time => 5, :children_time => 6}
206
+
207
+ sort_method_with_column_number.each_pair do |sort_method, n|
208
+ printer.print(output = '', :sort_method => sort_method)
209
+ times = graph_output_nth_column_values(output, n)
210
+ assert_sorted times
211
+ end
212
+ end
213
+
214
+ def test_graph_html_result_sorting_by_total_time_is_default
215
+ printer = RubyProf::GraphHtmlPrinter.new(@result)
216
+ printer.print(output = '')
217
+ total_times = graph_html_output_nth_column_values(output, 3)
218
+
219
+ assert_sorted total_times
220
+ end
221
+
222
+ def test_graph_html_result_sorting
223
+ printer = RubyProf::GraphHtmlPrinter.new(@result)
224
+
225
+ sort_method_with_column_number = {:total_time => 3, :self_time => 4, :wait_time => 5, :children_time => 6}
226
+
227
+ sort_method_with_column_number.each_pair do |sort_method, n|
228
+ printer.print(output = '', :sort_method => sort_method)
229
+ times = graph_html_output_nth_column_values(output, n)
230
+ assert_sorted times
231
+ end
232
+ end
233
+
234
+ private
235
+ def flat_output_nth_column_values(output, n)
236
+ only_method_calls = output.split("\n").select { |line| line =~ /^ +\d+/ }
237
+ only_method_calls.collect { |line| line.split(/ +/)[n] }
238
+ end
239
+
240
+ def graph_output_nth_column_values(output, n)
241
+ only_root_calls = output.split("\n").select { |line| line =~ /^ +[\d\.]+%/ }
242
+ only_root_calls.collect { |line| line.split(/ +/)[n] }
243
+ end
244
+
245
+ def graph_html_output_nth_column_values(output, n)
246
+ only_root_calls = output.split('<tr class="method">')
247
+ only_root_calls.delete_at(0)
248
+ only_root_calls.collect {|line| line.scan(/[\d\.]+/)[n - 1] }
249
+ end
250
+
251
+ def assert_sorted array
252
+ array = array.map{|n| n.to_f} # allow for > 10s times to sort right, since lexographically 4.0 > 10.0
253
+ assert_equal array, array.sort.reverse, "Array #{array.inspect} is not sorted"
254
+ end
255
+ end