ruby-prof 0.15.9 → 0.16.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.
- checksums.yaml +4 -4
- data/CHANGES +27 -1
- data/README.rdoc +83 -31
- data/bin/ruby-prof +4 -4
- data/doc/LICENSE.html +1 -1
- data/doc/README_rdoc.html +92 -33
- data/doc/Rack.html +1 -1
- data/doc/Rack/RubyProf.html +17 -14
- data/doc/RubyProf.html +30 -29
- data/doc/RubyProf/AbstractPrinter.html +1 -1
- data/doc/RubyProf/AggregateCallInfo.html +1 -1
- data/doc/RubyProf/CallInfo.html +1 -1
- data/doc/RubyProf/CallInfoPrinter.html +1 -1
- data/doc/RubyProf/CallInfoVisitor.html +1 -1
- data/doc/RubyProf/CallStackPrinter.html +1 -1
- data/doc/RubyProf/CallTreePrinter.html +349 -67
- data/doc/RubyProf/Cmd.html +5 -5
- data/doc/RubyProf/DotPrinter.html +2 -2
- data/doc/RubyProf/FlatPrinter.html +1 -1
- data/doc/RubyProf/FlatPrinterWithLineNumbers.html +1 -1
- data/doc/RubyProf/GraphHtmlPrinter.html +1 -1
- data/doc/RubyProf/GraphPrinter.html +1 -1
- data/doc/RubyProf/MethodInfo.html +2 -2
- data/doc/RubyProf/MultiPrinter.html +11 -9
- data/doc/RubyProf/Profile.html +94 -44
- data/doc/RubyProf/ProfileTask.html +1 -1
- data/doc/RubyProf/Thread.html +43 -1
- data/doc/created.rid +16 -16
- data/doc/examples/flat_txt.html +1 -1
- data/doc/examples/graph_html.html +1 -1
- data/doc/examples/graph_txt.html +3 -3
- data/doc/index.html +85 -30
- data/doc/js/navigation.js.gz +0 -0
- data/doc/js/search_index.js +1 -1
- data/doc/js/search_index.js.gz +0 -0
- data/doc/js/searcher.js +2 -2
- data/doc/js/searcher.js.gz +0 -0
- data/doc/table_of_contents.html +117 -68
- data/examples/cachegrind.out.1 +114 -0
- data/examples/cachegrind.out.1.32313213 +114 -0
- data/examples/graph.txt +1 -1
- data/ext/ruby_prof/extconf.rb +6 -2
- data/ext/ruby_prof/rp_measure_cpu_time.c +29 -31
- data/ext/ruby_prof/rp_method.c +1 -1
- data/ext/ruby_prof/rp_thread.c +57 -52
- data/ext/ruby_prof/ruby_prof.c +122 -66
- data/ext/ruby_prof/ruby_prof.h +2 -0
- data/lib/ruby-prof.rb +14 -13
- data/lib/ruby-prof/assets/call_stack_printer.js.html +1 -1
- data/lib/ruby-prof/compatibility.rb +9 -8
- data/lib/ruby-prof/method_info.rb +1 -1
- data/lib/ruby-prof/printers/call_tree_printer.rb +88 -50
- data/lib/ruby-prof/printers/dot_printer.rb +1 -1
- data/lib/ruby-prof/printers/multi_printer.rb +6 -4
- data/lib/ruby-prof/profile.rb +0 -1
- data/lib/ruby-prof/rack.rb +53 -16
- data/lib/ruby-prof/thread.rb +11 -0
- data/lib/ruby-prof/version.rb +1 -1
- data/test/exclude_threads_test.rb +2 -3
- data/test/fiber_test.rb +21 -7
- data/test/measure_cpu_time_test.rb +84 -24
- data/test/multi_printer_test.rb +5 -4
- data/test/pause_resume_test.rb +7 -7
- data/test/printers_test.rb +6 -4
- data/test/rack_test.rb +26 -1
- data/test/test_helper.rb +28 -3
- data/test/thread_test.rb +1 -0
- metadata +5 -3
data/ext/ruby_prof/ruby_prof.c
CHANGED
@@ -107,19 +107,21 @@ get_method(rb_event_flag_t event, VALUE klass, ID mid, thread_data_t* thread_dat
|
|
107
107
|
static int
|
108
108
|
pop_frames(st_data_t key, st_data_t value, st_data_t data)
|
109
109
|
{
|
110
|
-
VALUE fiber_id = (VALUE)key;
|
111
110
|
thread_data_t* thread_data = (thread_data_t *) value;
|
112
111
|
prof_profile_t* profile = (prof_profile_t*) data;
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
112
|
+
VALUE thread_id = thread_data->thread_id;
|
113
|
+
VALUE fiber_id = thread_data->fiber_id;
|
114
|
+
double measurement = profile->measurer->measure();
|
115
|
+
|
116
|
+
if (!profile->last_thread_data
|
117
|
+
|| (!profile->merge_fibers && profile->last_thread_data->fiber_id != fiber_id)
|
118
|
+
|| profile->last_thread_data->thread_id != thread_id
|
119
|
+
)
|
120
|
+
thread_data = switch_thread(profile, thread_id, fiber_id);
|
117
121
|
else
|
118
|
-
|
122
|
+
thread_data = profile->last_thread_data;
|
119
123
|
|
120
|
-
while (prof_stack_pop(thread_data->stack, measurement))
|
121
|
-
{
|
122
|
-
}
|
124
|
+
while (prof_stack_pop(thread_data->stack, measurement));
|
123
125
|
|
124
126
|
return ST_CONTINUE;
|
125
127
|
}
|
@@ -188,7 +190,7 @@ prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE kla
|
|
188
190
|
module or cProfile class since they clutter
|
189
191
|
the results but aren't important to them results. */
|
190
192
|
if (self == mProf || klass == cProfile)
|
191
|
-
|
193
|
+
return;
|
192
194
|
|
193
195
|
if (trace_file != NULL)
|
194
196
|
{
|
@@ -201,16 +203,31 @@ prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE kla
|
|
201
203
|
fiber = rb_fiber_current();
|
202
204
|
fiber_id = rb_obj_id(fiber);
|
203
205
|
|
204
|
-
if
|
206
|
+
/* Don't measure anything if the include_threads option has been specified
|
207
|
+
and the current thread is not in the list
|
208
|
+
*/
|
209
|
+
if (profile->include_threads_tbl && !st_lookup(profile->include_threads_tbl, (st_data_t) thread_id, 0))
|
210
|
+
{
|
211
|
+
return;
|
212
|
+
}
|
213
|
+
|
214
|
+
/* Don't measure anything if the current thread is in the excluded thread table
|
215
|
+
*/
|
216
|
+
if (profile->exclude_threads_tbl && st_lookup(profile->exclude_threads_tbl, (st_data_t) thread_id, 0))
|
205
217
|
{
|
206
|
-
|
218
|
+
return;
|
207
219
|
}
|
208
220
|
|
209
|
-
/*
|
210
|
-
|
211
|
-
|
221
|
+
/* We need to switch the profiling context if we either had none before,
|
222
|
+
we don't merge fibers and the fiber ids differ, or the thread ids differ.
|
223
|
+
*/
|
224
|
+
if (!profile->last_thread_data
|
225
|
+
|| (!profile->merge_fibers && profile->last_thread_data->fiber_id != fiber_id)
|
226
|
+
|| profile->last_thread_data->thread_id != thread_id
|
227
|
+
)
|
228
|
+
thread_data = switch_thread(profile, thread_id, fiber_id);
|
212
229
|
else
|
213
|
-
|
230
|
+
thread_data = profile->last_thread_data;
|
214
231
|
|
215
232
|
/* Get the current frame for the current thread. */
|
216
233
|
frame = prof_stack_peek(thread_data->stack);
|
@@ -301,7 +318,7 @@ collect_threads(st_data_t key, st_data_t value, st_data_t result)
|
|
301
318
|
{
|
302
319
|
thread_data_t* thread_data = (thread_data_t*) value;
|
303
320
|
VALUE threads_array = (VALUE) result;
|
304
|
-
|
321
|
+
rb_ary_push(threads_array, prof_thread_wrap(thread_data));
|
305
322
|
return ST_CONTINUE;
|
306
323
|
}
|
307
324
|
|
@@ -317,7 +334,7 @@ mark_threads(st_data_t key, st_data_t value, st_data_t result)
|
|
317
334
|
static void
|
318
335
|
prof_mark(prof_profile_t *profile)
|
319
336
|
{
|
320
|
-
|
337
|
+
st_foreach(profile->threads_tbl, mark_threads, 0);
|
321
338
|
}
|
322
339
|
|
323
340
|
/* Freeing the profile creates a cascade of freeing.
|
@@ -326,16 +343,23 @@ prof_mark(prof_profile_t *profile)
|
|
326
343
|
static void
|
327
344
|
prof_free(prof_profile_t *profile)
|
328
345
|
{
|
329
|
-
|
346
|
+
profile->last_thread_data = NULL;
|
330
347
|
|
331
|
-
|
348
|
+
threads_table_free(profile->threads_tbl);
|
332
349
|
profile->threads_tbl = NULL;
|
333
350
|
|
334
|
-
|
335
|
-
|
351
|
+
if (profile->exclude_threads_tbl) {
|
352
|
+
st_free_table(profile->exclude_threads_tbl);
|
353
|
+
profile->exclude_threads_tbl = NULL;
|
354
|
+
}
|
355
|
+
|
356
|
+
if (profile->include_threads_tbl) {
|
357
|
+
st_free_table(profile->include_threads_tbl);
|
358
|
+
profile->include_threads_tbl = NULL;
|
359
|
+
}
|
336
360
|
|
337
|
-
|
338
|
-
|
361
|
+
xfree(profile->measurer);
|
362
|
+
profile->measurer = NULL;
|
339
363
|
|
340
364
|
xfree(profile);
|
341
365
|
}
|
@@ -347,58 +371,86 @@ prof_allocate(VALUE klass)
|
|
347
371
|
prof_profile_t* profile;
|
348
372
|
result = Data_Make_Struct(klass, prof_profile_t, prof_mark, prof_free, profile);
|
349
373
|
profile->threads_tbl = threads_table_create();
|
350
|
-
|
374
|
+
profile->exclude_threads_tbl = NULL;
|
375
|
+
profile->include_threads_tbl = NULL;
|
351
376
|
profile->running = Qfalse;
|
377
|
+
profile->merge_fibers = 0;
|
352
378
|
return result;
|
353
379
|
}
|
354
380
|
|
355
381
|
/* call-seq:
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
exclude_threads:: Threads to exclude from the profiling results
|
382
|
+
new()
|
383
|
+
new(options)
|
384
|
+
|
385
|
+
Returns a new profiler. Possible options for the options hash are:
|
386
|
+
|
387
|
+
measure_mode:: Measure mode. Specifies the profile measure mode.
|
388
|
+
If not specified, defaults to RubyProf::WALL_TIME.
|
389
|
+
exclude_threads:: Threads to exclude from the profiling results.
|
390
|
+
include_threads:: Focus profiling on only the given threads. This will ignore
|
391
|
+
all other threads.
|
392
|
+
merge_fibers:: Whether to merge all fibers under a given thread. This should be
|
393
|
+
used when profiling for a callgrind printer.
|
394
|
+
*/
|
364
395
|
static VALUE
|
365
396
|
prof_initialize(int argc, VALUE *argv, VALUE self)
|
366
397
|
{
|
367
398
|
prof_profile_t* profile = prof_get_profile(self);
|
368
|
-
VALUE
|
369
|
-
|
370
|
-
VALUE exclude_threads;
|
399
|
+
VALUE mode_or_options;
|
400
|
+
VALUE mode = Qnil;
|
401
|
+
VALUE exclude_threads = Qnil;
|
402
|
+
VALUE include_threads = Qnil;
|
403
|
+
VALUE merge_fibers = Qnil;
|
371
404
|
int i;
|
372
|
-
|
373
|
-
switch (rb_scan_args(argc, argv, "02", &
|
374
|
-
|
375
|
-
case 0:
|
376
|
-
{
|
377
|
-
measurer = MEASURE_WALL_TIME;
|
378
|
-
exclude_threads = rb_ary_new();
|
405
|
+
|
406
|
+
switch (rb_scan_args(argc, argv, "02", &mode_or_options, &exclude_threads)) {
|
407
|
+
case 0:
|
379
408
|
break;
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
409
|
+
case 1:
|
410
|
+
if (FIXNUM_P(mode_or_options)) {
|
411
|
+
mode = mode_or_options;
|
412
|
+
}
|
413
|
+
else {
|
414
|
+
Check_Type(mode_or_options, T_HASH);
|
415
|
+
mode = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("measure_mode")));
|
416
|
+
merge_fibers = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("merge_fibers")));
|
417
|
+
exclude_threads = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("exclude_threads")));
|
418
|
+
include_threads = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("include_threads")));
|
419
|
+
}
|
385
420
|
break;
|
386
|
-
|
387
|
-
case 2:
|
388
|
-
{
|
421
|
+
case 2:
|
389
422
|
Check_Type(exclude_threads, T_ARRAY);
|
390
|
-
measurer = (prof_measure_mode_t)NUM2INT(mode);
|
391
423
|
break;
|
392
|
-
}
|
393
424
|
}
|
394
425
|
|
395
|
-
|
426
|
+
if (mode == Qnil) {
|
427
|
+
mode = INT2NUM(MEASURE_WALL_TIME);
|
428
|
+
} else {
|
429
|
+
Check_Type(mode, T_FIXNUM);
|
430
|
+
}
|
431
|
+
profile->measurer = prof_get_measurer(NUM2INT(mode));
|
432
|
+
profile->merge_fibers = merge_fibers != Qnil && merge_fibers != Qfalse;
|
396
433
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
434
|
+
if (exclude_threads != Qnil) {
|
435
|
+
Check_Type(exclude_threads, T_ARRAY);
|
436
|
+
assert(profile->exclude_threads_tbl == NULL);
|
437
|
+
profile->exclude_threads_tbl = threads_table_create();
|
438
|
+
for (i = 0; i < RARRAY_LEN(exclude_threads); i++) {
|
439
|
+
VALUE thread = rb_ary_entry(exclude_threads, i);
|
440
|
+
VALUE thread_id = rb_obj_id(thread);
|
441
|
+
st_insert(profile->exclude_threads_tbl, thread_id, Qtrue);
|
442
|
+
}
|
443
|
+
}
|
444
|
+
|
445
|
+
if (include_threads != Qnil) {
|
446
|
+
Check_Type(include_threads, T_ARRAY);
|
447
|
+
assert(profile->include_threads_tbl == NULL);
|
448
|
+
profile->include_threads_tbl = threads_table_create();
|
449
|
+
for (i = 0; i < RARRAY_LEN(include_threads); i++) {
|
450
|
+
VALUE thread = rb_ary_entry(include_threads, i);
|
451
|
+
VALUE thread_id = rb_obj_id(thread);
|
452
|
+
st_insert(profile->include_threads_tbl, thread_id, Qtrue);
|
453
|
+
}
|
402
454
|
}
|
403
455
|
|
404
456
|
return self;
|
@@ -427,7 +479,7 @@ prof_running(VALUE self)
|
|
427
479
|
}
|
428
480
|
|
429
481
|
/* call-seq:
|
430
|
-
start ->
|
482
|
+
start -> self
|
431
483
|
|
432
484
|
Starts recording profile data.*/
|
433
485
|
static VALUE
|
@@ -470,7 +522,7 @@ prof_start(VALUE self)
|
|
470
522
|
}
|
471
523
|
|
472
524
|
/* call-seq:
|
473
|
-
pause ->
|
525
|
+
pause -> self
|
474
526
|
|
475
527
|
Pauses collecting profile data. */
|
476
528
|
static VALUE
|
@@ -493,7 +545,8 @@ prof_pause(VALUE self)
|
|
493
545
|
}
|
494
546
|
|
495
547
|
/* call-seq:
|
496
|
-
resume
|
548
|
+
resume -> self
|
549
|
+
resume(&block) -> self
|
497
550
|
|
498
551
|
Resumes recording profile data.*/
|
499
552
|
static VALUE
|
@@ -559,9 +612,12 @@ prof_stop(VALUE self)
|
|
559
612
|
}
|
560
613
|
|
561
614
|
/* call-seq:
|
562
|
-
profile
|
615
|
+
profile(&block) -> self
|
616
|
+
profile(options, &block) -> self
|
563
617
|
|
564
|
-
Profiles the specified block and returns a RubyProf::
|
618
|
+
Profiles the specified block and returns a RubyProf::Profile
|
619
|
+
object. Arguments are passed to Profile initialize method.
|
620
|
+
*/
|
565
621
|
static VALUE
|
566
622
|
prof_profile(int argc, VALUE *argv, VALUE klass)
|
567
623
|
{
|
@@ -579,14 +635,14 @@ prof_profile(int argc, VALUE *argv, VALUE klass)
|
|
579
635
|
}
|
580
636
|
|
581
637
|
/* call-seq:
|
582
|
-
threads ->
|
638
|
+
threads -> array of RubyProf::Thread
|
583
639
|
|
584
640
|
Returns an array of RubyProf::Thread instances that were executed
|
585
641
|
while the the program was being run. */
|
586
642
|
static VALUE
|
587
643
|
prof_threads(VALUE self)
|
588
644
|
{
|
589
|
-
|
645
|
+
VALUE result = rb_ary_new();
|
590
646
|
prof_profile_t* profile = prof_get_profile(self);
|
591
647
|
st_foreach(profile->threads_tbl, collect_threads, result);
|
592
648
|
return result;
|
data/ext/ruby_prof/ruby_prof.h
CHANGED
data/lib/ruby-prof.rb
CHANGED
@@ -9,27 +9,28 @@ rescue LoadError
|
|
9
9
|
end
|
10
10
|
|
11
11
|
require 'ruby-prof/version'
|
12
|
-
require 'ruby-prof/aggregate_call_info'
|
13
12
|
require 'ruby-prof/call_info'
|
14
|
-
require 'ruby-prof/call_info_visitor'
|
15
13
|
require 'ruby-prof/compatibility'
|
16
14
|
require 'ruby-prof/method_info'
|
17
15
|
require 'ruby-prof/profile'
|
18
16
|
require 'ruby-prof/rack'
|
19
17
|
require 'ruby-prof/thread'
|
20
18
|
|
21
|
-
require 'ruby-prof/printers/abstract_printer'
|
22
|
-
require 'ruby-prof/printers/call_info_printer'
|
23
|
-
require 'ruby-prof/printers/call_stack_printer'
|
24
|
-
require 'ruby-prof/printers/call_tree_printer'
|
25
|
-
require 'ruby-prof/printers/dot_printer'
|
26
|
-
require 'ruby-prof/printers/flat_printer'
|
27
|
-
require 'ruby-prof/printers/flat_printer_with_line_numbers'
|
28
|
-
require 'ruby-prof/printers/graph_html_printer'
|
29
|
-
require 'ruby-prof/printers/graph_printer'
|
30
|
-
require 'ruby-prof/printers/multi_printer'
|
31
|
-
|
32
19
|
module RubyProf
|
20
|
+
autoload :AggregateCallInfo, 'ruby-prof/aggregate_call_info'
|
21
|
+
autoload :CallInfoVisitor, 'ruby-prof/call_info_visitor'
|
22
|
+
|
23
|
+
autoload :AbstractPrinter, 'ruby-prof/printers/abstract_printer'
|
24
|
+
autoload :CallInfoPrinter, 'ruby-prof/printers/call_info_printer'
|
25
|
+
autoload :CallStackPrinter, 'ruby-prof/printers/call_stack_printer'
|
26
|
+
autoload :CallTreePrinter, 'ruby-prof/printers/call_tree_printer'
|
27
|
+
autoload :DotPrinter, 'ruby-prof/printers/dot_printer'
|
28
|
+
autoload :FlatPrinter, 'ruby-prof/printers/flat_printer'
|
29
|
+
autoload :FlatPrinterWithLineNumbers, 'ruby-prof/printers/flat_printer_with_line_numbers'
|
30
|
+
autoload :GraphHtmlPrinter, 'ruby-prof/printers/graph_html_printer'
|
31
|
+
autoload :GraphPrinter, 'ruby-prof/printers/graph_printer'
|
32
|
+
autoload :MultiPrinter, 'ruby-prof/printers/multi_printer'
|
33
|
+
|
33
34
|
# Checks if the user specified the clock mode via
|
34
35
|
# the RUBY_PROF_MEASURE_MODE environment variable
|
35
36
|
def self.figure_measure_mode
|
@@ -40,8 +40,8 @@ module RubyProf
|
|
40
40
|
#
|
41
41
|
# Returns what ruby-prof is measuring. Valid values include:
|
42
42
|
#
|
43
|
-
# *RubyProf::
|
44
|
-
# *RubyProf::
|
43
|
+
# *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows. This is default.
|
44
|
+
# *RubyProf::PROCESS_TIME - Measure process time. It is implemented using the clock functions in the C Runtime library.
|
45
45
|
# *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms.
|
46
46
|
# *RubyProf::ALLOCATIONS - Measure object allocations. This requires a patched Ruby interpreter.
|
47
47
|
# *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.
|
@@ -57,8 +57,8 @@ module RubyProf
|
|
57
57
|
#
|
58
58
|
# Specifies what ruby-prof should measure. Valid values include:
|
59
59
|
#
|
60
|
-
# *RubyProf::
|
61
|
-
# *RubyProf::
|
60
|
+
# *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows. This is default.
|
61
|
+
# *RubyProf::PROCESS_TIME - Measure process time. It is implemented using the clock functions in the C Runtime library.
|
62
62
|
# *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms.
|
63
63
|
# *RubyProf::ALLOCATIONS - Measure object allocations. This requires a patched Ruby interpreter.
|
64
64
|
# *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.
|
@@ -106,7 +106,7 @@ module RubyProf
|
|
106
106
|
|
107
107
|
def self.start
|
108
108
|
ensure_not_running!
|
109
|
-
@profile = Profile.new(
|
109
|
+
@profile = Profile.new(measure_mode: measure_mode, exclude_threads: exclude_threads)
|
110
110
|
enable_gc_stats_if_needed
|
111
111
|
@profile.start
|
112
112
|
end
|
@@ -140,12 +140,13 @@ module RubyProf
|
|
140
140
|
end
|
141
141
|
|
142
142
|
# Profile a block
|
143
|
-
def self.profile(&block)
|
143
|
+
def self.profile(options = {}, &block)
|
144
144
|
ensure_not_running!
|
145
145
|
gc_stat_was_enabled = enable_gc_stats_if_needed
|
146
|
-
|
146
|
+
options = { measure_mode: measure_mode, exclude_threads: exclude_threads }.merge!(options)
|
147
|
+
result = Profile.profile(options, &block)
|
147
148
|
disable_gc_stats_if_needed(gc_stat_was_enabled)
|
148
|
-
|
149
|
+
result
|
149
150
|
end
|
150
151
|
|
151
152
|
|
@@ -115,7 +115,7 @@ module RubyProf
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def to_s
|
118
|
-
"#{self.full_name} (c: #{self.called}, tt: #{self.total_time}, st: #{self.self_time}, ct: #{self.children_time})"
|
118
|
+
"#{self.full_name} (c: #{self.called}, tt: #{self.total_time}, st: #{self.self_time}, wt: #{wait_time}, ct: #{self.children_time})"
|
119
119
|
end
|
120
120
|
|
121
121
|
# remove method from the call graph. should not be called directly.
|
@@ -1,59 +1,58 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require 'fiber'
|
4
|
+
require 'thread'
|
5
|
+
require 'fileutils'
|
6
|
+
|
3
7
|
module RubyProf
|
4
|
-
# Generate profiling information in
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# options - Hash table
|
11
|
-
# :min_percent - Number 0 to 100 that specifes the minimum
|
12
|
-
# %self (the methods self time divided by the
|
13
|
-
# overall total time) that a method must take
|
14
|
-
# for it to be printed out in the report.
|
15
|
-
# Default value is 0.
|
16
|
-
#
|
17
|
-
# :print_file - True or false. Specifies if a method's source
|
18
|
-
# file should be printed. Default value if false.
|
19
|
-
#
|
20
|
-
def print(output = STDOUT, options = {})
|
21
|
-
@output = output
|
22
|
-
setup_options(options)
|
8
|
+
# Generate profiling information in callgrind format for use by
|
9
|
+
# kcachegrind and similar tools.
|
10
|
+
#
|
11
|
+
# Note: when profiling for a callgrind printer, one should use the
|
12
|
+
# merge_fibers: true option when creating the profile. Otherwise
|
13
|
+
# each fiber would appear as a separate profile.
|
23
14
|
|
24
|
-
|
25
|
-
|
15
|
+
class CallTreePrinter < AbstractPrinter
|
16
|
+
|
17
|
+
def determine_event_specification_and_value_scale
|
18
|
+
@event_specification = "events: "
|
26
19
|
case RubyProf.measure_mode
|
27
20
|
when RubyProf::PROCESS_TIME
|
28
|
-
@value_scale = RubyProf::CLOCKS_PER_SEC
|
29
|
-
@
|
21
|
+
@value_scale = RubyProf::CLOCKS_PER_SEC
|
22
|
+
@event_specification << 'process_time'
|
30
23
|
when RubyProf::WALL_TIME
|
31
24
|
@value_scale = 1_000_000
|
32
|
-
@
|
25
|
+
@event_specification << 'wall_time'
|
33
26
|
when RubyProf.const_defined?(:CPU_TIME) && RubyProf::CPU_TIME
|
34
27
|
@value_scale = RubyProf.cpu_frequency
|
35
|
-
@
|
28
|
+
@event_specification << 'cpu_time'
|
36
29
|
when RubyProf.const_defined?(:ALLOCATIONS) && RubyProf::ALLOCATIONS
|
37
30
|
@value_scale = 1
|
38
|
-
@
|
31
|
+
@event_specification << 'allocations'
|
39
32
|
when RubyProf.const_defined?(:MEMORY) && RubyProf::MEMORY
|
40
33
|
@value_scale = 1
|
41
|
-
@
|
34
|
+
@event_specification << 'memory'
|
42
35
|
when RubyProf.const_defined?(:GC_RUNS) && RubyProf::GC_RUNS
|
43
36
|
@value_scale = 1
|
44
|
-
@
|
37
|
+
@event_specification << 'gc_runs'
|
45
38
|
when RubyProf.const_defined?(:GC_TIME) && RubyProf::GC_TIME
|
46
39
|
@value_scale = 1000000
|
47
|
-
@
|
40
|
+
@event_specification << 'gc_time'
|
48
41
|
else
|
49
42
|
raise "Unknown measure mode: #{RubyProf.measure_mode}"
|
50
43
|
end
|
51
|
-
|
44
|
+
end
|
52
45
|
|
46
|
+
def print(options = {})
|
47
|
+
setup_options(options)
|
48
|
+
determine_event_specification_and_value_scale
|
53
49
|
print_threads
|
54
50
|
end
|
55
51
|
|
56
52
|
def print_threads
|
53
|
+
remove_subsidiary_files_from_previous_profile_runs
|
54
|
+
# TODO: merge fibers of a given thread here, instead of relying
|
55
|
+
# on the profiler to merge fibers.
|
57
56
|
@result.threads.each do |thread|
|
58
57
|
print_thread(thread)
|
59
58
|
end
|
@@ -68,25 +67,64 @@ module RubyProf
|
|
68
67
|
end
|
69
68
|
|
70
69
|
def print_thread(thread)
|
71
|
-
thread
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
# Now print out the function line number and its self time
|
77
|
-
@output << "#{method.line} #{convert(method.self_time)}\n"
|
78
|
-
|
79
|
-
# Now print out all the children methods
|
80
|
-
method.children.each do |callee|
|
81
|
-
@output << "cfl=#{file(callee.target)}\n"
|
82
|
-
@output << "cfn=#{method_name(callee.target)}\n"
|
83
|
-
@output << "calls=#{callee.called} #{callee.line}\n"
|
84
|
-
|
85
|
-
# Print out total times here!
|
86
|
-
@output << "#{callee.line} #{convert(callee.total_time)}\n"
|
70
|
+
File.open(file_path_for_thread(thread), "w") do |f|
|
71
|
+
print_headers(f, thread)
|
72
|
+
thread.methods.reverse_each do |method|
|
73
|
+
print_method(f, method)
|
87
74
|
end
|
88
|
-
@output << "\n"
|
89
75
|
end
|
90
|
-
end
|
91
|
-
|
92
|
-
|
76
|
+
end
|
77
|
+
|
78
|
+
def path
|
79
|
+
@options[:path] || "."
|
80
|
+
end
|
81
|
+
|
82
|
+
def base_name
|
83
|
+
@options[:profile] || "profile"
|
84
|
+
end
|
85
|
+
|
86
|
+
def remove_subsidiary_files_from_previous_profile_runs
|
87
|
+
pattern = [base_name, "callgrind.out", $$, "*"].join(".")
|
88
|
+
files = Dir.glob(File.join(path, pattern))
|
89
|
+
FileUtils.rm_f(files)
|
90
|
+
end
|
91
|
+
|
92
|
+
def file_name_for_thread(thread)
|
93
|
+
if thread.fiber_id == Fiber.current.object_id
|
94
|
+
[base_name, "callgrind.out", $$].join(".")
|
95
|
+
else
|
96
|
+
[base_name, "callgrind.out", $$, thread.fiber_id].join(".")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def file_path_for_thread(thread)
|
101
|
+
File.join(path, file_name_for_thread(thread))
|
102
|
+
end
|
103
|
+
|
104
|
+
def print_headers(output, thread)
|
105
|
+
output << "#{@event_specification}\n\n"
|
106
|
+
# this doesn't work. kcachegrind does not fully support the spec.
|
107
|
+
# output << "thread: #{thread.id}\n\n"
|
108
|
+
end
|
109
|
+
|
110
|
+
def print_method(output, method)
|
111
|
+
# Print out the file and method name
|
112
|
+
output << "fl=#{file(method)}\n"
|
113
|
+
output << "fn=#{method_name(method)}\n"
|
114
|
+
|
115
|
+
# Now print out the function line number and its self time
|
116
|
+
output << "#{method.line} #{convert(method.self_time)}\n"
|
117
|
+
|
118
|
+
# Now print out all the children methods
|
119
|
+
method.children.each do |callee|
|
120
|
+
output << "cfl=#{file(callee.target)}\n"
|
121
|
+
output << "cfn=#{method_name(callee.target)}\n"
|
122
|
+
output << "calls=#{callee.called} #{callee.line}\n"
|
123
|
+
|
124
|
+
# Print out total times here!
|
125
|
+
output << "#{callee.line} #{convert(callee.total_time)}\n"
|
126
|
+
end
|
127
|
+
output << "\n"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|