ruby-prof 0.18.0-x64-mingw32

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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +500 -0
  3. data/LICENSE +25 -0
  4. data/README.rdoc +487 -0
  5. data/Rakefile +113 -0
  6. data/bin/ruby-prof +345 -0
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/examples/flat.txt +50 -0
  9. data/examples/graph.dot +84 -0
  10. data/examples/graph.html +823 -0
  11. data/examples/graph.txt +139 -0
  12. data/examples/multi.flat.txt +23 -0
  13. data/examples/multi.graph.html +760 -0
  14. data/examples/multi.grind.dat +114 -0
  15. data/examples/multi.stack.html +547 -0
  16. data/examples/stack.html +547 -0
  17. data/ext/ruby_prof/extconf.rb +68 -0
  18. data/ext/ruby_prof/rp_call_info.c +425 -0
  19. data/ext/ruby_prof/rp_call_info.h +53 -0
  20. data/ext/ruby_prof/rp_measure.c +40 -0
  21. data/ext/ruby_prof/rp_measure.h +45 -0
  22. data/ext/ruby_prof/rp_measure_allocations.c +76 -0
  23. data/ext/ruby_prof/rp_measure_cpu_time.c +136 -0
  24. data/ext/ruby_prof/rp_measure_gc_runs.c +73 -0
  25. data/ext/ruby_prof/rp_measure_gc_time.c +60 -0
  26. data/ext/ruby_prof/rp_measure_memory.c +77 -0
  27. data/ext/ruby_prof/rp_measure_process_time.c +71 -0
  28. data/ext/ruby_prof/rp_measure_wall_time.c +45 -0
  29. data/ext/ruby_prof/rp_method.c +630 -0
  30. data/ext/ruby_prof/rp_method.h +75 -0
  31. data/ext/ruby_prof/rp_stack.c +173 -0
  32. data/ext/ruby_prof/rp_stack.h +63 -0
  33. data/ext/ruby_prof/rp_thread.c +277 -0
  34. data/ext/ruby_prof/rp_thread.h +27 -0
  35. data/ext/ruby_prof/ruby_prof.c +794 -0
  36. data/ext/ruby_prof/ruby_prof.h +60 -0
  37. data/ext/ruby_prof/vc/ruby_prof.sln +31 -0
  38. data/ext/ruby_prof/vc/ruby_prof.vcxproj +141 -0
  39. data/lib/2.6.3/ruby_prof.so +0 -0
  40. data/lib/ruby-prof.rb +68 -0
  41. data/lib/ruby-prof/aggregate_call_info.rb +76 -0
  42. data/lib/ruby-prof/assets/call_stack_printer.css.html +117 -0
  43. data/lib/ruby-prof/assets/call_stack_printer.js.html +385 -0
  44. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  45. data/lib/ruby-prof/call_info.rb +115 -0
  46. data/lib/ruby-prof/call_info_visitor.rb +40 -0
  47. data/lib/ruby-prof/compatibility.rb +179 -0
  48. data/lib/ruby-prof/method_info.rb +121 -0
  49. data/lib/ruby-prof/printers/abstract_printer.rb +104 -0
  50. data/lib/ruby-prof/printers/call_info_printer.rb +41 -0
  51. data/lib/ruby-prof/printers/call_stack_printer.rb +265 -0
  52. data/lib/ruby-prof/printers/call_tree_printer.rb +143 -0
  53. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  54. data/lib/ruby-prof/printers/flat_printer.rb +70 -0
  55. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +83 -0
  56. data/lib/ruby-prof/printers/graph_html_printer.rb +249 -0
  57. data/lib/ruby-prof/printers/graph_printer.rb +116 -0
  58. data/lib/ruby-prof/printers/multi_printer.rb +84 -0
  59. data/lib/ruby-prof/profile.rb +26 -0
  60. data/lib/ruby-prof/profile/exclude_common_methods.rb +207 -0
  61. data/lib/ruby-prof/profile/legacy_method_elimination.rb +50 -0
  62. data/lib/ruby-prof/rack.rb +174 -0
  63. data/lib/ruby-prof/task.rb +147 -0
  64. data/lib/ruby-prof/thread.rb +35 -0
  65. data/lib/ruby-prof/version.rb +3 -0
  66. data/lib/unprof.rb +10 -0
  67. data/ruby-prof.gemspec +58 -0
  68. data/test/abstract_printer_test.rb +53 -0
  69. data/test/aggregate_test.rb +136 -0
  70. data/test/basic_test.rb +128 -0
  71. data/test/block_test.rb +74 -0
  72. data/test/call_info_test.rb +78 -0
  73. data/test/call_info_visitor_test.rb +31 -0
  74. data/test/duplicate_names_test.rb +32 -0
  75. data/test/dynamic_method_test.rb +55 -0
  76. data/test/enumerable_test.rb +21 -0
  77. data/test/exceptions_test.rb +24 -0
  78. data/test/exclude_methods_test.rb +146 -0
  79. data/test/exclude_threads_test.rb +53 -0
  80. data/test/fiber_test.rb +79 -0
  81. data/test/issue137_test.rb +63 -0
  82. data/test/line_number_test.rb +80 -0
  83. data/test/measure_allocations_test.rb +26 -0
  84. data/test/measure_cpu_time_test.rb +212 -0
  85. data/test/measure_gc_runs_test.rb +32 -0
  86. data/test/measure_gc_time_test.rb +36 -0
  87. data/test/measure_memory_test.rb +33 -0
  88. data/test/measure_process_time_test.rb +61 -0
  89. data/test/measure_wall_time_test.rb +255 -0
  90. data/test/method_elimination_test.rb +84 -0
  91. data/test/module_test.rb +45 -0
  92. data/test/multi_printer_test.rb +104 -0
  93. data/test/no_method_class_test.rb +15 -0
  94. data/test/pause_resume_test.rb +166 -0
  95. data/test/prime.rb +54 -0
  96. data/test/printers_test.rb +275 -0
  97. data/test/printing_recursive_graph_test.rb +127 -0
  98. data/test/rack_test.rb +157 -0
  99. data/test/recursive_test.rb +215 -0
  100. data/test/singleton_test.rb +38 -0
  101. data/test/stack_printer_test.rb +77 -0
  102. data/test/stack_test.rb +138 -0
  103. data/test/start_stop_test.rb +112 -0
  104. data/test/test_helper.rb +267 -0
  105. data/test/thread_test.rb +187 -0
  106. data/test/unique_call_path_test.rb +202 -0
  107. data/test/yarv_test.rb +55 -0
  108. metadata +199 -0
@@ -0,0 +1,84 @@
1
+ # encoding: utf-8
2
+
3
+ module RubyProf
4
+ # Helper class to simplify printing profiles of several types from
5
+ # one profiling run. Currently prints a flat profile, a callgrind
6
+ # profile, a call stack profile and a graph profile.
7
+ class MultiPrinter
8
+ def initialize(result, printers = [:stack, :graph, :tree, :flat])
9
+ @stack_printer = CallStackPrinter.new(result) if printers.include?(:stack)
10
+ @graph_printer = GraphHtmlPrinter.new(result) if printers.include?(:graph)
11
+ @tree_printer = CallTreePrinter.new(result) if printers.include?(:tree)
12
+ @flat_printer = FlatPrinter.new(result) if printers.include?(:flat)
13
+ end
14
+
15
+ def self.needs_dir?
16
+ true
17
+ end
18
+
19
+ # create profile files under options[:path] or the current
20
+ # directory. options[:profile] is used as the base name for the
21
+ # pofile file, defaults to "profile".
22
+ def print(options)
23
+ validate_print_params(options)
24
+
25
+ @profile = options.delete(:profile) || "profile"
26
+ @directory = options.delete(:path) || File.expand_path(".")
27
+
28
+ print_to_stack(options) if @stack_printer
29
+ print_to_graph(options) if @graph_printer
30
+ print_to_tree(options) if @tree_printer
31
+ print_to_flat(options) if @flat_printer
32
+ end
33
+
34
+ # the name of the call stack profile file
35
+ def stack_profile
36
+ "#{@directory}/#{@profile}.stack.html"
37
+ end
38
+
39
+ # the name of the graph profile file
40
+ def graph_profile
41
+ "#{@directory}/#{@profile}.graph.html"
42
+ end
43
+
44
+ # the name of the callgrind profile file
45
+ def tree_profile
46
+ "#{@directory}/#{@profile}.callgrind.out.#{$$}"
47
+ end
48
+
49
+ # the name of the flat profile file
50
+ def flat_profile
51
+ "#{@directory}/#{@profile}.flat.txt"
52
+ end
53
+
54
+ def print_to_stack(options)
55
+ File.open(stack_profile, "w") do |f|
56
+ @stack_printer.print(f, options.merge(:graph => "#{@profile}.graph.html"))
57
+ end
58
+ end
59
+
60
+ def print_to_graph(options)
61
+ File.open(graph_profile, "w") do |f|
62
+ @graph_printer.print(f, options)
63
+ end
64
+ end
65
+
66
+ def print_to_tree(options)
67
+ @tree_printer.print(options.merge(:path => @directory, :profile => @profile))
68
+ end
69
+
70
+ def print_to_flat(options)
71
+ File.open(flat_profile, "w") do |f|
72
+ @flat_printer.print(f, options)
73
+ end
74
+ end
75
+
76
+ def validate_print_params(options)
77
+ if options.is_a?(IO)
78
+ raise ArgumentError, "#{self.class.name}#print cannot print to IO objects"
79
+ elsif !options.is_a?(Hash)
80
+ raise ArgumentError, "#{self.class.name}#print requires an options hash"
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ruby-prof/profile/exclude_common_methods'
4
+ require 'ruby-prof/profile/legacy_method_elimination'
5
+
6
+ module RubyProf
7
+ class Profile
8
+ include LegacyMethodElimination
9
+
10
+ # Hides methods that, when represented as a call graph, have
11
+ # extremely large in and out degrees and make navigation impossible.
12
+ def exclude_common_methods!
13
+ ExcludeCommonMethods.apply!(self)
14
+ end
15
+
16
+ def exclude_methods!(mod, *method_or_methods)
17
+ [method_or_methods].flatten.each do |name|
18
+ exclude_method!(mod, name)
19
+ end
20
+ end
21
+
22
+ def exclude_singleton_methods!(mod, *method_or_methods)
23
+ exclude_methods!(mod.singleton_class, *method_or_methods)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,207 @@
1
+ require 'set'
2
+
3
+ module RubyProf
4
+ class Profile
5
+ class ExcludeCommonMethods
6
+ ENUMERABLE_NAMES = Enumerable.instance_methods(false)
7
+
8
+ def self.apply!(profile)
9
+ new(profile).apply!
10
+ end
11
+
12
+ def initialize(profile)
13
+ @profile = profile
14
+ end
15
+
16
+ def apply!
17
+ ##
18
+ # Kernel Methods
19
+ ##
20
+
21
+ exclude_methods Kernel, [
22
+ :dup,
23
+ :initialize_dup,
24
+ :tap,
25
+ :send,
26
+ :public_send,
27
+ ]
28
+
29
+ ##
30
+ # Fundamental Types
31
+ ##
32
+
33
+ exclude_methods BasicObject, :"!="
34
+ exclude_methods Method, :"[]"
35
+ exclude_methods Module, :new
36
+ exclude_methods Class, :new
37
+ exclude_methods Proc, :call, :yield
38
+ exclude_methods Range, :each
39
+ exclude_methods Integer, :times
40
+
41
+ ##
42
+ # Value Types
43
+ ##
44
+
45
+ exclude_methods String, [
46
+ :sub,
47
+ :sub!,
48
+ :gsub,
49
+ :gsub!,
50
+ ]
51
+
52
+ ##
53
+ # Emumerables
54
+ ##
55
+
56
+ exclude_enumerable Enumerable
57
+ exclude_enumerable Enumerator
58
+
59
+ ##
60
+ # Collections
61
+ ##
62
+
63
+ exclude_enumerable Array, [
64
+ :each_index,
65
+ :map!,
66
+ :select!,
67
+ :reject!,
68
+ :collect!,
69
+ :sort!,
70
+ :sort_by!,
71
+ :index,
72
+ :delete_if,
73
+ :keep_if,
74
+ :drop_while,
75
+ :uniq,
76
+ :uniq!,
77
+ :"==",
78
+ :eql?,
79
+ :hash,
80
+ :to_json,
81
+ :as_json,
82
+ :encode_json,
83
+ ]
84
+
85
+ exclude_enumerable Hash, [
86
+ :dup,
87
+ :initialize_dup,
88
+ :fetch,
89
+ :"[]",
90
+ :"[]=",
91
+ :each_key,
92
+ :each_value,
93
+ :each_pair,
94
+ :map!,
95
+ :select!,
96
+ :reject!,
97
+ :collect!,
98
+ :delete_if,
99
+ :keep_if,
100
+ :slice,
101
+ :slice!,
102
+ :except,
103
+ :except!,
104
+ :"==",
105
+ :eql?,
106
+ :hash,
107
+ :to_json,
108
+ :as_json,
109
+ :encode_json,
110
+ ]
111
+
112
+ exclude_enumerable Set, [
113
+ :map!,
114
+ :select!,
115
+ :reject!,
116
+ :collect!,
117
+ :classify,
118
+ :delete_if,
119
+ :keep_if,
120
+ :divide,
121
+ :"==",
122
+ :eql?,
123
+ :hash,
124
+ :to_json,
125
+ :as_json,
126
+ :encode_json,
127
+ ]
128
+
129
+ ##
130
+ # Garbage Collection
131
+ ##
132
+
133
+ exclude_singleton_methods GC, [
134
+ :start
135
+ ]
136
+
137
+ ##
138
+ # Unicorn
139
+ ##
140
+
141
+ if defined?(Unicorn)
142
+ exclude_methods Unicorn::HttpServer, :process_client
143
+ end
144
+
145
+ if defined?(Unicorn::OobGC)
146
+ exclude_methods Unicorn::OobGC, :process_client
147
+ end
148
+
149
+ ##
150
+ # New Relic
151
+ ##
152
+
153
+ if defined?(NewRelic::Agent)
154
+ if defined?(NewRelic::Agent::Instrumentation::MiddlewareTracing)
155
+ exclude_methods NewRelic::Agent::Instrumentation::MiddlewareTracing, [
156
+ :call
157
+ ]
158
+ end
159
+
160
+ if defined?(NewRelic::Agent::MethodTracerHelpers)
161
+ exclude_methods NewRelic::Agent::MethodTracerHelpers, [
162
+ :trace_execution_scoped,
163
+ :log_errors,
164
+ ]
165
+
166
+ exclude_singleton_methods NewRelic::Agent::MethodTracerHelpers, [
167
+ :trace_execution_scoped,
168
+ :log_errors,
169
+ ]
170
+ end
171
+
172
+ if defined?(NewRelic::Agent::MethodTracer)
173
+ exclude_methods NewRelic::Agent::MethodTracer, [
174
+ :trace_execution_scoped,
175
+ :trace_execution_unscoped,
176
+ ]
177
+ end
178
+ end
179
+
180
+ ##
181
+ # Miscellaneous Methods
182
+ ##
183
+
184
+ if defined?(Mustache)
185
+ exclude_methods Mustache::Context, [
186
+ :fetch
187
+ ]
188
+ end
189
+ end
190
+
191
+ private
192
+
193
+ def exclude_methods(mod, *method_or_methods)
194
+ @profile.exclude_methods!(mod, method_or_methods)
195
+ end
196
+
197
+ def exclude_singleton_methods(mod, *method_or_methods)
198
+ @profile.exclude_singleton_methods!(mod, method_or_methods)
199
+ end
200
+
201
+ def exclude_enumerable(mod, *method_or_methods)
202
+ exclude_methods(mod, [:each, *method_or_methods])
203
+ exclude_methods(mod, ENUMERABLE_NAMES)
204
+ end
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,50 @@
1
+ module RubyProf
2
+ class Profile
3
+ module LegacyMethodElimination
4
+ # eliminate some calls from the graph by merging the information into callers.
5
+ # matchers can be a list of strings or regular expressions or the name of a file containing regexps.
6
+ def eliminate_methods!(matchers)
7
+ RubyProf.deprecation_warning(
8
+ "Method 'eliminate_methods!' is deprecated",
9
+ "Please call 'exclude_methods!' before starting the profile run instead."
10
+ )
11
+ matchers = read_regexps_from_file(matchers) if matchers.is_a?(String)
12
+ eliminated = []
13
+ threads.each do |thread|
14
+ matchers.each{ |matcher| eliminated.concat(eliminate_methods(thread.methods, matcher)) }
15
+ end
16
+ eliminated
17
+ end
18
+
19
+ private
20
+
21
+ # read regexps from file
22
+ def read_regexps_from_file(file_name)
23
+ matchers = []
24
+ File.open(file_name).each_line do |l|
25
+ next if (l =~ /^(#.*|\s*)$/) # emtpy lines and lines starting with #
26
+ matchers << Regexp.new(l.strip)
27
+ end
28
+ matchers
29
+ end
30
+
31
+ # eliminate methods matching matcher
32
+ def eliminate_methods(methods, matcher)
33
+ eliminated = []
34
+ i = 0
35
+ while i < methods.size
36
+ method_info = methods[i]
37
+ method_name = method_info.full_name
38
+ if matcher === method_name
39
+ raise "can't eliminate root method" if method_info.root?
40
+ eliminated << methods.delete_at(i)
41
+ method_info.eliminate!
42
+ else
43
+ i += 1
44
+ end
45
+ end
46
+ eliminated
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,174 @@
1
+ # encoding: utf-8
2
+ require 'tmpdir'
3
+
4
+ module Rack
5
+ class RubyProf
6
+ def initialize(app, options = {})
7
+ @app = app
8
+
9
+ options[:min_percent] ||= 1
10
+
11
+ options[:path] ||= Dir.tmpdir
12
+ FileUtils.mkdir_p(options[:path])
13
+
14
+ @skip_paths = options[:skip_paths] || [%r{^/assets}, %r{\.(css|js|png|jpeg|jpg|gif)$}]
15
+ @only_paths = options[:only_paths]
16
+
17
+ @max_requests = options[:max_requests]
18
+
19
+ @options = options
20
+ end
21
+
22
+ def call(env)
23
+ request = Rack::Request.new(env)
24
+
25
+ if should_profile?(request.path)
26
+ profiler.resume
27
+ begin
28
+ result = @app.call(env)
29
+ ensure
30
+ profiler.pause
31
+ end
32
+
33
+ if profiler.max_requests_reached?
34
+ prefix = if aggregate_requests?
35
+ nil
36
+ else
37
+ request.path.gsub('/', '-')[1..-1]
38
+ end
39
+
40
+ profiler.print!(prefix)
41
+ delete_profiler!
42
+ end
43
+
44
+ result
45
+ else
46
+ @app.call(env)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ class RackProfiler
53
+ def initialize(options)
54
+ @options = options
55
+
56
+ @profile = ::RubyProf::Profile.new(profiling_options)
57
+ @profile.start
58
+ @profile.pause
59
+
60
+ @printer_klasses = options[:printers] || default_printers
61
+
62
+ @tmpdir = options[:path]
63
+
64
+ @max_requests = options[:max_requests] || 1
65
+ @requests_count = 0
66
+
67
+ @printed = false
68
+ # if running across multiple requests, we want to make sure that the
69
+ # ongoing profile is not lost if the process shuts down before the
70
+ # max request count is reached
71
+ ObjectSpace.define_finalizer(self, proc { print! })
72
+ end
73
+
74
+ def resume
75
+ @profile.resume
76
+ end
77
+
78
+ def pause
79
+ @profile.pause
80
+ @requests_count += 1
81
+ end
82
+
83
+ def max_requests_reached?
84
+ @requests_count >= @max_requests
85
+ end
86
+
87
+ def print!(prefix = nil)
88
+ return false if @printed || @requests_count == 0
89
+
90
+ data = @profile.stop
91
+
92
+ prefix ||= "multi-requests-#{@requests_count}"
93
+
94
+ @printer_klasses.each do |printer_klass, base_name|
95
+ printer = printer_klass.new(data)
96
+
97
+ if base_name.respond_to?(:call)
98
+ base_name = base_name.call
99
+ end
100
+
101
+ if printer_klass == ::RubyProf::MultiPrinter \
102
+ || printer_klass == ::RubyProf::CallTreePrinter
103
+ printer.print(@options.merge(:profile => "#{prefix}-#{base_name}"))
104
+ else
105
+ file_name = ::File.join(@tmpdir, "#{prefix}-#{base_name}")
106
+ ::File.open(file_name, 'wb') do |file|
107
+ printer.print(file, @options)
108
+ end
109
+ end
110
+ end
111
+
112
+ @printed = true
113
+ end
114
+
115
+ private
116
+
117
+ def profiling_options
118
+ options = {}
119
+ options[:measure_mode] = ::RubyProf.measure_mode
120
+ options[:exclude_threads] =
121
+ if @options[:ignore_existing_threads]
122
+ Thread.list.select{|t| t != Thread.current}
123
+ else
124
+ ::RubyProf.exclude_threads
125
+ end
126
+ if @options[:request_thread_only]
127
+ options[:include_threads] = [Thread.current]
128
+ end
129
+ if @options[:merge_fibers]
130
+ options[:merge_fibers] = true
131
+ end
132
+ options
133
+ end
134
+
135
+ def default_printers
136
+ {::RubyProf::FlatPrinter => 'flat.txt',
137
+ ::RubyProf::GraphPrinter => 'graph.txt',
138
+ ::RubyProf::GraphHtmlPrinter => 'graph.html',
139
+ ::RubyProf::CallStackPrinter => 'call_stack.html'}
140
+ end
141
+ end
142
+
143
+ def profiler
144
+ if aggregate_requests?
145
+ @@_shared_profiler ||= RackProfiler.new(@options)
146
+ else
147
+ @_profiler ||= RackProfiler.new(@options)
148
+ end
149
+ end
150
+
151
+ def delete_profiler!
152
+ if aggregate_requests?
153
+ @@_shared_profiler.print! if @@_shared_profiler
154
+ @@_shared_profiler = nil
155
+ else
156
+ @_profiler = nil
157
+ end
158
+ end
159
+
160
+ def aggregate_requests?
161
+ !@max_requests.nil?
162
+ end
163
+
164
+ def should_profile?(path)
165
+ return false if paths_match?(path, @skip_paths)
166
+
167
+ @only_paths ? paths_match?(path, @only_paths) : true
168
+ end
169
+
170
+ def paths_match?(path, paths)
171
+ paths.any? { |skip_path| skip_path =~ path }
172
+ end
173
+ end
174
+ end