ruby-prof 1.0.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.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +523 -0
  3. data/LICENSE +25 -0
  4. data/README.rdoc +5 -0
  5. data/Rakefile +110 -0
  6. data/bin/ruby-prof +380 -0
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/ext/ruby_prof/extconf.rb +36 -0
  9. data/ext/ruby_prof/rp_allocation.c +292 -0
  10. data/ext/ruby_prof/rp_allocation.h +31 -0
  11. data/ext/ruby_prof/rp_call_info.c +283 -0
  12. data/ext/ruby_prof/rp_call_info.h +35 -0
  13. data/ext/ruby_prof/rp_measure_allocations.c +52 -0
  14. data/ext/ruby_prof/rp_measure_memory.c +42 -0
  15. data/ext/ruby_prof/rp_measure_process_time.c +63 -0
  16. data/ext/ruby_prof/rp_measure_wall_time.c +62 -0
  17. data/ext/ruby_prof/rp_measurement.c +236 -0
  18. data/ext/ruby_prof/rp_measurement.h +49 -0
  19. data/ext/ruby_prof/rp_method.c +642 -0
  20. data/ext/ruby_prof/rp_method.h +70 -0
  21. data/ext/ruby_prof/rp_profile.c +881 -0
  22. data/ext/ruby_prof/rp_profile.h +36 -0
  23. data/ext/ruby_prof/rp_stack.c +196 -0
  24. data/ext/ruby_prof/rp_stack.h +56 -0
  25. data/ext/ruby_prof/rp_thread.c +338 -0
  26. data/ext/ruby_prof/rp_thread.h +36 -0
  27. data/ext/ruby_prof/ruby_prof.c +48 -0
  28. data/ext/ruby_prof/ruby_prof.h +17 -0
  29. data/ext/ruby_prof/vc/ruby_prof.sln +31 -0
  30. data/ext/ruby_prof/vc/ruby_prof.vcxproj +143 -0
  31. data/lib/ruby-prof.rb +53 -0
  32. data/lib/ruby-prof/assets/call_stack_printer.css.html +117 -0
  33. data/lib/ruby-prof/assets/call_stack_printer.js.html +385 -0
  34. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  35. data/lib/ruby-prof/assets/graph_printer.html.erb +356 -0
  36. data/lib/ruby-prof/call_info.rb +57 -0
  37. data/lib/ruby-prof/call_info_visitor.rb +38 -0
  38. data/lib/ruby-prof/compatibility.rb +109 -0
  39. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  40. data/lib/ruby-prof/measurement.rb +14 -0
  41. data/lib/ruby-prof/method_info.rb +90 -0
  42. data/lib/ruby-prof/printers/abstract_printer.rb +118 -0
  43. data/lib/ruby-prof/printers/call_info_printer.rb +51 -0
  44. data/lib/ruby-prof/printers/call_stack_printer.rb +269 -0
  45. data/lib/ruby-prof/printers/call_tree_printer.rb +151 -0
  46. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  47. data/lib/ruby-prof/printers/flat_printer.rb +52 -0
  48. data/lib/ruby-prof/printers/graph_html_printer.rb +64 -0
  49. data/lib/ruby-prof/printers/graph_printer.rb +114 -0
  50. data/lib/ruby-prof/printers/multi_printer.rb +127 -0
  51. data/lib/ruby-prof/profile.rb +33 -0
  52. data/lib/ruby-prof/rack.rb +171 -0
  53. data/lib/ruby-prof/task.rb +147 -0
  54. data/lib/ruby-prof/thread.rb +35 -0
  55. data/lib/ruby-prof/version.rb +3 -0
  56. data/lib/unprof.rb +10 -0
  57. data/ruby-prof.gemspec +58 -0
  58. data/test/abstract_printer_test.rb +26 -0
  59. data/test/alias_test.rb +129 -0
  60. data/test/basic_test.rb +129 -0
  61. data/test/call_info_visitor_test.rb +31 -0
  62. data/test/duplicate_names_test.rb +32 -0
  63. data/test/dynamic_method_test.rb +53 -0
  64. data/test/enumerable_test.rb +21 -0
  65. data/test/exceptions_test.rb +24 -0
  66. data/test/exclude_methods_test.rb +146 -0
  67. data/test/exclude_threads_test.rb +53 -0
  68. data/test/line_number_test.rb +161 -0
  69. data/test/marshal_test.rb +119 -0
  70. data/test/measure_allocations.rb +30 -0
  71. data/test/measure_allocations_test.rb +385 -0
  72. data/test/measure_allocations_trace_test.rb +385 -0
  73. data/test/measure_memory_trace_test.rb +756 -0
  74. data/test/measure_process_time_test.rb +849 -0
  75. data/test/measure_times.rb +54 -0
  76. data/test/measure_wall_time_test.rb +459 -0
  77. data/test/multi_printer_test.rb +71 -0
  78. data/test/no_method_class_test.rb +15 -0
  79. data/test/parser_timings.rb +24 -0
  80. data/test/pause_resume_test.rb +166 -0
  81. data/test/prime.rb +56 -0
  82. data/test/printer_call_tree_test.rb +31 -0
  83. data/test/printer_flat_test.rb +68 -0
  84. data/test/printer_graph_html_test.rb +60 -0
  85. data/test/printer_graph_test.rb +41 -0
  86. data/test/printers_test.rb +141 -0
  87. data/test/printing_recursive_graph_test.rb +81 -0
  88. data/test/rack_test.rb +157 -0
  89. data/test/recursive_test.rb +210 -0
  90. data/test/singleton_test.rb +38 -0
  91. data/test/stack_printer_test.rb +64 -0
  92. data/test/start_stop_test.rb +109 -0
  93. data/test/test_helper.rb +24 -0
  94. data/test/thread_test.rb +144 -0
  95. data/test/unique_call_path_test.rb +190 -0
  96. data/test/yarv_test.rb +56 -0
  97. metadata +189 -0
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ class AbstractPrinterTest < TestCase
7
+ def setup
8
+ @result = {}
9
+ @printer = RubyProf::AbstractPrinter.new(@result)
10
+ @options = {}
11
+ @printer.setup_options(@options)
12
+ end
13
+
14
+ private
15
+
16
+ def with_const_stubbed(name, value)
17
+ old_verbose, $VERBOSE = $VERBOSE, nil
18
+ old_value = Object.const_get(name)
19
+
20
+ Object.const_set(name, value)
21
+ yield
22
+ Object.const_set(name, old_value)
23
+
24
+ $VERBOSE = old_verbose
25
+ end
26
+ end
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path("../test_helper", __FILE__)
5
+
6
+ class AliasTest < TestCase
7
+ class TestMe
8
+ def some_method
9
+ sleep(0.1)
10
+ end
11
+
12
+ alias :some_method_original :some_method
13
+ def some_method
14
+ some_method_original
15
+ end
16
+ end
17
+
18
+ def setup
19
+ # Need to use wall time for this test due to the sleep calls
20
+ RubyProf::measure_mode = RubyProf::WALL_TIME
21
+ end
22
+
23
+ # This test only correct works on Ruby 2.5 and higher because - see:
24
+ # https://bugs.ruby-lang.org/issues/12747
25
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
26
+ def test_alias
27
+ result = RubyProf.profile do
28
+ TestMe.new.some_method
29
+ end
30
+
31
+ methods = result.threads.first.methods
32
+ assert_equal(6, methods.count)
33
+
34
+ # Method 0
35
+ method = methods[0]
36
+ assert_equal('AliasTest#test_alias', method.full_name)
37
+ assert_equal(28, method.line)
38
+ refute(method.recursive?)
39
+
40
+ assert_equal(1, method.callers.count)
41
+ call_info = method.callers[0]
42
+ assert_nil(call_info.parent)
43
+ assert_equal(28, call_info.line)
44
+
45
+ assert_equal(2, method.callees.count)
46
+ call_info = method.callees[0]
47
+ assert_equal('Class#new', call_info.target.full_name)
48
+ assert_equal(28, call_info.line)
49
+
50
+ call_info = method.callees[1]
51
+ assert_equal('AliasTest::TestMe#some_method', call_info.target.full_name)
52
+ assert_equal(28, call_info.line)
53
+
54
+ # Method 1
55
+ method = methods[1]
56
+ assert_equal('Class#new', method.full_name)
57
+ assert_equal(0, method.line)
58
+ refute(method.recursive?)
59
+
60
+ assert_equal(1, method.callers.count)
61
+ call_info = method.callers[0]
62
+ assert_equal('AliasTest#test_alias', call_info.parent.full_name)
63
+ assert_equal(28, call_info.line)
64
+
65
+ assert_equal(1, method.callees.count)
66
+ call_info = method.callees[0]
67
+ assert_equal('BasicObject#initialize', call_info.target.full_name)
68
+ assert_equal(0, call_info.line)
69
+
70
+ # Method 2
71
+ method = methods[2]
72
+ assert_equal('BasicObject#initialize', method.full_name)
73
+ assert_equal(0, method.line)
74
+ refute(method.recursive?)
75
+
76
+ assert_equal(1, method.callers.count)
77
+ call_info = method.callers[0]
78
+ assert_equal('Class#new', call_info.parent.full_name)
79
+ assert_equal(0, call_info.line)
80
+
81
+ assert_equal(0, method.callees.count)
82
+
83
+ # Method 3
84
+ method = methods[3]
85
+ assert_equal('AliasTest::TestMe#some_method', method.full_name)
86
+ assert_equal(13, method.line)
87
+ refute(method.recursive?)
88
+
89
+ assert_equal(1, method.callers.count)
90
+ call_info = method.callers[0]
91
+ assert_equal('AliasTest#test_alias', call_info.parent.full_name)
92
+ assert_equal(28, call_info.line)
93
+
94
+ assert_equal(1, method.callees.count)
95
+ call_info = method.callees[0]
96
+ assert_equal('AliasTest::TestMe#some_method_original', call_info.target.full_name)
97
+ assert_equal(14, call_info.line)
98
+
99
+ # Method 4
100
+ method = methods[4]
101
+ assert_equal('AliasTest::TestMe#some_method_original', method.full_name)
102
+ assert_equal(8, method.line)
103
+ refute(method.recursive?)
104
+
105
+ assert_equal(1, method.callers.count)
106
+ call_info = method.callers[0]
107
+ assert_equal('AliasTest::TestMe#some_method', call_info.parent.full_name)
108
+ assert_equal(14, call_info.line)
109
+
110
+ assert_equal(1, method.callees.count)
111
+ call_info = method.callees[0]
112
+ assert_equal('Kernel#sleep', call_info.target.full_name)
113
+ assert_equal(9, call_info.line)
114
+
115
+ # Method 5
116
+ method = methods[5]
117
+ assert_equal('Kernel#sleep', method.full_name)
118
+ assert_equal(0, method.line)
119
+ refute(method.recursive?)
120
+
121
+ assert_equal(1, method.callers.count)
122
+ call_info = method.callers[0]
123
+ assert_equal('AliasTest::TestMe#some_method_original', call_info.parent.full_name)
124
+ assert_equal(9, call_info.line)
125
+
126
+ assert_equal(0, method.callees.count)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+ require_relative './measure_times'
6
+
7
+ class BasicTest < TestCase
8
+ def setup
9
+ # Need to use wall time for this test due to the sleep calls
10
+ RubyProf::measure_mode = RubyProf::WALL_TIME
11
+ end
12
+
13
+ def start
14
+ RubyProf.start
15
+ RubyProf::C1.sleep_wait
16
+ end
17
+
18
+ def test_running
19
+ assert(!RubyProf.running?)
20
+ RubyProf.start
21
+ assert(RubyProf.running?)
22
+ RubyProf.stop
23
+ assert(!RubyProf.running?)
24
+ end
25
+
26
+ def test_double_profile
27
+ RubyProf.start
28
+ assert_raises(RuntimeError) do
29
+ RubyProf.start
30
+ end
31
+ RubyProf.stop
32
+ end
33
+
34
+ def test_no_block
35
+ assert_raises(ArgumentError) do
36
+ RubyProf.profile
37
+ end
38
+ end
39
+
40
+ def test_traceback
41
+ RubyProf.start
42
+ assert_raises(NoMethodError) do
43
+ RubyProf.xxx
44
+ end
45
+
46
+ RubyProf.stop
47
+ end
48
+
49
+ def test_leave_method
50
+ start
51
+ sleep 0.15
52
+ profile = RubyProf.stop
53
+
54
+ assert_equal(1, profile.threads.count)
55
+
56
+ thread = profile.threads.first
57
+ assert_in_delta(0.25, thread.total_time, 0.05)
58
+
59
+ root_methods = thread.root_methods.sort
60
+ assert_equal(2, root_methods.count)
61
+ assert_equal("BasicTest#start", root_methods[0].full_name)
62
+ assert_equal("BasicTest#test_leave_method", root_methods[1].full_name)
63
+
64
+ assert_equal(4, thread.methods.length)
65
+ methods = profile.threads.first.methods.sort
66
+
67
+ # Check times
68
+ assert_equal("<Class::RubyProf::C1>#sleep_wait", methods[0].full_name)
69
+ assert_in_delta(0.1, methods[0].total_time, 0.05)
70
+ assert_in_delta(0.0, methods[0].wait_time, 0.05)
71
+ assert_in_delta(0.0, methods[0].self_time, 0.05)
72
+
73
+ assert_equal("BasicTest#start", methods[1].full_name)
74
+ assert_in_delta(0.1, methods[1].total_time, 0.05)
75
+ assert_in_delta(0.0, methods[1].wait_time, 0.05)
76
+ assert_in_delta(0.0, methods[1].self_time, 0.05)
77
+
78
+ assert_equal("BasicTest#test_leave_method", methods[2].full_name)
79
+ assert_in_delta(0.15, methods[2].total_time, 0.05)
80
+ assert_in_delta(0.0, methods[2].wait_time, 0.05)
81
+ assert_in_delta(0.0, methods[2].self_time, 0.05)
82
+
83
+ assert_equal("Kernel#sleep", methods[3].full_name)
84
+ assert_in_delta(0.25, methods[3].total_time, 0.05)
85
+ assert_in_delta(0.0, methods[3].wait_time, 0.05)
86
+ assert_in_delta(0.25, methods[3].self_time, 0.05)
87
+ end
88
+
89
+ def test_leave_method_2
90
+ start
91
+ RubyProf::C1.sleep_wait
92
+ RubyProf::C1.sleep_wait
93
+ profile = RubyProf.stop
94
+
95
+ assert_equal(1, profile.threads.count)
96
+
97
+ thread = profile.threads.first
98
+ assert_in_delta(0.3, thread.total_time, 0.05)
99
+
100
+ root_methods = thread.root_methods.sort
101
+ assert_equal(2, root_methods.count)
102
+ assert_equal("BasicTest#start", root_methods[0].full_name)
103
+ assert_equal("BasicTest#test_leave_method_2", root_methods[1].full_name)
104
+
105
+ assert_equal(4, thread.methods.length)
106
+ methods = profile.threads.first.methods.sort
107
+
108
+ # Check times
109
+ assert_equal("BasicTest#start", methods[0].full_name)
110
+ assert_in_delta(0.1, methods[0].total_time, 0.05)
111
+ assert_in_delta(0.0, methods[0].wait_time, 0.05)
112
+ assert_in_delta(0.0, methods[0].self_time, 0.05)
113
+
114
+ assert_equal("BasicTest#test_leave_method_2", methods[1].full_name)
115
+ assert_in_delta(0.2, methods[1].total_time, 0.05)
116
+ assert_in_delta(0.0, methods[1].wait_time, 0.05)
117
+ assert_in_delta(0.0, methods[1].self_time, 0.05)
118
+
119
+ assert_equal("Kernel#sleep", methods[2].full_name)
120
+ assert_in_delta(0.3, methods[2].total_time, 0.05)
121
+ assert_in_delta(0.0, methods[2].wait_time, 0.05)
122
+ assert_in_delta(0.3, methods[2].self_time, 0.05)
123
+
124
+ assert_equal("<Class::RubyProf::C1>#sleep_wait", methods[3].full_name)
125
+ assert_in_delta(0.3, methods[3].total_time, 0.05)
126
+ assert_in_delta(0.0, methods[3].wait_time, 0.05)
127
+ assert_in_delta(0.0, methods[3].self_time, 0.05)
128
+ end
129
+ end
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ class CallInfoVisitorTest < 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_visit
13
+ result = RubyProf.profile do
14
+ RubyProf::C1.sleep_wait
15
+ end
16
+
17
+ visitor = RubyProf::CallInfoVisitor.new(result.threads.first.root_methods)
18
+
19
+ method_names = Array.new
20
+
21
+ visitor.visit do |call_info, event|
22
+ method_names << call_info.target.full_name if event == :enter
23
+ end
24
+
25
+ assert_equal(3, method_names.length)
26
+ assert_equal("CallInfoVisitorTest#test_visit", method_names[0])
27
+ assert_equal("<Class::RubyProf::C1>#sleep_wait", method_names[1])
28
+ assert_equal("Kernel#sleep", method_names[2])
29
+ end
30
+ end
31
+
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ class DuplicateNames < TestCase
7
+ def test_names
8
+ result = RubyProf::profile do
9
+ str = %{module Foo; class Bar; def foo; end end end}
10
+
11
+ eval str
12
+ Foo::Bar.new.foo
13
+ DuplicateNames.class_eval {remove_const :Foo}
14
+
15
+ eval str
16
+ Foo::Bar.new.foo
17
+ DuplicateNames.class_eval {remove_const :Foo}
18
+
19
+ eval str
20
+ Foo::Bar.new.foo
21
+ end
22
+
23
+ # There should be 3 foo methods
24
+ methods = result.threads.first.methods.sort.reverse
25
+
26
+ methods = methods.select do |method|
27
+ method.full_name == 'DuplicateNames::Foo::Bar#foo'
28
+ end
29
+
30
+ assert_equal(3, methods.length)
31
+ end
32
+ end
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path("../test_helper", __FILE__)
5
+
6
+ class DynamicMethodTest < TestCase
7
+
8
+ class FruitMedley
9
+ define_method(:apple) do
10
+ sleep(0.1)
11
+ "I'm a peach"
12
+ end
13
+
14
+ define_method(:orange) do
15
+ sleep(0.2)
16
+ "I'm an orange"
17
+ end
18
+
19
+ [:banana, :peach].each_with_index do |fruit,i|
20
+ define_method(fruit) do
21
+ sleep(i == 0 ? 0.3 : 0.4)
22
+ "I'm a #{fruit}"
23
+ end
24
+ end
25
+ end
26
+
27
+ def setup
28
+ # Need to use wall time for this test due to the sleep calls
29
+ RubyProf::measure_mode = RubyProf::WALL_TIME
30
+ end
31
+
32
+ def test_dynamic_method
33
+ medley = FruitMedley.new
34
+ result = RubyProf.profile do
35
+ medley.apple
36
+ medley.orange
37
+ medley.banana
38
+ medley.peach
39
+ end
40
+
41
+ methods = result.threads.first.methods.sort.reverse
42
+ expected_method_names = %w(
43
+ DynamicMethodTest#test_dynamic_method
44
+ Kernel#sleep
45
+ DynamicMethodTest::FruitMedley#peach
46
+ DynamicMethodTest::FruitMedley#banana
47
+ DynamicMethodTest::FruitMedley#orange
48
+ DynamicMethodTest::FruitMedley#apple
49
+ Symbol#to_s
50
+ )
51
+ assert_equal expected_method_names.join("\n"), methods.map(&:full_name).join("\n")
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ # -- Test for bug
7
+ # http://github.com/rdp/ruby-prof/issues#issue/12
8
+
9
+ class EnumerableTest < TestCase
10
+ def test_enumerable
11
+ result = RubyProf.profile do
12
+ 3.times { [1,2,3].any? {|n| n} }
13
+ end
14
+ methods = if RUBY_VERSION >= "2.2.0"
15
+ %w(EnumerableTest#test_enumerable Integer#times Array#any?)
16
+ else
17
+ %w(EnumerableTest#test_enumerable Integer#times Enumerable#any? Array#each)
18
+ end
19
+ assert_equal(methods, result.threads.first.methods.map(&:full_name))
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ class ExceptionsTest < TestCase
7
+ def test_profile
8
+ result = begin
9
+ RubyProf.profile do
10
+ raise(RuntimeError, 'Test error')
11
+ end
12
+ rescue
13
+ end
14
+ assert_kind_of(RubyProf::Profile, result)
15
+ end
16
+
17
+ def test_profile_allows_exceptions
18
+ assert_raises(RuntimeError) do
19
+ RubyProf.profile(:allow_exceptions => true) do
20
+ raise(RuntimeError, 'Test error')
21
+ end
22
+ end
23
+ end
24
+ end