acunote-ruby-prof 0.9.2

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 (84) hide show
  1. data/CHANGES +240 -0
  2. data/LICENSE +23 -0
  3. data/README.rdoc +439 -0
  4. data/Rakefile +148 -0
  5. data/bin/ruby-prof +236 -0
  6. data/examples/empty.png +0 -0
  7. data/examples/flat.txt +55 -0
  8. data/examples/graph.dot +106 -0
  9. data/examples/graph.html +823 -0
  10. data/examples/graph.png +0 -0
  11. data/examples/graph.txt +170 -0
  12. data/examples/minus.png +0 -0
  13. data/examples/multi.flat.txt +23 -0
  14. data/examples/multi.graph.html +906 -0
  15. data/examples/multi.grind.dat +194 -0
  16. data/examples/multi.stack.html +573 -0
  17. data/examples/plus.png +0 -0
  18. data/examples/stack.html +573 -0
  19. data/ext/ruby_prof/extconf.rb +43 -0
  20. data/ext/ruby_prof/measure_allocations.h +58 -0
  21. data/ext/ruby_prof/measure_cpu_time.h +152 -0
  22. data/ext/ruby_prof/measure_gc_runs.h +76 -0
  23. data/ext/ruby_prof/measure_gc_time.h +57 -0
  24. data/ext/ruby_prof/measure_memory.h +101 -0
  25. data/ext/ruby_prof/measure_process_time.h +52 -0
  26. data/ext/ruby_prof/measure_wall_time.h +53 -0
  27. data/ext/ruby_prof/mingw/Rakefile +23 -0
  28. data/ext/ruby_prof/mingw/build.rake +38 -0
  29. data/ext/ruby_prof/ruby_prof.c +1834 -0
  30. data/ext/ruby_prof/ruby_prof.h +190 -0
  31. data/ext/ruby_prof/version.h +4 -0
  32. data/lib/ruby-prof.rb +62 -0
  33. data/lib/ruby-prof/abstract_printer.rb +41 -0
  34. data/lib/ruby-prof/aggregate_call_info.rb +68 -0
  35. data/lib/ruby-prof/call_info.rb +112 -0
  36. data/lib/ruby-prof/call_stack_printer.rb +751 -0
  37. data/lib/ruby-prof/call_tree_printer.rb +133 -0
  38. data/lib/ruby-prof/dot_printer.rb +153 -0
  39. data/lib/ruby-prof/empty.png +0 -0
  40. data/lib/ruby-prof/flat_printer.rb +78 -0
  41. data/lib/ruby-prof/flat_printer_with_line_numbers.rb +72 -0
  42. data/lib/ruby-prof/graph_html_printer.rb +278 -0
  43. data/lib/ruby-prof/graph_printer.rb +245 -0
  44. data/lib/ruby-prof/method_info.rb +131 -0
  45. data/lib/ruby-prof/minus.png +0 -0
  46. data/lib/ruby-prof/multi_printer.rb +54 -0
  47. data/lib/ruby-prof/plus.png +0 -0
  48. data/lib/ruby-prof/rack.rb +30 -0
  49. data/lib/ruby-prof/result.rb +70 -0
  50. data/lib/ruby-prof/symbol_to_proc.rb +8 -0
  51. data/lib/ruby-prof/task.rb +146 -0
  52. data/lib/ruby-prof/test.rb +148 -0
  53. data/lib/unprof.rb +8 -0
  54. data/rails/environment/profile.rb +24 -0
  55. data/rails/example/example_test.rb +9 -0
  56. data/rails/profile_test_helper.rb +21 -0
  57. data/test/aggregate_test.rb +136 -0
  58. data/test/basic_test.rb +290 -0
  59. data/test/current_failures_windows +8 -0
  60. data/test/do_nothing.rb +0 -0
  61. data/test/duplicate_names_test.rb +32 -0
  62. data/test/enumerable_test.rb +16 -0
  63. data/test/exceptions_test.rb +15 -0
  64. data/test/exclude_threads_test.rb +54 -0
  65. data/test/exec_test.rb +14 -0
  66. data/test/line_number_test.rb +73 -0
  67. data/test/measurement_test.rb +122 -0
  68. data/test/method_elimination_test.rb +74 -0
  69. data/test/module_test.rb +44 -0
  70. data/test/multi_printer_test.rb +81 -0
  71. data/test/no_method_class_test.rb +13 -0
  72. data/test/prime.rb +55 -0
  73. data/test/prime_test.rb +13 -0
  74. data/test/printers_test.rb +164 -0
  75. data/test/recursive_test.rb +236 -0
  76. data/test/ruby-prof-bin +20 -0
  77. data/test/singleton_test.rb +38 -0
  78. data/test/stack_printer_test.rb +74 -0
  79. data/test/stack_test.rb +138 -0
  80. data/test/start_stop_test.rb +112 -0
  81. data/test/test_suite.rb +32 -0
  82. data/test/thread_test.rb +173 -0
  83. data/test/unique_call_path_test.rb +225 -0
  84. metadata +185 -0
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ require 'ruby-prof'
4
+
5
+ class MeasurementTest < Test::Unit::TestCase
6
+ def setup
7
+ GC.enable_stats if GC.respond_to?(:enable_stats)
8
+ end
9
+
10
+ def teardown
11
+ GC.disable_stats if GC.respond_to?(:disable_stats)
12
+ end
13
+
14
+ def test_process_time_mode
15
+ RubyProf::measure_mode = RubyProf::PROCESS_TIME
16
+ assert_equal(RubyProf::PROCESS_TIME, RubyProf::measure_mode)
17
+ end
18
+
19
+ def test_process_time
20
+ t = RubyProf.measure_process_time
21
+ assert_kind_of(Float, t)
22
+
23
+ u = RubyProf.measure_process_time
24
+ assert(u >= t, [t, u].inspect)
25
+ end
26
+
27
+ def test_wall_time_mode
28
+ RubyProf::measure_mode = RubyProf::WALL_TIME
29
+ assert_equal(RubyProf::WALL_TIME, RubyProf::measure_mode)
30
+ end
31
+
32
+ def test_wall_time
33
+ t = RubyProf.measure_wall_time
34
+ assert_kind_of Float, t
35
+
36
+ u = RubyProf.measure_wall_time
37
+ assert u >= t, [t, u].inspect
38
+ end
39
+
40
+ if RubyProf::CPU_TIME
41
+ def test_cpu_time_mode
42
+ RubyProf::measure_mode = RubyProf::CPU_TIME
43
+ assert_equal(RubyProf::CPU_TIME, RubyProf::measure_mode)
44
+ end
45
+
46
+ def test_cpu_time
47
+ RubyProf.cpu_frequency = 2.33e9
48
+
49
+ t = RubyProf.measure_cpu_time
50
+ assert_kind_of Float, t
51
+
52
+ u = RubyProf.measure_cpu_time
53
+ assert u > t, [t, u].inspect
54
+ end
55
+ end
56
+
57
+ if RubyProf::ALLOCATIONS
58
+ def test_allocations_mode
59
+ RubyProf::measure_mode = RubyProf::ALLOCATIONS
60
+ assert_equal(RubyProf::ALLOCATIONS, RubyProf::measure_mode)
61
+ end
62
+
63
+ def test_allocations
64
+ t = RubyProf.measure_allocations
65
+ assert_kind_of Integer, t
66
+
67
+ u = RubyProf.measure_allocations
68
+ assert u > t, [t, u].inspect
69
+ end
70
+ end
71
+
72
+ if RubyProf::MEMORY
73
+ def test_memory_mode
74
+ RubyProf::measure_mode = RubyProf::MEMORY
75
+ assert_equal(RubyProf::MEMORY, RubyProf::measure_mode)
76
+ end
77
+
78
+ def test_memory
79
+ t = RubyProf.measure_memory
80
+ assert_kind_of Integer, t
81
+
82
+ u = RubyProf.measure_memory
83
+ assert(u >= t, [t, u].inspect)
84
+
85
+ RubyProf::measure_mode = RubyProf::MEMORY
86
+ result = RubyProf.profile {Array.new}
87
+ total = result.threads.values.first.inject(0) { |sum, m| sum + m.total_time }
88
+
89
+ assert(total > 0, 'Should measure more than zero kilobytes of memory usage')
90
+ assert_not_equal(0, total % 1, 'Should not truncate fractional kilobyte measurements')
91
+ end
92
+ end
93
+
94
+ if RubyProf::GC_RUNS
95
+ def test_gc_runs_mode
96
+ RubyProf::measure_mode = RubyProf::GC_RUNS
97
+ assert_equal(RubyProf::GC_RUNS, RubyProf::measure_mode)
98
+ end
99
+
100
+ def test_gc_runs
101
+ t = RubyProf.measure_gc_runs
102
+ assert_kind_of Integer, t
103
+
104
+ GC.start
105
+
106
+ u = RubyProf.measure_gc_runs
107
+ assert u > t, [t, u].inspect
108
+ end
109
+ end
110
+
111
+ if RubyProf::GC_TIME
112
+ def test_gc_time
113
+ t = RubyProf.measure_gc_time
114
+ assert_kind_of Integer, t
115
+
116
+ GC.start
117
+
118
+ u = RubyProf.measure_gc_time
119
+ assert u > t, [t, u].inspect
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'ruby-prof'
5
+ require 'tmpdir'
6
+
7
+ # Test data
8
+ # A
9
+ # / \
10
+ # B C
11
+ # \
12
+ # B
13
+
14
+ class ESTPT
15
+ def a
16
+ 100.times{b}
17
+ 300.times{c}
18
+ c;c;c
19
+ end
20
+
21
+ def b
22
+ sleep 0
23
+ end
24
+
25
+ def c
26
+ 5.times{b}
27
+ end
28
+ end
29
+
30
+ class MethodEliminationTest < Test::Unit::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_setting_parent
37
+ result = RubyProf.profile do
38
+ 1000.times { 1+1 }
39
+ end
40
+ method_infos = result.threads.values.first
41
+ assert(m1 = method_infos[0])
42
+ assert(c1 = m1.call_infos.first)
43
+ assert_equal(c1, c1.parent = c1)
44
+ assert_equal c1, c1.parent
45
+ end
46
+
47
+ def test_methods_can_be_eliminated
48
+ RubyProf.start
49
+ 5.times{ESTPT.new.a}
50
+ result = RubyProf.stop
51
+ # result.dump
52
+ eliminated = result.eliminate_methods!([/Integer#times/])
53
+ # puts eliminated.inspect
54
+ # result.dump
55
+ eliminated.each do |m|
56
+ assert_method_has_been_eliminated(result, m)
57
+ end
58
+ end
59
+
60
+ private
61
+ def assert_method_has_been_eliminated(result, eliminated_method)
62
+ result.threads.each do |thread_id, methods|
63
+ methods.each do |method|
64
+ method.call_infos.each do |ci|
65
+ assert(ci.target != eliminated_method, "broken self")
66
+ assert(ci.parent.target != eliminated_method, "broken parent") if ci.parent
67
+ ci.children.each do |callee|
68
+ assert(callee.target != eliminated_method, "broken kid")
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ require 'ruby-prof'
4
+
5
+ # Need to use wall time for this test due to the sleep calls
6
+ RubyProf::measure_mode = RubyProf::WALL_TIME
7
+
8
+ module Foo
9
+ def Foo::hello
10
+ sleep(0.5)
11
+ end
12
+ end
13
+
14
+ module Bar
15
+ def Bar::hello
16
+ sleep(0.5)
17
+ Foo::hello
18
+ end
19
+
20
+ def hello
21
+ sleep(0.5)
22
+ Bar::hello
23
+ end
24
+ end
25
+
26
+ include Bar
27
+
28
+ class ModuleTest < Test::Unit::TestCase
29
+ def test_nested_modules
30
+ result = RubyProf.profile do
31
+ hello
32
+ end
33
+
34
+ methods = result.threads.values.first
35
+
36
+ # Length should be 5
37
+ assert_equal(5, methods.length)
38
+
39
+ # these methods should be in there... (hard to tell order though).
40
+ for name in ['ModuleTest#test_nested_modules','Bar#hello','Kernel#sleep','<Module::Bar>#hello','<Module::Foo>#hello']
41
+ assert methods.map(&:full_name).include? name
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'ruby-prof'
5
+ require 'tmpdir'
6
+
7
+ # Test data
8
+ # A
9
+ # / \
10
+ # B C
11
+ # \
12
+ # B
13
+
14
+ class MSTPT
15
+ def a
16
+ 100.times{b}
17
+ 300.times{c}
18
+ c;c;c
19
+ end
20
+
21
+ def b
22
+ sleep 0
23
+ end
24
+
25
+ def c
26
+ 5.times{b}
27
+ end
28
+ end
29
+
30
+ class MultiPrinterTest < Test::Unit::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_all_profiles_can_be_created
37
+ start_time = Time.now
38
+ RubyProf.start
39
+ 5.times{MSTPT.new.a}
40
+ result = RubyProf.stop
41
+ end_time = Time.now
42
+ expected_time = end_time - start_time
43
+ stack = graph = nil
44
+ assert_nothing_raised { stack, graph = print(result) }
45
+ re = Regexp.new('
46
+ \s*<table>
47
+ \s*<tr>
48
+ \s*<th>Thread ID</th>
49
+ \s*<th>Total Time</th>
50
+ \s*</tr>
51
+ \s*
52
+ \s*<tr>
53
+ \s*<td><a href="#\d+">\d+</a></td>
54
+ \s*<td>([\.0-9]+)</td>
55
+ \s*</tr>
56
+ \s*
57
+ \s*</table>')
58
+ assert graph =~ re
59
+ display_time = $1.to_f
60
+ difference = (expected_time-display_time).abs
61
+ assert_in_delta expected_time, display_time, 0.005
62
+ end
63
+
64
+ private
65
+ def print(result)
66
+ test = caller.first =~ /in `(.*)'/ ? $1 : "test"
67
+ path = Dir::tmpdir
68
+ profile = "ruby_prof_#{test}"
69
+ printer = RubyProf::MultiPrinter.new(result)
70
+ printer.print(:path => path, :profile => profile,
71
+ :threshold => 0, :min_percent => 0, :title => "ruby_prof #{test}")
72
+ if RUBY_PLATFORM =~ /darwin/ && ENV['SHOW_RUBY_PROF_PRINTER_OUTPUT']=="1"
73
+ system("open '#{printer.stack_profile}'")
74
+ end
75
+ if GC.respond_to?(:dump_file_and_line_info)
76
+ GC.start
77
+ GC.dump_file_and_line_info("heap.dump")
78
+ end
79
+ [File.open(printer.stack_profile){|f|f.read}, File.open(printer.graph_profile){|f|f.read}]
80
+ end
81
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ruby-prof'
3
+
4
+ # Make sure this works with no class or method
5
+ result = RubyProf.profile do
6
+ sleep 1
7
+ end
8
+
9
+ methods = result.threads.values.first
10
+ global_method = methods.sort_by {|method| method.full_name}.first
11
+ if global_method.full_name != 'Global#[No method]'
12
+ raise(RuntimeError, "Wrong method name. Expected: Global#[No method]. Actual: #{global_method.full_name}")
13
+ end
@@ -0,0 +1,55 @@
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
+ sleep(0.02)
38
+ prime = primes[i]
39
+ if prime > largest
40
+ largest = prime
41
+ end
42
+ end
43
+ largest
44
+ end
45
+
46
+ def run_primes(length=10, maxnum=1000)
47
+ # Create random numbers
48
+ random_array = make_random_array(length, maxnum)
49
+
50
+ # Find the primes
51
+ primes = find_primes(random_array)
52
+
53
+ # Find the largest primes
54
+ # largest = find_largest(primes)
55
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ require 'ruby-prof'
4
+ require 'prime'
5
+
6
+ # -- Tests ----
7
+ class PrimeTest< Test::Unit::TestCase
8
+ def test_consistency
9
+ result = RubyProf.profile do
10
+ run_primes
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ require 'ruby-prof'
4
+ require 'prime'
5
+ require 'stringio'
6
+ require 'fileutils'
7
+ require 'rubygems'
8
+
9
+ # -- Tests ----
10
+ class PrintersTest < Test::Unit::TestCase
11
+
12
+ def go
13
+ run_primes(1000)
14
+ end
15
+
16
+ def setup
17
+ RubyProf::measure_mode = RubyProf::WALL_TIME # WALL_TIME so we can use sleep in our test and get same measurements on linux and doze
18
+ @result = RubyProf.profile do
19
+ begin
20
+ run_primes(1000)
21
+ go
22
+ rescue => e
23
+ p e
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ def test_printers
30
+ assert_nothing_raised do
31
+ output = ENV['SHOW_RUBY_PROF_PRINTER_OUTPUT'] == "1" ? STDOUT : StringIO.new('')
32
+
33
+ printer = RubyProf::FlatPrinter.new(@result)
34
+ printer.print(output)
35
+
36
+ printer = RubyProf::FlatPrinterWithLineNumbers.new(@result)
37
+ printer.print(output)
38
+
39
+ printer = RubyProf::GraphHtmlPrinter.new(@result)
40
+ printer.print(output)
41
+
42
+ printer = RubyProf::GraphPrinter.new(@result)
43
+ printer.print(output)
44
+
45
+ printer = RubyProf::CallTreePrinter.new(@result)
46
+ printer.print(output)
47
+ output_dir = 'examples2'
48
+
49
+ if ENV['SAVE_NEW_PRINTER_EXAMPLES']
50
+ output_dir = 'examples'
51
+ end
52
+ FileUtils.mkdir_p output_dir
53
+
54
+ printer = RubyProf::DotPrinter.new(@result)
55
+ File.open("#{output_dir}/graph.dot", "w") {|f| printer.print(f)}
56
+
57
+ printer = RubyProf::CallStackPrinter.new(@result)
58
+ File.open("#{output_dir}/stack.html", "w") {|f| printer.print(f, :application => "primes")}
59
+
60
+ printer = RubyProf::MultiPrinter.new(@result)
61
+ printer.print(:path => "#{output_dir}", :profile => "multi", :application => "primes")
62
+ for file in ['empty.png', 'graph.dot', 'minus.png', 'multi.flat.txt', 'multi.graph.html', 'multi.grind.dat', 'multi.stack.html', 'plus.png', 'stack.html']
63
+ existant_file = output_dir + '/' + file
64
+ assert File.size(existant_file) > 0
65
+ end
66
+ end
67
+ end
68
+
69
+ def test_flat_string
70
+ output = helper_test_flat_string RubyProf::FlatPrinter
71
+ assert_no_match(/prime.rb/, output)
72
+ end
73
+
74
+ def helper_test_flat_string klass
75
+ output = ''
76
+
77
+ printer = klass.new(@result)
78
+ printer.print(output)
79
+
80
+ assert_match(/Thread ID: -?\d+/i, output)
81
+ assert_match(/Total: \d+\.\d+/i, output)
82
+ assert_match(/Object#run_primes/i, output)
83
+ output
84
+ end
85
+
86
+ def test_flat_string_with_numbers
87
+ output = helper_test_flat_string RubyProf::FlatPrinterWithLineNumbers
88
+ assert_match(/prime.rb/, output)
89
+ assert_no_match(/ruby_runtime:0/, output)
90
+ assert_match(/called from/, output)
91
+
92
+ # should combine common parents
93
+ # lodo remove...
94
+ #if RUBY_VERSION < '1.9'
95
+ #require 'ruby-debug'
96
+ #debugger
97
+ #print output
98
+ assert_equal(3, output.scan(/Object#is_prime/).length) # failing this is prolly a 1.9.2 core bug
99
+ #else
100
+ # # 1.9
101
+ # require 'ruby-debug'
102
+ # debugger
103
+ # assert_equal(2, output.scan(/Object#is_prime/).length)
104
+ #end
105
+ assert_no_match(/\.\/test\/prime.rb/, output) # don't use relative paths
106
+ end
107
+
108
+ def test_graph_html_string
109
+ output = ''
110
+ printer = RubyProf::GraphHtmlPrinter.new(@result)
111
+ printer.print(output)
112
+
113
+ assert_match( /DTD HTML 4\.01/i, output )
114
+ assert_match( %r{<th>Total Time</th>}i, output )
115
+ assert_match( /Object#run_primes/i, output )
116
+ end
117
+
118
+ def test_graph_string
119
+ output = ''
120
+ printer = RubyProf::GraphPrinter.new(@result)
121
+ printer.print(output)
122
+
123
+ assert_match( /Thread ID: -?\d+/i, output )
124
+ assert_match( /Total Time: \d+\.\d+/i, output )
125
+ assert_match( /Object#run_primes/i, output )
126
+ end
127
+
128
+ def test_call_tree_string
129
+ output = ''
130
+ printer = RubyProf::CallTreePrinter.new(@result)
131
+ printer.print(output)
132
+ assert_match(/fn=Object#find_primes/i, output)
133
+ assert_match(/events: wall_time/i, output)
134
+ assert_no_match(/d\d\d\d\d\d/, output) # old bug looked [in error] like Object::run_primes(d5833116)
135
+ end
136
+
137
+ def do_nothing
138
+ start = Time.now
139
+ while(Time.now == start)
140
+ end
141
+ end
142
+
143
+ def test_all_with_small_percentiles
144
+
145
+ result = RubyProf.profile do
146
+ sleep 2
147
+ do_nothing
148
+ end
149
+
150
+ # RubyProf::CallTreePrinter doesn't "do" a min_percent
151
+ # RubyProf::FlatPrinter only outputs if self time > percent...
152
+ # RubyProf::FlatPrinterWithLineNumbers same
153
+ for klass in [ RubyProf::GraphPrinter, RubyProf::GraphHtmlPrinter]
154
+ printer = klass.new(result)
155
+ out = ''
156
+ output = printer.print(out, :min_percent => 0.00000001 )
157
+ assert_match(/do_nothing/, out)
158
+ end
159
+
160
+ end
161
+
162
+
163
+
164
+ end