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,57 @@
1
+ # encoding: utf-8
2
+
3
+ module RubyProf
4
+ # The CallInfo class is used to track the relationships between methods. It is a helper class used by
5
+ # RubyProf::MethodInfo to keep track of which methods called a given method and which methods a given
6
+ # method called. Each CallInfo has a parent and target method. You cannot create a CallInfo object directly,
7
+ # they are generated while running a profile.
8
+ class CallInfo
9
+ # The number of times the parent method called the target method
10
+ def called
11
+ self.measurement.called
12
+ end
13
+
14
+ # The total time resulting from the parent method calling the target method
15
+ def total_time
16
+ self.measurement.total_time
17
+ end
18
+
19
+ # The self time (of the parent) resulting from the parent method calling the target method
20
+ def self_time
21
+ self.measurement.self_time
22
+ end
23
+
24
+ # The wait time (of the parent) resulting from the parent method calling the target method
25
+ def wait_time
26
+ self.measurement.wait_time
27
+ end
28
+
29
+ # The time spent in child methods resulting from the parent method calling the target method
30
+ def children_time
31
+ self.total_time - self.self_time - self.wait_time
32
+ end
33
+
34
+ # Compares two CallInfo instances. The comparison is based on the CallInfo#parent, CallInfo#target,
35
+ # and total time.
36
+ def <=>(other)
37
+ if self.target == other.target && self.parent == other.parent
38
+ 0
39
+ elsif self.total_time < other.total_time
40
+ -1
41
+ elsif self.total_time > other.total_time
42
+ 1
43
+ else
44
+ self.target.full_name <=> other.target.full_name
45
+ end
46
+ end
47
+
48
+ # :nodoc:
49
+ def to_s
50
+ "#{parent ? parent.full_name : '<nil>'} - #{target.full_name}"
51
+ end
52
+
53
+ def inspect
54
+ super + "(#{self.to_s})"
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,38 @@
1
+ module RubyProf
2
+ # The call info visitor class does a depth-first traversal across a
3
+ # list of method infos. At each call_info node, the visitor executes
4
+ # the block provided in the #visit method. The block is passed two
5
+ # parameters, the event and the call_info instance. Event will be
6
+ # either :enter or :exit.
7
+ #
8
+ # visitor = RubyProf::CallInfoVisitor.new(result.threads.first.root_methods)
9
+ #
10
+ # method_names = Array.new
11
+ #
12
+ # visitor.visit do |call_info, event|
13
+ # method_names << call_info.target.full_name if event == :enter
14
+ # end
15
+ #
16
+ # puts method_names
17
+ class CallInfoVisitor
18
+ def initialize(root_methods)
19
+ @call_infos = root_methods.map(&:callers).flatten
20
+ end
21
+
22
+ def visit(&block)
23
+ @call_infos.each do |call_info|
24
+ visit_call_info(call_info, &block)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def visit_call_info(call_info, &block)
31
+ yield call_info, :enter
32
+ call_info.target.callees.each do |child|
33
+ visit_call_info(child, &block)
34
+ end
35
+ yield call_info, :exit
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,109 @@
1
+ # encoding: utf-8
2
+
3
+ # These methods are deprecated and are available for backwards compatability.
4
+ module RubyProf
5
+ # call-seq:
6
+ # measure_mode -> measure_mode
7
+ #
8
+ # Returns what ruby-prof is measuring. Valid values include:
9
+ #
10
+ # * RubyProf::WALL_TIME
11
+ # * RubyProf::PROCESS_TIME
12
+ # * RubyProf::ALLOCATIONS
13
+ # * RubyProf::MEMORY
14
+ def self.measure_mode
15
+ @measure_mode ||= RubyProf::WALL_TIME
16
+ end
17
+
18
+ # call-seq:
19
+ # measure_mode=value -> void
20
+ #
21
+ # Specifies what ruby-prof should measure. Valid values include:
22
+ #
23
+ # * RubyProf::WALL_TIME - Wall time measures the real-world time elapsed between any two moments. If there are other processes concurrently running on the system that use significant CPU or disk time during a profiling run then the reported results will be larger than expected. On Windows, wall time is measured using GetTickCount(), on MacOS by mach_absolute_time, on Linux by clock_gettime and otherwise by gettimeofday.
24
+ # * RubyProf::PROCESS_TIME - Process time measures the time used by a process between any two moments. It is unaffected by other processes concurrently running on the system. Remember with process time that calls to methods like sleep will not be included in profiling results. On Windows, process time is measured using GetProcessTimes and on other platforms by clock_gettime.
25
+ # * RubyProf::ALLOCATIONS - Object allocations measures show how many objects each method in a program allocates. Measurements are done via Ruby's GC.stat api.
26
+ # * RubyProf::MEMORY - Memory measures how much memory each method in a program uses. Measurements are done via Ruby's TracePoint api.
27
+ def self.measure_mode=(value)
28
+ @measure_mode = value
29
+ end
30
+
31
+ # Returns the threads that ruby-prof should exclude from profiling
32
+ def self.exclude_threads
33
+ @exclude_threads ||= Array.new
34
+ end
35
+
36
+ # Specifies which threads ruby-prof should exclude from profiling
37
+ def self.exclude_threads=(value)
38
+ @exclude_threads = value
39
+ end
40
+
41
+ # Starts profiling
42
+ def self.start
43
+ ensure_not_running!
44
+ @profile = Profile.new(:measure_mode => measure_mode, :exclude_threads => exclude_threads)
45
+ @profile.start
46
+ end
47
+
48
+ # Pauses profiling
49
+ def self.pause
50
+ ensure_running!
51
+ @profile.pause
52
+ end
53
+
54
+ # Is a profile running?
55
+ def self.running?
56
+ if defined?(@profile) and @profile
57
+ @profile.running?
58
+ else
59
+ false
60
+ end
61
+ end
62
+
63
+ # Resume profiling
64
+ def self.resume
65
+ ensure_running!
66
+ @profile.resume
67
+ end
68
+
69
+ # Stops profiling
70
+ def self.stop
71
+ ensure_running!
72
+ result = @profile.stop
73
+ @profile = nil
74
+ result
75
+ end
76
+
77
+ # Profiles a block
78
+ def self.profile(options = {}, &block)
79
+ ensure_not_running!
80
+ options = {:measure_mode => measure_mode, :exclude_threads => exclude_threads }.merge!(options)
81
+ Profile.profile(options, &block)
82
+ end
83
+
84
+ # :nodoc:
85
+ def self.measure_mode_string
86
+ case measure_mode
87
+ when WALL_TIME then "wall_time"
88
+ when PROCESS_TIME then "process_time"
89
+ when ALLOCATIONS then "allocations"
90
+ when MEMORY then "memory"
91
+ end
92
+ end
93
+
94
+ # :nodoc:
95
+ def self.start_script(script)
96
+ start
97
+ load script
98
+ end
99
+
100
+ private
101
+
102
+ def self.ensure_running!
103
+ raise(RuntimeError, "RubyProf.start was not yet called") unless running?
104
+ end
105
+
106
+ def self.ensure_not_running!
107
+ raise(RuntimeError, "RubyProf is already running") if running?
108
+ end
109
+ end
@@ -0,0 +1,198 @@
1
+ require 'set'
2
+
3
+ # :enddoc:
4
+ module RubyProf
5
+ module ExcludeCommonMethods
6
+ ENUMERABLE_NAMES = Enumerable.instance_methods(false)
7
+
8
+ def self.apply!(profile)
9
+ ##
10
+ # Kernel Methods
11
+ ##
12
+
13
+ exclude_methods(profile, Kernel, [
14
+ :dup,
15
+ :initialize_dup,
16
+ :tap,
17
+ :send,
18
+ :public_send,
19
+ ])
20
+
21
+ ##
22
+ # Fundamental Types
23
+ ##
24
+
25
+ exclude_methods(profile, BasicObject, :"!=")
26
+ exclude_methods(profile, Method, :"[]")
27
+ exclude_methods(profile, Module, :new)
28
+ exclude_methods(profile, Class, :new)
29
+ exclude_methods(profile, Proc, :call, :yield)
30
+ exclude_methods(profile, Range, :each)
31
+ exclude_methods(profile, Integer, :times)
32
+
33
+ ##
34
+ # Value Types
35
+ ##
36
+
37
+ exclude_methods(profile, String, [
38
+ :sub,
39
+ :sub!,
40
+ :gsub,
41
+ :gsub!,
42
+ ])
43
+
44
+ ##
45
+ # Emumerables
46
+ ##
47
+
48
+ exclude_enumerable(profile, Enumerable)
49
+ exclude_enumerable(profile, Enumerator)
50
+
51
+ ##
52
+ # Collections
53
+ ##
54
+
55
+ exclude_enumerable(profile, Array, [
56
+ :each_index,
57
+ :map!,
58
+ :select!,
59
+ :reject!,
60
+ :collect!,
61
+ :sort!,
62
+ :sort_by!,
63
+ :index,
64
+ :delete_if,
65
+ :keep_if,
66
+ :drop_while,
67
+ :uniq,
68
+ :uniq!,
69
+ :"==",
70
+ :eql?,
71
+ :hash,
72
+ :to_json,
73
+ :as_json,
74
+ :encode_json,
75
+ ])
76
+
77
+ exclude_enumerable(profile, Hash, [
78
+ :dup,
79
+ :initialize_dup,
80
+ :fetch,
81
+ :"[]",
82
+ :"[]=",
83
+ :each_key,
84
+ :each_value,
85
+ :each_pair,
86
+ :map!,
87
+ :select!,
88
+ :reject!,
89
+ :collect!,
90
+ :delete_if,
91
+ :keep_if,
92
+ :slice,
93
+ :slice!,
94
+ :except,
95
+ :except!,
96
+ :"==",
97
+ :eql?,
98
+ :hash,
99
+ :to_json,
100
+ :as_json,
101
+ :encode_json,
102
+ ])
103
+
104
+ exclude_enumerable(profile, Set, [
105
+ :map!,
106
+ :select!,
107
+ :reject!,
108
+ :collect!,
109
+ :classify,
110
+ :delete_if,
111
+ :keep_if,
112
+ :divide,
113
+ :"==",
114
+ :eql?,
115
+ :hash,
116
+ :to_json,
117
+ :as_json,
118
+ :encode_json,
119
+ ])
120
+
121
+ ##
122
+ # Garbage Collection
123
+ ##
124
+
125
+ exclude_singleton_methods(profile, GC, [
126
+ :start
127
+ ])
128
+
129
+ ##
130
+ # Unicorn
131
+ ##
132
+
133
+ if defined?(Unicorn)
134
+ exclude_methods(profile, Unicorn::HttpServer, :process_client)
135
+ end
136
+
137
+ if defined?(Unicorn::OobGC)
138
+ exclude_methods(profile, Unicorn::OobGC, :process_client)
139
+ end
140
+
141
+ ##
142
+ # New Relic
143
+ ##
144
+
145
+ if defined?(NewRelic::Agent)
146
+ if defined?(NewRelic::Agent::Instrumentation::MiddlewareTracing)
147
+ exclude_methods(profile, NewRelic::Agent::Instrumentation::MiddlewareTracing, [
148
+ :call
149
+ ])
150
+ end
151
+
152
+ if defined?(NewRelic::Agent::MethodTracerHelpers)
153
+ exclude_methods(profile, NewRelic::Agent::MethodTracerHelpers, [
154
+ :trace_execution_scoped,
155
+ :log_errors,
156
+ ])
157
+
158
+ exclude_singleton_methods(profile, NewRelic::Agent::MethodTracerHelpers, [
159
+ :trace_execution_scoped,
160
+ :log_errors,
161
+ ])
162
+ end
163
+
164
+ if defined?(NewRelic::Agent::MethodTracer)
165
+ exclude_methods(profile, NewRelic::Agent::MethodTracer, [
166
+ :trace_execution_scoped,
167
+ :trace_execution_unscoped,
168
+ ])
169
+ end
170
+ end
171
+
172
+ ##
173
+ # Miscellaneous Methods
174
+ ##
175
+
176
+ if defined?(Mustache)
177
+ exclude_methods(profile, Mustache::Context, [
178
+ :fetch
179
+ ])
180
+ end
181
+ end
182
+
183
+ private
184
+
185
+ def self.exclude_enumerable(profile, mod, *method_or_methods)
186
+ exclude_methods(profile, mod, [:each, *method_or_methods])
187
+ exclude_methods(profile, mod, ENUMERABLE_NAMES)
188
+ end
189
+
190
+ def self.exclude_methods(profile, mod, *method_or_methods)
191
+ profile.exclude_methods!(mod, method_or_methods)
192
+ end
193
+
194
+ def self.exclude_singleton_methods(profile, mod, *method_or_methods)
195
+ profile.exclude_singleton_methods!(mod, method_or_methods)
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,14 @@
1
+ module RubyProf
2
+ # The Measurement class is a helper class used by RubyProf::MethodInfo to store information about the method.
3
+ # You cannot create a CallInfo object directly, they are generated while running a profile.
4
+ class Measurement
5
+ # :nodoc:
6
+ def to_s
7
+ "c: #{called}, tt: #{total_time}, st: #{self_time}"
8
+ end
9
+
10
+ def inspect
11
+ super + "(#{self.to_s})"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,90 @@
1
+ # encoding: utf-8
2
+
3
+ module RubyProf
4
+ # The MethodInfo class is used to track information about each method that is profiled.
5
+ # You cannot create a MethodInfo object directly, they are generated while running a profile.
6
+ class MethodInfo
7
+ include Comparable
8
+
9
+ # Returns the full name of a class. The interpretation of method names is:
10
+ #
11
+ # * MyObject#test - An method defined in a class
12
+ # * <Class:MyObject>#test - A method defined in a singleton class.
13
+ # * <Module:MyObject>#test - A method defined in a singleton module.
14
+ # * <Object:MyObject>#test - A method defined in a singleton object.
15
+ def full_name
16
+ decorated_class_name = case self.klass_flags
17
+ when 0x2
18
+ "<Class::#{klass_name}>"
19
+ when 0x4
20
+ "<Module::#{klass_name}>"
21
+ when 0x8
22
+ "<Object::#{klass_name}>"
23
+ else
24
+ klass_name
25
+ end
26
+
27
+ "#{decorated_class_name}##{method_name}"
28
+ end
29
+
30
+ # The number of times this method was called
31
+ def called
32
+ self.measurement.called
33
+ end
34
+
35
+ # The total time this method took - includes self time + wait time + child time
36
+ def total_time
37
+ self.measurement.total_time
38
+ end
39
+
40
+ # The time this method took to execute
41
+ def self_time
42
+ self.measurement.self_time
43
+ end
44
+
45
+ # The time this method waited for other fibers/threads to execute
46
+ def wait_time
47
+ self.measurement.wait_time
48
+ end
49
+
50
+ # The time this method's children took to execute
51
+ def children_time
52
+ self.total_time - self.self_time - self.wait_time
53
+ end
54
+
55
+ # The min call depth of this method
56
+ def min_depth
57
+ @min_depth ||= callers.map(&:depth).min
58
+ end
59
+
60
+ # :enddoc:
61
+ def <=>(other)
62
+ if other == nil
63
+ -1
64
+ elsif self.full_name == other.full_name
65
+ 0
66
+ elsif self.total_time < other.total_time
67
+ -1
68
+ elsif self.total_time > other.total_time
69
+ 1
70
+ elsif self.min_depth < other.min_depth
71
+ 1
72
+ elsif self.min_depth > other.min_depth
73
+ -1
74
+ else
75
+ self.full_name <=> other.full_name
76
+ end
77
+ end
78
+
79
+ def to_s
80
+ "#{self.full_name} (c: #{self.called}, tt: #{self.total_time}, st: #{self.self_time}, wt: #{wait_time}, ct: #{self.children_time})"
81
+ end
82
+
83
+ # Remove method from the call graph. should not be called directly.
84
+ def eliminate!
85
+ # $stderr.puts "eliminating #{self}"
86
+ callers.each{ |call_info| call_info.eliminate! }
87
+ callers.clear
88
+ end
89
+ end
90
+ end