rubinius-profiler 2.0.2 → 2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3e4c9fa8b3b0b60100946a5df72813837b22277c
4
- data.tar.gz: 2a1d31d54834f9d28a5bc03a405ff5ad86237188
3
+ metadata.gz: ad4c72f244576ac785fb4d34c2f6b80de5d89ca2
4
+ data.tar.gz: 5c6c617e535205dd682ed86f116bda2e2831753b
5
5
  SHA512:
6
- metadata.gz: d0b524467f23268cd28a8d18692b000332ca91191cb9d0be03d4aea78c18b3febf879cf367fe4bf8ab2149c62fdcdddc7d0b07f41838541b8f99e7c93b9b4551
7
- data.tar.gz: d002bcdf062824738b004dec08d192dd27303eb2c082cd575d3c1bbe63506a739ca72b3ed6d1fd5bf92c2a5a0b84f42144dd8d23f772e9f0f04d42e167fda622
6
+ metadata.gz: f90fc9103e3c7c43558c3c92024a76bca443beb88ca2e6e9b664a4065b9bb367ed2bac1908ee5ddd19adb3eab603e9e4c8b19c859916447dc3ac68f1d184b437
7
+ data.tar.gz: 78d961bb9c71a2da06ee26e4b47601846c64768badfa6afdb65dd00040c3603f8ab4533cc17bfc24c1547ade5625803e98c2c5cdd9c9c24d9299852d8be2fffd
@@ -1,444 +1,4 @@
1
1
  module Rubinius
2
2
  module Profiler
3
-
4
- ##
5
- # Interface to VM's instrumenting profiler.
6
-
7
- class Instrumenter
8
- include Stats::Units
9
-
10
- attr_reader :info, :options
11
-
12
- def self.available?
13
- Rubinius::Tooling.available?
14
- end
15
-
16
- def self.active?
17
- Rubinius::Tooling.active?
18
- end
19
-
20
- @loaded = false
21
- def self.load
22
- return if @loaded
23
- Rubinius::Tooling.load File.expand_path("../ext/profiler", __FILE__)
24
- @loaded = true
25
- end
26
-
27
- def initialize(options = {})
28
- Instrumenter.load
29
-
30
- @options = { :sort => :percent }
31
- set_options options
32
- set_options :full_report => true if Config["profiler.full_report"]
33
- set_options :graph => true if Config["profiler.graph"]
34
-
35
- if path = Config["profiler.json"]
36
- set_options :json => path
37
- end
38
-
39
- if name = Config["profiler.output"]
40
- set_options :output => "#{File.expand_path(name)}-#{$$}"
41
- end
42
-
43
- if Config['profiler.cumulative_percentage']
44
- set_options :cumulative_percentage => true
45
- end
46
-
47
- if Config['profiler.classes']
48
- set_options :classes => true
49
- end
50
- end
51
-
52
- # Set options for profiler output. Presently, the only option
53
- # is :sort. It takes a single symbol or an array of symbols to
54
- # specify the column(s) to sort by. The recognized symbols are:
55
- #
56
- # Symbol Profiler heading
57
- # :percent % time
58
- # :total_seconds cumulative seconds
59
- # :self_seconds self seconds
60
- # :calls calls
61
- # :self_ms self ms/call
62
- # :total_ms total ms/call
63
- # :name name
64
- #
65
- # @todo Add options for GC allocation counts
66
- def set_options(options)
67
- @options.merge!(options)
68
- end
69
-
70
- def start
71
- Rubinius::Tooling.enable
72
- end
73
-
74
- def __stop__
75
- Rubinius::Tooling.disable
76
- end
77
-
78
- def stop
79
- @info = __stop__
80
- end
81
-
82
- # Convenience method to profile snippets of code in a larger script or
83
- # program. Enables the profiler and yields to the given block.
84
- #
85
- # pr = Rubinius::Profiler::Instrumenter.new
86
- # pr.profile { # do some work here }
87
- def profile(display = true)
88
- start
89
- yield if block_given?
90
- stop
91
- show if display
92
- @info
93
- end
94
-
95
- SHORT_LINES = 45
96
-
97
- def show(out=$stdout)
98
- unless self.class.available? and @info
99
- out.puts "No profiling data was available"
100
- return
101
- end
102
-
103
- begin
104
- if name = options[:output]
105
- out = File.open name, "w"
106
- end
107
-
108
- if options[:json]
109
- json options[:json]
110
- elsif options[:graph]
111
- graph out
112
- else
113
- flat out
114
- end
115
- ensure
116
- out.close if options[:output]
117
- end
118
-
119
- nil
120
- end
121
-
122
- def epilogue(out, size, calls)
123
- unless options[:full_report] or size < SHORT_LINES
124
- out.puts "\n#{comma(size-SHORT_LINES)} methods omitted"
125
- end
126
- out.puts "\n#{comma(size)} methods called a total of #{comma(calls)} times"
127
- end
128
-
129
- def flat(out)
130
- keys = @info.keys.sort
131
-
132
- keys.each do |t_id|
133
- thread_flat out, t_id, @info[t_id]
134
- puts
135
- end
136
- end
137
-
138
- def thread_flat(out, t_id, info)
139
- total_calls = 0
140
- total = 0.0
141
-
142
- all_selves = 0.0
143
-
144
- # Because the info is actually in a tree form and we to just see it
145
- # flat for each method, we need to go through and collect all the stats
146
- # for each unique method.
147
-
148
- info[:nodes].each do |n_id, data|
149
- sub = data[4].inject(0) do |a,n|
150
- next unless x = info[:nodes][n]
151
- a + x[1]
152
- end
153
-
154
- meth = info[:methods][data[0]]
155
- if cur = meth[:edge_total]
156
- meth[:edge_total] = cur + sub
157
- else
158
- meth[:edge_total] = sub
159
- end
160
- end
161
-
162
- data = info[:methods].values.map do |m|
163
- cumulative = m[:cumulative]
164
- method_total = m[:total]
165
- edges_total = m[:edge_total]
166
- self_total = method_total - edges_total
167
- called = m[:called]
168
- total += method_total
169
- total_calls += called
170
-
171
- all_selves += self_total
172
-
173
- name = m[:name]
174
- name = "#toplevel" if name == "<metaclass>#__script__ {}"
175
- [ 0,
176
- sec(cumulative),
177
- sec(self_total),
178
- called,
179
- msec(self_total) / called,
180
- msec(cumulative) / called,
181
- name ]
182
- end
183
-
184
- all_selves = sec(all_selves)
185
-
186
- if options[:cumulative_percentage]
187
- data.each do |d|
188
- d[0] = (d[1] / sec(info[:runtime])) * 100
189
- end
190
- else
191
- data.each do |d|
192
- d[0] = (d[2] / all_selves) * 100
193
- end
194
- end
195
-
196
- columns = sort_order
197
- data = data.sort_by do |row|
198
- columns.map {|col| row[col] }
199
- end.reverse
200
-
201
- out.puts "Thread #{t_id}: total running time: #{sec(info[:runtime])}s"
202
- out.puts ""
203
- out.puts " % cumulative self self total"
204
- out.puts " time seconds seconds calls ms/call ms/call name"
205
- out.puts "------------------------------------------------------------"
206
-
207
- report = options[:full_report] ? data : data.first(SHORT_LINES)
208
- report.each do |d|
209
- out.printf " %6s", ("%.2f" % d[0])
210
- out.printf "%8.2f %8.2f %10d %8.2f %8.2f %s\n", *d.last(6)
211
- end
212
-
213
- epilogue out, data.size, total_calls
214
-
215
- if options[:classes]
216
- classes = Hash.new { |h,k| h[k] = 0.0 }
217
-
218
- data.each do |row|
219
- if m = /(.*)[#\.]/.match(row.last)
220
- classes[m[1]] += ((row[2] / all_selves) * 100)
221
- end
222
- end
223
-
224
- out.puts "\nUsage percentage by class:"
225
- sorted = classes.to_a.sort_by { |row| row[1] }.reverse
226
- sorted.each do |row|
227
- out.printf "%6s: %s\n", ("%.2f" % row[1]), row[0]
228
- end
229
- end
230
- end
231
-
232
- def json(path)
233
- File.open path, "w" do |f|
234
- t_final = @info.size - 1
235
- t_idx = 0
236
-
237
- f.puts "["
238
-
239
- @info.each do |t_id, info|
240
- f.puts "{"
241
- f.puts " \"thread_id\": #{t_id},"
242
- f.puts " \"runtime\": #{info[:runtime]},"
243
- f.puts " \"total_nodes\": #{info[:total_nodes]},"
244
- roots = info[:roots].map { |x| x.to_s.dump }.join(',')
245
- f.puts " \"roots\": [ #{roots} ],"
246
- f.puts " \"nodes\": {"
247
- idx = 0
248
- final = info[:nodes].size - 1
249
-
250
- info[:nodes].each do |n_id, data|
251
- f.puts " \"#{n_id}\": {"
252
- f.puts " \"method\": #{data[0]}, \"total\": #{data[1]}, \"called\": #{data[2]},"
253
- f.puts " \"total_nodes\": #{data[3]}, \"sub_nodes\": [ #{data[4].join(', ')} ]"
254
- if idx == final
255
- f.puts " }"
256
- else
257
- f.puts " },"
258
- end
259
- idx += 1
260
- end
261
-
262
- f.puts " },"
263
- f.puts " \"methods\": {"
264
-
265
- idx = 0
266
- final = info[:methods].size - 1
267
- info[:methods].each do |m_id, m|
268
- f.puts " \"#{m_id}\": {"
269
- f.puts " \"name\": \"#{m[:name]}\", \"file\": \"#{m[:file]}\", \"line\": #{m[:line] || 0},"
270
- f.puts " \"cumulative\": #{m[:cumulative]}, \"total\": #{m[:total]}, \"called\": #{m[:called]}"
271
- if idx == final
272
- f.puts " }"
273
- else
274
- f.puts " },"
275
- end
276
- idx += 1
277
- end
278
-
279
- f.puts " }"
280
-
281
- if t_idx == t_final
282
- f.puts "}"
283
- else
284
- f.puts "},"
285
- end
286
-
287
- t_idx += 1
288
- end
289
- f.puts "]"
290
- end
291
-
292
- puts "Wrote JSON to: #{path}"
293
- end
294
-
295
- def graph(out)
296
- keys = @info.keys.sort
297
-
298
- keys.each do |t_id|
299
- thread_graph out, t_id, @info[t_id]
300
- out.puts
301
- end
302
- end
303
-
304
- # Prints an entry for each method, along with the method's callers and
305
- # the methods called. The entry is delimited by the dashed lines. The
306
- # line for the method itself is called the "primary" line. The callers
307
- # are printed above the primary line and the methods called are printed
308
- # below.
309
- def thread_graph(out, t_id, info)
310
- total_calls = 0
311
- run_total = 0.0
312
-
313
- data = info[:nodes]
314
-
315
- methods = info[:methods]
316
-
317
- run_total = info[:runtime].to_f
318
-
319
- all_callers = Hash.new { |h,k| h[k] = [] }
320
-
321
- data.each do |n_id, n_data|
322
- n_data[4].each do |sub|
323
- all_callers[sub] << n_id
324
- end
325
- end
326
-
327
- indexes = data.keys.sort do |a, b|
328
- data[b][1] <=> data[a][1]
329
- end
330
-
331
- indexes = indexes.first(SHORT_LINES) unless options[:full_report]
332
-
333
- shown_indexes = {}
334
-
335
- indexes.each_with_index do |id, index|
336
- shown_indexes[id] = index + 1
337
- end
338
-
339
- out.puts "===== Thread #{t_id} ====="
340
- out.puts "Total running time: #{sec(info[:runtime])}s"
341
- out.puts "index % time self children called name"
342
- out.puts "----------------------------------------------------------"
343
-
344
- primary = "%-7s%6s %8.2f %9.2f %8d %s [%d]\n"
345
- secondary = " %8.2f %9.2f %8d %s%s\n"
346
-
347
- indexes.each do |id|
348
- m_id, total, called, tn, sub_nodes = data[id]
349
-
350
- # The idea is to report information about caller as a ratio of the
351
- # time it called method.
352
- #
353
-
354
- callers = all_callers[id].sort_by do |c_id|
355
- clr = data[c_id]
356
-
357
- clr[total]
358
- end
359
-
360
- callers = callers.first(10) unless options[:full_report]
361
-
362
- callers.each do |c_id|
363
- clr_m_id, clr_total, clr_called, clr_tn, clr_sub = data[c_id]
364
-
365
- sub_total = clr_sub.inject(0) { |a,s| a + data[s][1] }
366
-
367
- self_total = clr_total - sub_total
368
- out.printf(secondary, sec(self_total),
369
- sec(sub_total),
370
- clr_called,
371
- methods[clr_m_id][:name],
372
- graph_method_index(shown_indexes[c_id]))
373
- end
374
-
375
- # Now the primary line.
376
-
377
- children = sub_nodes.inject(0) { |a,s| a + data[s][1] }
378
- # children = method[:cumulative] * (method[:edges_total].to_f / method[:total])
379
-
380
- self_total = total - children
381
- out.printf primary, ("[%d]" % shown_indexes[id]),
382
- percentage(total, run_total, 1, nil),
383
- sec(self_total),
384
- sec(children),
385
- called,
386
- methods[m_id][:name],
387
- shown_indexes[id]
388
-
389
- # Same as caller, the idea is to report information about callee methods
390
- # as a ratio of the time it was called from method.
391
- #
392
-
393
- edges = sub_nodes.sort_by do |e_id|
394
- if edge = data[e_id]
395
- edge[1]
396
- else
397
- 0.0
398
- end
399
- end
400
-
401
- edges = edges.last(10) unless options[:full_report]
402
- # method[:edges] = method[:edges].first(10) unless options[:full_report]
403
-
404
- edges.reverse_each do |e_id|
405
- c_m_id, c_total, c_called, c_tn, c_sub_nodes = data[e_id]
406
-
407
- grandchildren = c_sub_nodes.inject(0) { |a,s| a + data[s][1] }
408
- grandchildren = 0 if grandchildren < 0
409
-
410
- self_total = c_total - grandchildren
411
- out.printf secondary, sec(self_total),
412
- sec(grandchildren),
413
- c_called,
414
- methods[c_m_id][:name],
415
- graph_method_index(shown_indexes[e_id])
416
- end
417
-
418
- out.puts "-------------------------------------------------------"
419
- end
420
-
421
- epilogue out, data.size, total_calls
422
- end
423
-
424
- def graph_method_index(index)
425
- index ? " [#{index}]" : ""
426
- end
427
-
428
- HEADER_INDEX = {
429
- :percent => 0,
430
- :total_seconds => 1,
431
- :self_seconds => 2,
432
- :calls => 3,
433
- :self_ms => 4,
434
- :total_ms => 5,
435
- :name => 6
436
- }
437
-
438
- def sort_order
439
- # call to_i so if unrecognized symbol is passed, column will be percent
440
- Array(@options[:sort]).map { |header| HEADER_INDEX[header].to_i }
441
- end
442
- end
443
3
  end
444
4
  end