ruby-prof 2.0.1 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/bin/ruby-prof +336 -338
- data/lib/ruby-prof/version.rb +1 -1
- data/test/call_tree_builder.rb +126 -126
- data/test/exceptions_test.rb +24 -24
- data/test/marshal_test.rb +144 -144
- data/test/printer_call_stack_test.rb +28 -28
- data/test/printer_flame_graph_test.rb +82 -82
- data/test/printer_flat_test.rb +99 -99
- data/test/printer_graph_html_test.rb +62 -62
- data/test/printer_graph_test.rb +42 -42
- data/test/printers_test.rb +162 -162
- data/test/printing_recursive_graph_test.rb +81 -81
- data/test/profile_test.rb +101 -101
- data/test/rack_test.rb +103 -103
- data/test/scheduler.rb +367 -367
- data/test/singleton_test.rb +39 -39
- data/test/thread_test.rb +229 -229
- data/test/yarv_test.rb +56 -56
- metadata +3 -3
data/bin/ruby-prof
CHANGED
|
@@ -1,338 +1,336 @@
|
|
|
1
|
-
#! /usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
# First require ruby-prof
|
|
4
|
-
require 'ruby-prof'
|
|
5
|
-
|
|
6
|
-
# Now setup option parser
|
|
7
|
-
require 'ostruct'
|
|
8
|
-
require 'optparse'
|
|
9
|
-
|
|
10
|
-
module RubyProf
|
|
11
|
-
# == Synopsis
|
|
12
|
-
#
|
|
13
|
-
# Profiles a Ruby program.
|
|
14
|
-
#
|
|
15
|
-
# == Usage
|
|
16
|
-
# ruby-prof [options] <script.rb> [--] [profiled-script-command-line-options]
|
|
17
|
-
#
|
|
18
|
-
# Options:
|
|
19
|
-
# --allow_exceptions Raise exceptions encountered during profiling (true) or suppress them (false)
|
|
20
|
-
# -E, --eval-noprof=code execute the ruby statements (not profiled)
|
|
21
|
-
# --exclude=methods A comma separated list of methods to exclude.
|
|
22
|
-
# Specify instance methods via # (Integer#times)
|
|
23
|
-
# Specify class methods via . (Integer.superclass)
|
|
24
|
-
# --exclude-common Remove common methods from the profile
|
|
25
|
-
# -f, --file=path Output results to a file instead of standard out.
|
|
26
|
-
# -m, --min_percent=min_percent The minimum percent a method must take before
|
|
27
|
-
# being included in output reports.
|
|
28
|
-
# This option is not supported for call tree.
|
|
29
|
-
# --mode=measure_mode Select what ruby-prof should measure:
|
|
30
|
-
# wall - Wall time (default).
|
|
31
|
-
# process - Process time.
|
|
32
|
-
# allocations - Object allocations (requires patched Ruby interpreter).
|
|
33
|
-
# memory - Allocated memory in KB (requires patched Ruby interpreter).
|
|
34
|
-
# -p, --printer=printer Select a printer:
|
|
35
|
-
# flat - Prints a flat profile as text (default).
|
|
36
|
-
# graph - Prints a graph profile as text.
|
|
37
|
-
# graph_html - Prints a graph profile as html.
|
|
38
|
-
# call_tree - format for KCacheGrind
|
|
39
|
-
# call_stack - prints a HTML visualization of the call tree
|
|
40
|
-
# dot - Prints a graph profile as a dot file
|
|
41
|
-
# multi - Creates several reports in output directory
|
|
42
|
-
# -R, --require-noprof=lib require a specific library (not profiled)
|
|
43
|
-
# -s, --sort=sort_mode Select how ruby-prof results should be sorted:
|
|
44
|
-
# total - Total time
|
|
45
|
-
# self - Self time
|
|
46
|
-
# wait - Wait time
|
|
47
|
-
# child - Child time
|
|
48
|
-
# --track_allocations Track allocations while profiling
|
|
49
|
-
# -v, --version version Show version (1.1.0)
|
|
50
|
-
# -h, --help Show help message
|
|
51
|
-
|
|
52
|
-
class Cmd
|
|
53
|
-
# :enddoc:
|
|
54
|
-
attr_accessor :options
|
|
55
|
-
attr_reader :profile
|
|
56
|
-
|
|
57
|
-
def initialize
|
|
58
|
-
setup_options
|
|
59
|
-
parse_args
|
|
60
|
-
|
|
61
|
-
load_pre_libs
|
|
62
|
-
load_pre_execs
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def setup_options
|
|
66
|
-
@options = OpenStruct.new
|
|
67
|
-
options.allow_exceptions = false
|
|
68
|
-
options.exclude = Array.new
|
|
69
|
-
options.exclude_common = false
|
|
70
|
-
options.file = nil
|
|
71
|
-
options.measure_mode = RubyProf::WALL_TIME
|
|
72
|
-
options.min_percent = 0
|
|
73
|
-
options.pre_libs = Array.new
|
|
74
|
-
options.pre_execs = Array.new
|
|
75
|
-
options.printer = RubyProf::FlatPrinter
|
|
76
|
-
options.track_allocations = false
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# This is copied from ActiveSupport:
|
|
80
|
-
def constantize(camel_cased_word)
|
|
81
|
-
if !camel_cased_word.include?("::")
|
|
82
|
-
Object.const_get(camel_cased_word)
|
|
83
|
-
else
|
|
84
|
-
names = camel_cased_word.split("::")
|
|
85
|
-
|
|
86
|
-
# Trigger a built-in NameError exception including the ill-formed constant in the message.
|
|
87
|
-
Object.const_get(camel_cased_word) if names.empty?
|
|
88
|
-
|
|
89
|
-
# Remove the first blank element in case of '::ClassName' notation.
|
|
90
|
-
names.shift if names.size > 1 && names.first.empty?
|
|
91
|
-
|
|
92
|
-
names.inject(Object) do |constant, name|
|
|
93
|
-
if constant == Object
|
|
94
|
-
constant.const_get(name)
|
|
95
|
-
else
|
|
96
|
-
candidate = constant.const_get(name)
|
|
97
|
-
next candidate if constant.const_defined?(name, false)
|
|
98
|
-
next candidate unless Object.const_defined?(name)
|
|
99
|
-
|
|
100
|
-
# Go down the ancestors to check if it is owned directly. The check
|
|
101
|
-
# stops when we reach Object or the end of ancestors tree.
|
|
102
|
-
constant = constant.ancestors.inject(constant) do |const, ancestor|
|
|
103
|
-
break const if ancestor == Object
|
|
104
|
-
break ancestor if ancestor.const_defined?(name, false)
|
|
105
|
-
const
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# owner is in Object, so raise
|
|
109
|
-
constant.const_get(name, false)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def option_parser
|
|
116
|
-
OptionParser.new do |opts|
|
|
117
|
-
opts.banner = "ruby_prof #{RubyProf::VERSION}\n" +
|
|
118
|
-
"Usage: ruby-prof [options] <script.rb> [--] [profiled-script-command-line-options]"
|
|
119
|
-
|
|
120
|
-
opts.separator ""
|
|
121
|
-
opts.separator "Options:"
|
|
122
|
-
|
|
123
|
-
opts.on('--allow_exceptions', 'Raise exceptions encountered during profiling (true) or suppress them (false)') do
|
|
124
|
-
options.allow_exceptions = true
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
opts.on('-E code', '--eval-noprof=code', 'execute the ruby statements (not profiled)') do |code|
|
|
128
|
-
options.pre_execs << code
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
opts.on('--exclude=methods', String,
|
|
132
|
-
'A comma separated list of methods to exclude.',
|
|
133
|
-
' Specify instance methods via # (Integer#times)',
|
|
134
|
-
' Specify class methods via . (Integer.superclass)') do |exclude_string|
|
|
135
|
-
exclude_string.split(',').each do |string|
|
|
136
|
-
match = string.strip.match(/(.*)(#|\.)(.*)/)
|
|
137
|
-
klass = constantize(match[1])
|
|
138
|
-
if match[2] == '.'
|
|
139
|
-
klass = klass.singleton_class
|
|
140
|
-
end
|
|
141
|
-
method = match[3].to_sym
|
|
142
|
-
options.exclude << [klass, method]
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
opts.on('--exclude-common', 'Remove common methods from the profile') do
|
|
147
|
-
options.exclude_common = true
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
opts.on('-f path', '--file=path',
|
|
151
|
-
'Output results to a file instead of standard out.') do |file|
|
|
152
|
-
options.file = file
|
|
153
|
-
options.old_wd = Dir.pwd
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
opts.on('-m min_percent', '--min_percent=min_percent', Float,
|
|
157
|
-
'The minimum percent a method must take before ',
|
|
158
|
-
' being included in output reports.',
|
|
159
|
-
' This option is not supported for call tree.') do |min_percent|
|
|
160
|
-
options.min_percent = min_percent
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
opts.on('--mode=measure_mode',
|
|
164
|
-
[:process, :wall, :allocations, :memory],
|
|
165
|
-
'Select what ruby-prof should measure:',
|
|
166
|
-
' wall - Wall time (default).',
|
|
167
|
-
' process - Process time.',
|
|
168
|
-
' allocations - Object allocations (requires patched Ruby interpreter).') do |measure_mode|
|
|
169
|
-
|
|
170
|
-
case measure_mode
|
|
171
|
-
when :wall
|
|
172
|
-
options.measure_mode = RubyProf::WALL_TIME
|
|
173
|
-
when :process
|
|
174
|
-
options.measure_mode = RubyProf::PROCESS_TIME
|
|
175
|
-
when :allocations
|
|
176
|
-
options.measure_mode = RubyProf::ALLOCATIONS
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
opts.on('-p printer', '--printer=printer', [:flat, :flat_with_line_numbers, :graph, :graph_html, :call_tree, :call_stack, :dot, :multi],
|
|
181
|
-
'Select a printer:',
|
|
182
|
-
' flat - Prints a flat profile as text (default).',
|
|
183
|
-
' graph - Prints a graph profile as text.',
|
|
184
|
-
' graph_html - Prints a graph profile as html.',
|
|
185
|
-
' call_tree - format for KCacheGrind',
|
|
186
|
-
' call_stack - prints a HTML visualization of the call tree',
|
|
187
|
-
' dot - Prints a graph profile as a dot file',
|
|
188
|
-
' multi - Creates several reports in output directory'
|
|
189
|
-
) do |printer|
|
|
190
|
-
|
|
191
|
-
case printer
|
|
192
|
-
when :flat
|
|
193
|
-
options.printer = RubyProf::FlatPrinter
|
|
194
|
-
when :graph
|
|
195
|
-
options.printer = RubyProf::GraphPrinter
|
|
196
|
-
when :graph_html
|
|
197
|
-
options.printer = RubyProf::GraphHtmlPrinter
|
|
198
|
-
when :call_tree
|
|
199
|
-
options.printer = RubyProf::CallTreePrinter
|
|
200
|
-
when :call_stack
|
|
201
|
-
options.printer = RubyProf::CallStackPrinter
|
|
202
|
-
when :dot
|
|
203
|
-
options.printer = RubyProf::DotPrinter
|
|
204
|
-
when :multi
|
|
205
|
-
options.printer = RubyProf::MultiPrinter
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
opts.on('-R lib', '--require-noprof=lib', 'require a specific library (not profiled)') do |lib|
|
|
210
|
-
options.pre_libs << lib
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
opts.on('-s sort_mode', '--sort=sort_mode', [:total, :self, :wait, :child],
|
|
214
|
-
'Select how ruby-prof results should be sorted:',
|
|
215
|
-
' total - Total time',
|
|
216
|
-
' self - Self time',
|
|
217
|
-
' wait - Wait time',
|
|
218
|
-
' child - Child time') do |sort_mode|
|
|
219
|
-
|
|
220
|
-
options.sort_method = case sort_mode
|
|
221
|
-
when :total
|
|
222
|
-
:total_time
|
|
223
|
-
when :self
|
|
224
|
-
:self_time
|
|
225
|
-
when :wait
|
|
226
|
-
:wait_time
|
|
227
|
-
when :child
|
|
228
|
-
:children_time
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
opts.on('--track_allocations', 'Track allocations while profiling') do
|
|
233
|
-
options.track_allocations = true
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
opts.on_tail("-v version", "--version", "Show version (#{RubyProf::VERSION})") do
|
|
237
|
-
puts "ruby_prof " + RubyProf::VERSION
|
|
238
|
-
exit
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
opts.on_tail("-h", "--help", "Show help message") do
|
|
242
|
-
puts opts
|
|
243
|
-
exit
|
|
244
|
-
end
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def parse_args
|
|
249
|
-
# Make sure the user specified at least one file
|
|
250
|
-
if ARGV.length < 1 and not options.exec
|
|
251
|
-
puts self.option_parser
|
|
252
|
-
puts ""
|
|
253
|
-
puts "Must specify a script to run"
|
|
254
|
-
exit(-1)
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
self.option_parser.parse! ARGV
|
|
258
|
-
|
|
259
|
-
if options.printer.needs_dir?
|
|
260
|
-
options.file ||= "."
|
|
261
|
-
options.old_wd ||= Dir.pwd
|
|
262
|
-
if !File.directory?(options.file)
|
|
263
|
-
puts "'#{options.file}' is not a directory"
|
|
264
|
-
puts "#{options.printer} needs an existing directory path to put profiles under."
|
|
265
|
-
exit(-1)
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, OptionParser::MissingArgument => e
|
|
269
|
-
puts self.option_parser
|
|
270
|
-
puts e.message
|
|
271
|
-
exit(-1)
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def load_pre_libs
|
|
275
|
-
options.pre_libs.each do |lib|
|
|
276
|
-
require lib
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
def load_pre_execs
|
|
281
|
-
options.pre_execs.each do |exec|
|
|
282
|
-
eval(exec)
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
def run
|
|
287
|
-
profile_options = {:allow_exceptions => options.allow_exceptions,
|
|
288
|
-
:exclude_common => options.exclude_common,
|
|
289
|
-
:measure_mode => options.measure_mode,
|
|
290
|
-
:track_allocations => options.track_allocations}
|
|
291
|
-
|
|
292
|
-
@profile = Profile.new(**profile_options)
|
|
293
|
-
|
|
294
|
-
options.exclude.each do |klass, method|
|
|
295
|
-
@profile.exclude_method!(klass, method)
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
script = ARGV.shift
|
|
299
|
-
profile.profile do
|
|
300
|
-
load script
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
# Parse command line options
|
|
307
|
-
cmd = RubyProf::Cmd.new
|
|
308
|
-
|
|
309
|
-
# Install at_exit handler. It is important that we do this
|
|
310
|
-
# before loading the scripts so our at_exit handler run
|
|
311
|
-
# *after* any other one that will be installed.
|
|
312
|
-
|
|
313
|
-
at_exit {
|
|
314
|
-
# Create a printer
|
|
315
|
-
printer = cmd.options.printer.new(cmd.profile)
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
# Now profile some code
|
|
338
|
-
cmd.run
|
|
1
|
+
#! /usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# First require ruby-prof
|
|
4
|
+
require 'ruby-prof'
|
|
5
|
+
|
|
6
|
+
# Now setup option parser
|
|
7
|
+
require 'ostruct'
|
|
8
|
+
require 'optparse'
|
|
9
|
+
|
|
10
|
+
module RubyProf
|
|
11
|
+
# == Synopsis
|
|
12
|
+
#
|
|
13
|
+
# Profiles a Ruby program.
|
|
14
|
+
#
|
|
15
|
+
# == Usage
|
|
16
|
+
# ruby-prof [options] <script.rb> [--] [profiled-script-command-line-options]
|
|
17
|
+
#
|
|
18
|
+
# Options:
|
|
19
|
+
# --allow_exceptions Raise exceptions encountered during profiling (true) or suppress them (false)
|
|
20
|
+
# -E, --eval-noprof=code execute the ruby statements (not profiled)
|
|
21
|
+
# --exclude=methods A comma separated list of methods to exclude.
|
|
22
|
+
# Specify instance methods via # (Integer#times)
|
|
23
|
+
# Specify class methods via . (Integer.superclass)
|
|
24
|
+
# --exclude-common Remove common methods from the profile
|
|
25
|
+
# -f, --file=path Output results to a file instead of standard out.
|
|
26
|
+
# -m, --min_percent=min_percent The minimum percent a method must take before
|
|
27
|
+
# being included in output reports.
|
|
28
|
+
# This option is not supported for call tree.
|
|
29
|
+
# --mode=measure_mode Select what ruby-prof should measure:
|
|
30
|
+
# wall - Wall time (default).
|
|
31
|
+
# process - Process time.
|
|
32
|
+
# allocations - Object allocations (requires patched Ruby interpreter).
|
|
33
|
+
# memory - Allocated memory in KB (requires patched Ruby interpreter).
|
|
34
|
+
# -p, --printer=printer Select a printer:
|
|
35
|
+
# flat - Prints a flat profile as text (default).
|
|
36
|
+
# graph - Prints a graph profile as text.
|
|
37
|
+
# graph_html - Prints a graph profile as html.
|
|
38
|
+
# call_tree - format for KCacheGrind
|
|
39
|
+
# call_stack - prints a HTML visualization of the call tree
|
|
40
|
+
# dot - Prints a graph profile as a dot file
|
|
41
|
+
# multi - Creates several reports in output directory
|
|
42
|
+
# -R, --require-noprof=lib require a specific library (not profiled)
|
|
43
|
+
# -s, --sort=sort_mode Select how ruby-prof results should be sorted:
|
|
44
|
+
# total - Total time
|
|
45
|
+
# self - Self time
|
|
46
|
+
# wait - Wait time
|
|
47
|
+
# child - Child time
|
|
48
|
+
# --track_allocations Track allocations while profiling
|
|
49
|
+
# -v, --version version Show version (1.1.0)
|
|
50
|
+
# -h, --help Show help message
|
|
51
|
+
|
|
52
|
+
class Cmd
|
|
53
|
+
# :enddoc:
|
|
54
|
+
attr_accessor :options
|
|
55
|
+
attr_reader :profile
|
|
56
|
+
|
|
57
|
+
def initialize
|
|
58
|
+
setup_options
|
|
59
|
+
parse_args
|
|
60
|
+
|
|
61
|
+
load_pre_libs
|
|
62
|
+
load_pre_execs
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def setup_options
|
|
66
|
+
@options = OpenStruct.new
|
|
67
|
+
options.allow_exceptions = false
|
|
68
|
+
options.exclude = Array.new
|
|
69
|
+
options.exclude_common = false
|
|
70
|
+
options.file = nil
|
|
71
|
+
options.measure_mode = RubyProf::WALL_TIME
|
|
72
|
+
options.min_percent = 0
|
|
73
|
+
options.pre_libs = Array.new
|
|
74
|
+
options.pre_execs = Array.new
|
|
75
|
+
options.printer = RubyProf::FlatPrinter
|
|
76
|
+
options.track_allocations = false
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# This is copied from ActiveSupport:
|
|
80
|
+
def constantize(camel_cased_word)
|
|
81
|
+
if !camel_cased_word.include?("::")
|
|
82
|
+
Object.const_get(camel_cased_word)
|
|
83
|
+
else
|
|
84
|
+
names = camel_cased_word.split("::")
|
|
85
|
+
|
|
86
|
+
# Trigger a built-in NameError exception including the ill-formed constant in the message.
|
|
87
|
+
Object.const_get(camel_cased_word) if names.empty?
|
|
88
|
+
|
|
89
|
+
# Remove the first blank element in case of '::ClassName' notation.
|
|
90
|
+
names.shift if names.size > 1 && names.first.empty?
|
|
91
|
+
|
|
92
|
+
names.inject(Object) do |constant, name|
|
|
93
|
+
if constant == Object
|
|
94
|
+
constant.const_get(name)
|
|
95
|
+
else
|
|
96
|
+
candidate = constant.const_get(name)
|
|
97
|
+
next candidate if constant.const_defined?(name, false)
|
|
98
|
+
next candidate unless Object.const_defined?(name)
|
|
99
|
+
|
|
100
|
+
# Go down the ancestors to check if it is owned directly. The check
|
|
101
|
+
# stops when we reach Object or the end of ancestors tree.
|
|
102
|
+
constant = constant.ancestors.inject(constant) do |const, ancestor|
|
|
103
|
+
break const if ancestor == Object
|
|
104
|
+
break ancestor if ancestor.const_defined?(name, false)
|
|
105
|
+
const
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# owner is in Object, so raise
|
|
109
|
+
constant.const_get(name, false)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def option_parser
|
|
116
|
+
OptionParser.new do |opts|
|
|
117
|
+
opts.banner = "ruby_prof #{RubyProf::VERSION}\n" +
|
|
118
|
+
"Usage: ruby-prof [options] <script.rb> [--] [profiled-script-command-line-options]"
|
|
119
|
+
|
|
120
|
+
opts.separator ""
|
|
121
|
+
opts.separator "Options:"
|
|
122
|
+
|
|
123
|
+
opts.on('--allow_exceptions', 'Raise exceptions encountered during profiling (true) or suppress them (false)') do
|
|
124
|
+
options.allow_exceptions = true
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
opts.on('-E code', '--eval-noprof=code', 'execute the ruby statements (not profiled)') do |code|
|
|
128
|
+
options.pre_execs << code
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
opts.on('--exclude=methods', String,
|
|
132
|
+
'A comma separated list of methods to exclude.',
|
|
133
|
+
' Specify instance methods via # (Integer#times)',
|
|
134
|
+
' Specify class methods via . (Integer.superclass)') do |exclude_string|
|
|
135
|
+
exclude_string.split(',').each do |string|
|
|
136
|
+
match = string.strip.match(/(.*)(#|\.)(.*)/)
|
|
137
|
+
klass = constantize(match[1])
|
|
138
|
+
if match[2] == '.'
|
|
139
|
+
klass = klass.singleton_class
|
|
140
|
+
end
|
|
141
|
+
method = match[3].to_sym
|
|
142
|
+
options.exclude << [klass, method]
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
opts.on('--exclude-common', 'Remove common methods from the profile') do
|
|
147
|
+
options.exclude_common = true
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
opts.on('-f path', '--file=path',
|
|
151
|
+
'Output results to a file instead of standard out.') do |file|
|
|
152
|
+
options.file = file
|
|
153
|
+
options.old_wd = Dir.pwd
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
opts.on('-m min_percent', '--min_percent=min_percent', Float,
|
|
157
|
+
'The minimum percent a method must take before ',
|
|
158
|
+
' being included in output reports.',
|
|
159
|
+
' This option is not supported for call tree.') do |min_percent|
|
|
160
|
+
options.min_percent = min_percent
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
opts.on('--mode=measure_mode',
|
|
164
|
+
[:process, :wall, :allocations, :memory],
|
|
165
|
+
'Select what ruby-prof should measure:',
|
|
166
|
+
' wall - Wall time (default).',
|
|
167
|
+
' process - Process time.',
|
|
168
|
+
' allocations - Object allocations (requires patched Ruby interpreter).') do |measure_mode|
|
|
169
|
+
|
|
170
|
+
case measure_mode
|
|
171
|
+
when :wall
|
|
172
|
+
options.measure_mode = RubyProf::WALL_TIME
|
|
173
|
+
when :process
|
|
174
|
+
options.measure_mode = RubyProf::PROCESS_TIME
|
|
175
|
+
when :allocations
|
|
176
|
+
options.measure_mode = RubyProf::ALLOCATIONS
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
opts.on('-p printer', '--printer=printer', [:flat, :flat_with_line_numbers, :graph, :graph_html, :call_tree, :call_stack, :dot, :multi],
|
|
181
|
+
'Select a printer:',
|
|
182
|
+
' flat - Prints a flat profile as text (default).',
|
|
183
|
+
' graph - Prints a graph profile as text.',
|
|
184
|
+
' graph_html - Prints a graph profile as html.',
|
|
185
|
+
' call_tree - format for KCacheGrind',
|
|
186
|
+
' call_stack - prints a HTML visualization of the call tree',
|
|
187
|
+
' dot - Prints a graph profile as a dot file',
|
|
188
|
+
' multi - Creates several reports in output directory'
|
|
189
|
+
) do |printer|
|
|
190
|
+
|
|
191
|
+
case printer
|
|
192
|
+
when :flat
|
|
193
|
+
options.printer = RubyProf::FlatPrinter
|
|
194
|
+
when :graph
|
|
195
|
+
options.printer = RubyProf::GraphPrinter
|
|
196
|
+
when :graph_html
|
|
197
|
+
options.printer = RubyProf::GraphHtmlPrinter
|
|
198
|
+
when :call_tree
|
|
199
|
+
options.printer = RubyProf::CallTreePrinter
|
|
200
|
+
when :call_stack
|
|
201
|
+
options.printer = RubyProf::CallStackPrinter
|
|
202
|
+
when :dot
|
|
203
|
+
options.printer = RubyProf::DotPrinter
|
|
204
|
+
when :multi
|
|
205
|
+
options.printer = RubyProf::MultiPrinter
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
opts.on('-R lib', '--require-noprof=lib', 'require a specific library (not profiled)') do |lib|
|
|
210
|
+
options.pre_libs << lib
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
opts.on('-s sort_mode', '--sort=sort_mode', [:total, :self, :wait, :child],
|
|
214
|
+
'Select how ruby-prof results should be sorted:',
|
|
215
|
+
' total - Total time',
|
|
216
|
+
' self - Self time',
|
|
217
|
+
' wait - Wait time',
|
|
218
|
+
' child - Child time') do |sort_mode|
|
|
219
|
+
|
|
220
|
+
options.sort_method = case sort_mode
|
|
221
|
+
when :total
|
|
222
|
+
:total_time
|
|
223
|
+
when :self
|
|
224
|
+
:self_time
|
|
225
|
+
when :wait
|
|
226
|
+
:wait_time
|
|
227
|
+
when :child
|
|
228
|
+
:children_time
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
opts.on('--track_allocations', 'Track allocations while profiling') do
|
|
233
|
+
options.track_allocations = true
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
opts.on_tail("-v version", "--version", "Show version (#{RubyProf::VERSION})") do
|
|
237
|
+
puts "ruby_prof " + RubyProf::VERSION
|
|
238
|
+
exit
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
opts.on_tail("-h", "--help", "Show help message") do
|
|
242
|
+
puts opts
|
|
243
|
+
exit
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def parse_args
|
|
249
|
+
# Make sure the user specified at least one file
|
|
250
|
+
if ARGV.length < 1 and not options.exec
|
|
251
|
+
puts self.option_parser
|
|
252
|
+
puts ""
|
|
253
|
+
puts "Must specify a script to run"
|
|
254
|
+
exit(-1)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
self.option_parser.parse! ARGV
|
|
258
|
+
|
|
259
|
+
if options.printer.needs_dir?
|
|
260
|
+
options.file ||= "."
|
|
261
|
+
options.old_wd ||= Dir.pwd
|
|
262
|
+
if !File.directory?(options.file)
|
|
263
|
+
puts "'#{options.file}' is not a directory"
|
|
264
|
+
puts "#{options.printer} needs an existing directory path to put profiles under."
|
|
265
|
+
exit(-1)
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, OptionParser::MissingArgument => e
|
|
269
|
+
puts self.option_parser
|
|
270
|
+
puts e.message
|
|
271
|
+
exit(-1)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def load_pre_libs
|
|
275
|
+
options.pre_libs.each do |lib|
|
|
276
|
+
require lib
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def load_pre_execs
|
|
281
|
+
options.pre_execs.each do |exec|
|
|
282
|
+
eval(exec)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def run
|
|
287
|
+
profile_options = {:allow_exceptions => options.allow_exceptions,
|
|
288
|
+
:exclude_common => options.exclude_common,
|
|
289
|
+
:measure_mode => options.measure_mode,
|
|
290
|
+
:track_allocations => options.track_allocations}
|
|
291
|
+
|
|
292
|
+
@profile = Profile.new(**profile_options)
|
|
293
|
+
|
|
294
|
+
options.exclude.each do |klass, method|
|
|
295
|
+
@profile.exclude_method!(klass, method)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
script = ARGV.shift
|
|
299
|
+
profile.profile do
|
|
300
|
+
load script
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Parse command line options
|
|
307
|
+
cmd = RubyProf::Cmd.new
|
|
308
|
+
|
|
309
|
+
# Install at_exit handler. It is important that we do this
|
|
310
|
+
# before loading the scripts so our at_exit handler run
|
|
311
|
+
# *after* any other one that will be installed.
|
|
312
|
+
|
|
313
|
+
at_exit {
|
|
314
|
+
# Create a printer
|
|
315
|
+
printer = cmd.options.printer.new(cmd.profile)
|
|
316
|
+
|
|
317
|
+
# Get output
|
|
318
|
+
if cmd.options.file
|
|
319
|
+
# write it relative to the dir they *started* in, as it's a bit surprising to write it in the dir they end up in.
|
|
320
|
+
Dir.chdir(cmd.options.old_wd) do
|
|
321
|
+
if printer.class.needs_dir?
|
|
322
|
+
printer.print(path: cmd.options.file, min_percent: cmd.options.min_percent, sort_method: cmd.options.sort_method)
|
|
323
|
+
else
|
|
324
|
+
File.open(cmd.options.file, 'w') do |file|
|
|
325
|
+
printer.print(file, min_percent: cmd.options.min_percent, sort_method: cmd.options.sort_method)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
else
|
|
330
|
+
# Print out results
|
|
331
|
+
printer.print(STDOUT, min_percent: cmd.options.min_percent, sort_method: cmd.options.sort_method)
|
|
332
|
+
end
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
# Now profile some code
|
|
336
|
+
cmd.run
|