airbnb-ruby-prof 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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