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.
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
- printer_options = {:min_percent => cmd.options.min_percent,
317
- :sort_method => cmd.options.sort_method}
318
-
319
- # Get output
320
- if cmd.options.file
321
- # 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.
322
- Dir.chdir(cmd.options.old_wd) do
323
- if printer.class.needs_dir?
324
- printer.print(printer_options.merge(:path => cmd.options.file))
325
- else
326
- File.open(cmd.options.file, 'w') do |file|
327
- printer.print(file, printer_options)
328
- end
329
- end
330
- end
331
- else
332
- # Print out results
333
- printer.print(STDOUT, printer_options)
334
- end
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