ruby-prof 0.8.2 → 0.9.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.
- data/CHANGES +23 -13
- data/{README → README.rdoc} +118 -111
- data/Rakefile +14 -26
- data/bin/ruby-prof +16 -4
- data/examples/empty.png +0 -0
- data/examples/graph.dot +106 -0
- data/examples/graph.png +0 -0
- data/examples/minus.png +0 -0
- data/examples/multi.flat.txt +23 -0
- data/examples/multi.graph.html +906 -0
- data/examples/multi.grind.dat +194 -0
- data/examples/multi.stack.html +573 -0
- data/examples/plus.png +0 -0
- data/examples/stack.html +573 -0
- data/ext/ruby_prof/extconf.rb +0 -1
- data/ext/ruby_prof/measure_allocations.h +6 -6
- data/ext/ruby_prof/measure_cpu_time.h +5 -5
- data/ext/ruby_prof/measure_gc_runs.h +1 -1
- data/ext/ruby_prof/measure_gc_time.h +1 -1
- data/ext/ruby_prof/measure_memory.h +4 -4
- data/ext/ruby_prof/measure_process_time.h +1 -1
- data/ext/ruby_prof/measure_wall_time.h +1 -1
- data/ext/ruby_prof/ruby_prof.c +240 -167
- data/ext/ruby_prof/ruby_prof.h +12 -10
- data/ext/ruby_prof/version.h +3 -3
- data/lib/ruby-prof.rb +7 -1
- data/lib/ruby-prof/abstract_printer.rb +5 -5
- data/lib/ruby-prof/aggregate_call_info.rb +9 -3
- data/lib/ruby-prof/call_info.rb +66 -1
- data/lib/ruby-prof/call_stack_printer.rb +751 -0
- data/lib/ruby-prof/call_tree_printer.rb +2 -2
- data/lib/ruby-prof/dot_printer.rb +151 -0
- data/lib/ruby-prof/empty.png +0 -0
- data/lib/ruby-prof/flat_printer.rb +16 -16
- data/lib/ruby-prof/flat_printer_with_line_numbers.rb +13 -13
- data/lib/ruby-prof/graph_html_printer.rb +58 -36
- data/lib/ruby-prof/graph_printer.rb +30 -30
- data/lib/ruby-prof/method_info.rb +24 -4
- data/lib/ruby-prof/minus.png +0 -0
- data/lib/ruby-prof/multi_printer.rb +54 -0
- data/lib/ruby-prof/plus.png +0 -0
- data/lib/ruby-prof/rack.rb +28 -0
- data/lib/ruby-prof/result.rb +70 -0
- data/lib/ruby-prof/symbol_to_proc.rb +1 -1
- data/lib/ruby-prof/task.rb +19 -19
- data/lib/ruby-prof/test.rb +3 -3
- data/lib/ruby_prof.so +0 -0
- data/rails/environment/profile.rb +2 -2
- data/rails/example/example_test.rb +2 -2
- data/rails/profile_test_helper.rb +1 -1
- data/test/aggregate_test.rb +21 -6
- data/test/basic_test.rb +3 -3
- data/test/duplicate_names_test.rb +2 -2
- data/test/enumerable_test.rb +2 -2
- data/test/exceptions_test.rb +2 -2
- data/test/exclude_threads_test.rb +45 -45
- data/test/exec_test.rb +2 -2
- data/test/line_number_test.rb +11 -11
- data/test/measurement_test.rb +4 -3
- data/test/method_elimination_test.rb +74 -0
- data/test/module_test.rb +7 -7
- data/test/multi_printer_test.rb +81 -0
- data/test/no_method_class_test.rb +2 -2
- data/test/prime.rb +7 -10
- data/test/printers_test.rb +57 -47
- data/test/recursive_test.rb +23 -62
- data/test/singleton_test.rb +3 -2
- data/test/stack_printer_test.rb +74 -0
- data/test/stack_test.rb +1 -1
- data/test/start_stop_test.rb +2 -2
- data/test/test_suite.rb +9 -0
- data/test/thread_test.rb +17 -17
- data/test/unique_call_path_test.rb +4 -4
- metadata +29 -8
data/lib/ruby-prof/test.rb
CHANGED
@@ -15,7 +15,7 @@ module RubyProf
|
|
15
15
|
def output_dir
|
16
16
|
PROFILE_OPTIONS[:output_dir]
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def run(result)
|
20
20
|
return if @method_name.to_s == "default_test"
|
21
21
|
|
@@ -108,7 +108,7 @@ module RubyProf
|
|
108
108
|
def report_profile(data, measure_mode)
|
109
109
|
PROFILE_OPTIONS[:printers].each do |printer_klass|
|
110
110
|
printer = printer_klass.new(data)
|
111
|
-
|
111
|
+
|
112
112
|
# Makes sure the output directory exits
|
113
113
|
FileUtils.mkdir_p(output_dir)
|
114
114
|
|
@@ -145,4 +145,4 @@ module RubyProf
|
|
145
145
|
end
|
146
146
|
end
|
147
147
|
end
|
148
|
-
end
|
148
|
+
end
|
data/lib/ruby_prof.so
CHANGED
Binary file
|
@@ -5,7 +5,7 @@
|
|
5
5
|
# definitely not use the production databse!
|
6
6
|
|
7
7
|
|
8
|
-
# Cache classes - otherwise your code
|
8
|
+
# Cache classes - otherwise your code
|
9
9
|
# will run approximately 5 times slower and the
|
10
10
|
# profiling results will be overwhelmed by Rails
|
11
11
|
# dependency loading mechanism
|
@@ -21,4 +21,4 @@ config.action_view.cache_template_loading = true
|
|
21
21
|
config.action_controller.perform_caching = false
|
22
22
|
|
23
23
|
# Turn off most logging
|
24
|
-
config.log_level = :info
|
24
|
+
config.log_level = :info
|
data/test/aggregate_test.rb
CHANGED
@@ -14,7 +14,7 @@ class AggClass
|
|
14
14
|
def z
|
15
15
|
sleep 1
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def a
|
19
19
|
z
|
20
20
|
end
|
@@ -24,7 +24,7 @@ class AggClass
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def c
|
27
|
-
|
27
|
+
a
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -34,6 +34,21 @@ class AggregateTest < Test::Unit::TestCase
|
|
34
34
|
RubyProf::measure_mode = RubyProf::WALL_TIME
|
35
35
|
end
|
36
36
|
|
37
|
+
def test_all_call_infos_are_minimal_as_there_is_no_recursion
|
38
|
+
c1 = AggClass.new
|
39
|
+
result = RubyProf.profile do
|
40
|
+
c1.a
|
41
|
+
c1.b
|
42
|
+
c1.c
|
43
|
+
end
|
44
|
+
methods = result.threads.values.first.sort.reverse
|
45
|
+
methods.each do |m|
|
46
|
+
m.call_infos.each do |ci|
|
47
|
+
assert ci.minimal?, "#{ci.call_sequence} should be minimal in the call tree"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
37
52
|
def test_call_infos
|
38
53
|
c1 = AggClass.new
|
39
54
|
result = RubyProf.profile do
|
@@ -43,7 +58,7 @@ class AggregateTest < Test::Unit::TestCase
|
|
43
58
|
end
|
44
59
|
|
45
60
|
methods = result.threads.values.first.sort.reverse
|
46
|
-
method = methods.find {|
|
61
|
+
method = methods.find {|meth| meth.full_name == 'AggClass#z'}
|
47
62
|
|
48
63
|
# Check AggClass#z
|
49
64
|
assert_equal('AggClass#z', method.full_name)
|
@@ -76,7 +91,7 @@ class AggregateTest < Test::Unit::TestCase
|
|
76
91
|
end
|
77
92
|
|
78
93
|
methods = result.threads.values.first.sort.reverse
|
79
|
-
method = methods.find {|
|
94
|
+
method = methods.find {|meth| meth.full_name == 'AggClass#z'}
|
80
95
|
|
81
96
|
# Check AggClass#z
|
82
97
|
assert_equal('AggClass#z', method.full_name)
|
@@ -102,7 +117,7 @@ class AggregateTest < Test::Unit::TestCase
|
|
102
117
|
end
|
103
118
|
|
104
119
|
methods = result.threads.values.first.sort.reverse
|
105
|
-
method = methods.find {|
|
120
|
+
method = methods.find {|meth| meth.full_name == 'AggClass#a'}
|
106
121
|
|
107
122
|
# Check AggClass#a
|
108
123
|
assert_equal('AggClass#a', method.full_name)
|
@@ -118,4 +133,4 @@ class AggregateTest < Test::Unit::TestCase
|
|
118
133
|
assert_in_delta(3, call_info.children_time, 0.05)
|
119
134
|
assert_equal(3, call_info.called)
|
120
135
|
end
|
121
|
-
end
|
136
|
+
end
|
data/test/basic_test.rb
CHANGED
@@ -7,7 +7,7 @@ class C1
|
|
7
7
|
def C1.hello
|
8
8
|
sleep(0.1)
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def hello
|
12
12
|
sleep(0.2)
|
13
13
|
end
|
@@ -115,7 +115,7 @@ class BasicTest < Test::Unit::TestCase
|
|
115
115
|
assert_in_delta(0, methods[2].wait_time, 0.01)
|
116
116
|
assert_in_delta(0.1, methods[2].self_time, 0.01)
|
117
117
|
end
|
118
|
-
|
118
|
+
|
119
119
|
if RUBY_VERSION < '1.9'
|
120
120
|
PARENT = Object
|
121
121
|
else
|
@@ -225,7 +225,7 @@ class BasicTest < Test::Unit::TestCase
|
|
225
225
|
assert_equal('Class#new', names[3])
|
226
226
|
assert(names.include?("<Class::#{PARENT}>#allocate"))
|
227
227
|
assert(names.include?("#{PARENT}#initialize"))
|
228
|
-
|
228
|
+
|
229
229
|
# Check times
|
230
230
|
assert_in_delta(0.3, methods[0].total_time, 0.1)
|
231
231
|
assert_in_delta(0, methods[0].wait_time, 0.1)
|
@@ -19,10 +19,10 @@ class DuplicateNames < Test::Unit::TestCase
|
|
19
19
|
eval str
|
20
20
|
Foo::Bar.new.foo
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
# There should be 3 foo methods
|
24
24
|
methods = result.threads.values.first.sort.reverse
|
25
|
-
|
25
|
+
|
26
26
|
methods = methods.select do |method|
|
27
27
|
method.full_name == 'DuplicateNames::Foo::Bar#foo'
|
28
28
|
end
|
data/test/enumerable_test.rb
CHANGED
data/test/exceptions_test.rb
CHANGED
@@ -5,11 +5,11 @@ require 'ruby-prof'
|
|
5
5
|
class ExceptionsTest < Test::Unit::TestCase
|
6
6
|
def test_profile
|
7
7
|
result = begin
|
8
|
-
RubyProf.profile do
|
8
|
+
RubyProf.profile do
|
9
9
|
raise(RuntimeError, 'Test error')
|
10
10
|
end
|
11
11
|
rescue => e
|
12
|
-
end
|
12
|
+
end
|
13
13
|
assert_not_nil(result)
|
14
14
|
end
|
15
15
|
end
|
@@ -6,49 +6,49 @@ require 'ruby-prof'
|
|
6
6
|
|
7
7
|
# -- Tests ----
|
8
8
|
class ExcludeThreadsTest < Test::Unit::TestCase
|
9
|
-
def test_exclude_threads
|
10
|
-
|
11
|
-
def thread1_proc
|
12
|
-
sleep(0.5)
|
13
|
-
sleep(2)
|
14
|
-
end
|
15
|
-
|
16
|
-
def thread2_proc
|
17
|
-
sleep(0.5)
|
18
|
-
sleep(2)
|
19
|
-
end
|
20
|
-
|
21
|
-
thread1 = Thread.new do
|
22
|
-
thread1_proc
|
23
|
-
end
|
24
|
-
|
25
|
-
thread2 = Thread.new do
|
26
|
-
thread2_proc
|
27
|
-
end
|
28
|
-
|
29
|
-
RubyProf::exclude_threads = [ thread2 ]
|
30
|
-
|
31
|
-
RubyProf.start
|
32
|
-
|
33
|
-
thread1.join
|
34
|
-
thread2.join
|
35
|
-
|
36
|
-
result = RubyProf.stop
|
37
|
-
|
38
|
-
RubyProf::exclude_threads = nil
|
39
|
-
|
40
|
-
assert_equal(2, result.threads.length)
|
41
|
-
|
42
|
-
output = Array.new
|
43
|
-
result.threads.each do | thread_id, methods |
|
44
|
-
methods.each do | m |
|
45
|
-
if m.full_name.index("ExcludeThreadsTest#thread") == 0
|
46
|
-
output.push(m.full_name)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
assert_equal(1, output.length)
|
52
|
-
assert_equal("ExcludeThreadsTest#thread1_proc", output[0])
|
9
|
+
def test_exclude_threads
|
10
|
+
|
11
|
+
def thread1_proc
|
12
|
+
sleep(0.5)
|
13
|
+
sleep(2)
|
14
|
+
end
|
15
|
+
|
16
|
+
def thread2_proc
|
17
|
+
sleep(0.5)
|
18
|
+
sleep(2)
|
19
|
+
end
|
20
|
+
|
21
|
+
thread1 = Thread.new do
|
22
|
+
thread1_proc
|
23
|
+
end
|
24
|
+
|
25
|
+
thread2 = Thread.new do
|
26
|
+
thread2_proc
|
27
|
+
end
|
28
|
+
|
29
|
+
RubyProf::exclude_threads = [ thread2 ]
|
30
|
+
|
31
|
+
RubyProf.start
|
32
|
+
|
33
|
+
thread1.join
|
34
|
+
thread2.join
|
35
|
+
|
36
|
+
result = RubyProf.stop
|
37
|
+
|
38
|
+
RubyProf::exclude_threads = nil
|
39
|
+
|
40
|
+
assert_equal(2, result.threads.length)
|
41
|
+
|
42
|
+
output = Array.new
|
43
|
+
result.threads.each do | thread_id, methods |
|
44
|
+
methods.each do | m |
|
45
|
+
if m.full_name.index("ExcludeThreadsTest#thread") == 0
|
46
|
+
output.push(m.full_name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
assert_equal(1, output.length)
|
52
|
+
assert_equal("ExcludeThreadsTest#thread1_proc", output[0])
|
53
53
|
end
|
54
|
-
end
|
54
|
+
end
|
data/test/exec_test.rb
CHANGED
data/test/line_number_test.rb
CHANGED
@@ -7,12 +7,12 @@ class LineNumbers
|
|
7
7
|
def method1
|
8
8
|
a = 3
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def method2
|
12
12
|
a = 3
|
13
13
|
method1
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def method3
|
17
17
|
sleep(1)
|
18
18
|
end
|
@@ -22,30 +22,30 @@ end
|
|
22
22
|
class LineNumbersTest < Test::Unit::TestCase
|
23
23
|
def test_function_line_no
|
24
24
|
numbers = LineNumbers.new
|
25
|
-
|
25
|
+
|
26
26
|
result = RubyProf.profile do
|
27
27
|
numbers.method2
|
28
28
|
end
|
29
29
|
|
30
30
|
methods = result.threads.values.first.sort.reverse
|
31
31
|
assert_equal(3, methods.length)
|
32
|
-
|
32
|
+
|
33
33
|
method = methods[0]
|
34
34
|
assert_equal('LineNumbersTest#test_function_line_no', method.full_name)
|
35
35
|
assert_equal(27, method.line)
|
36
|
-
|
36
|
+
|
37
37
|
method = methods[1]
|
38
38
|
assert_equal('LineNumbers#method2', method.full_name)
|
39
39
|
assert_equal(11, method.line)
|
40
|
-
|
40
|
+
|
41
41
|
method = methods[2]
|
42
42
|
assert_equal('LineNumbers#method1', method.full_name)
|
43
43
|
assert_equal(7, method.line)
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
def test_c_function
|
47
47
|
numbers = LineNumbers.new
|
48
|
-
|
48
|
+
|
49
49
|
result = RubyProf.profile do
|
50
50
|
numbers.method3
|
51
51
|
end
|
@@ -61,13 +61,13 @@ class LineNumbersTest < Test::Unit::TestCase
|
|
61
61
|
method = methods[0]
|
62
62
|
assert_equal('Kernel#sleep', method.full_name)
|
63
63
|
assert_equal(0, method.line)
|
64
|
-
|
64
|
+
|
65
65
|
method = methods[1]
|
66
66
|
assert_equal('LineNumbers#method3', method.full_name)
|
67
67
|
assert_equal(16, method.line)
|
68
|
-
|
68
|
+
|
69
69
|
method = methods[2]
|
70
70
|
assert_equal('LineNumbersTest#test_c_function', method.full_name)
|
71
71
|
assert_equal(50, method.line)
|
72
72
|
end
|
73
|
-
end
|
73
|
+
end
|
data/test/measurement_test.rb
CHANGED
@@ -42,7 +42,7 @@ class MeasurementTest < Test::Unit::TestCase
|
|
42
42
|
RubyProf::measure_mode = RubyProf::CPU_TIME
|
43
43
|
assert_equal(RubyProf::CPU_TIME, RubyProf::measure_mode)
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
def test_cpu_time
|
47
47
|
RubyProf.cpu_frequency = 2.33e9
|
48
48
|
|
@@ -82,8 +82,9 @@ class MeasurementTest < Test::Unit::TestCase
|
|
82
82
|
u = RubyProf.measure_memory
|
83
83
|
assert(u >= t, [t, u].inspect)
|
84
84
|
|
85
|
+
RubyProf::measure_mode = RubyProf::MEMORY
|
85
86
|
result = RubyProf.profile {Array.new}
|
86
|
-
total = result.threads.values.first.
|
87
|
+
total = result.threads.values.first.inject(0) { |sum, m| sum + m.total_time }
|
87
88
|
|
88
89
|
assert(total > 0, 'Should measure more than zero kilobytes of memory usage')
|
89
90
|
assert_not_equal(0, total % 1, 'Should not truncate fractional kilobyte measurements')
|
@@ -118,4 +119,4 @@ class MeasurementTest < Test::Unit::TestCase
|
|
118
119
|
assert u > t, [t, u].inspect
|
119
120
|
end
|
120
121
|
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
|