ruby-prof 1.4.5-x64-mingw-ucrt → 1.5.0-x64-mingw-ucrt
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES +6 -0
- data/Rakefile +1 -1
- data/ext/ruby_prof/rp_allocation.c +4 -4
- data/ext/ruby_prof/rp_call_tree.c +126 -36
- data/ext/ruby_prof/rp_call_tree.h +2 -1
- data/ext/ruby_prof/rp_call_trees.c +15 -7
- data/ext/ruby_prof/rp_measurement.c +139 -19
- data/ext/ruby_prof/rp_measurement.h +3 -0
- data/ext/ruby_prof/rp_method.c +58 -6
- data/ext/ruby_prof/rp_method.h +1 -1
- data/ext/ruby_prof/rp_profile.c +36 -3
- data/ext/ruby_prof/rp_thread.c +61 -13
- data/ext/ruby_prof/rp_thread.h +1 -1
- data/ext/ruby_prof/ruby_prof.c +0 -2
- data/ext/ruby_prof/ruby_prof.h +8 -0
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +4 -6
- data/lib/3.1/ruby_prof.so +0 -0
- data/lib/3.2/ruby_prof.so +0 -0
- data/lib/ruby-prof/method_info.rb +8 -1
- data/lib/ruby-prof/printers/call_tree_printer.rb +0 -2
- data/lib/ruby-prof/profile.rb +34 -1
- data/lib/ruby-prof/version.rb +1 -1
- data/test/call_tree_builder.rb +126 -0
- data/test/call_tree_test.rb +197 -0
- data/test/call_trees_test.rb +4 -4
- data/test/fiber_test.rb +123 -1
- data/test/gc_test.rb +9 -7
- data/test/line_number_test.rb +36 -60
- data/test/measurement_test.rb +82 -0
- data/test/method_info_test.rb +95 -0
- data/test/profile_test.rb +85 -0
- data/test/recursive_test.rb +1 -1
- data/test/scheduler.rb +354 -0
- data/test/thread_test.rb +28 -2
- metadata +9 -6
- data/ext/ruby_prof/rp_aggregate_call_tree.c +0 -59
- data/ext/ruby_prof/rp_aggregate_call_tree.h +0 -13
data/ext/ruby_prof/rp_method.c
CHANGED
@@ -40,7 +40,7 @@ VALUE resolve_klass(VALUE klass, unsigned int* klass_flags)
|
|
40
40
|
*klass_flags |= kObjectSingleton;
|
41
41
|
result = rb_class_superclass(klass);
|
42
42
|
}
|
43
|
-
/* Ok, this could be other things like an array
|
43
|
+
/* Ok, this could be other things like an array put onto
|
44
44
|
a singleton object (yeah, it happens, see the singleton
|
45
45
|
objects test case). */
|
46
46
|
else
|
@@ -97,7 +97,12 @@ st_data_t method_key(VALUE klass, VALUE msym)
|
|
97
97
|
resolved_klass = RBASIC(klass)->klass;
|
98
98
|
}
|
99
99
|
|
100
|
-
|
100
|
+
st_data_t hash = rb_hash_start(0);
|
101
|
+
hash = rb_hash_uint(hash, resolved_klass);
|
102
|
+
hash = rb_hash_uint(hash, msym);
|
103
|
+
hash = rb_hash_end(hash);
|
104
|
+
|
105
|
+
return hash;
|
101
106
|
}
|
102
107
|
|
103
108
|
/* ====== Allocation Table ====== */
|
@@ -309,6 +314,45 @@ created. RubyProf::MethodInfo objects can be accessed via
|
|
309
314
|
the RubyProf::Profile object.
|
310
315
|
*/
|
311
316
|
|
317
|
+
/* call-seq:
|
318
|
+
new(klass, method_name) -> method_info
|
319
|
+
|
320
|
+
Creates a new MethodInfo instance. +Klass+ should be a reference to
|
321
|
+
a Ruby class and +method_name+ a symbol identifying one of its instance methods.*/
|
322
|
+
static VALUE prof_method_initialize(VALUE self, VALUE klass, VALUE method_name)
|
323
|
+
{
|
324
|
+
prof_method_t* method_ptr = prof_get_method(self);
|
325
|
+
method_ptr->klass = klass;
|
326
|
+
method_ptr->method_name = method_name;
|
327
|
+
|
328
|
+
// Setup method key
|
329
|
+
method_ptr->key = method_key(klass, method_name);
|
330
|
+
|
331
|
+
// Get method object
|
332
|
+
VALUE ruby_method = rb_funcall(klass, rb_intern("instance_method"), 1, method_name);
|
333
|
+
|
334
|
+
// Get source file and line number
|
335
|
+
VALUE location_array = rb_funcall(ruby_method, rb_intern("source_location"), 0);
|
336
|
+
if (location_array != Qnil && RARRAY_LEN(location_array) == 2)
|
337
|
+
{
|
338
|
+
method_ptr->source_file = rb_ary_entry(location_array, 0);
|
339
|
+
method_ptr->source_line = NUM2INT(rb_ary_entry(location_array, 1));
|
340
|
+
}
|
341
|
+
|
342
|
+
return self;
|
343
|
+
}
|
344
|
+
|
345
|
+
/* call-seq:
|
346
|
+
hash -> hash
|
347
|
+
|
348
|
+
Returns the hash key for this method info. The hash key is calculated based on the
|
349
|
+
klass name and method name */
|
350
|
+
static VALUE prof_method_hash(VALUE self)
|
351
|
+
{
|
352
|
+
prof_method_t* method_ptr = prof_get_method(self);
|
353
|
+
return ULL2NUM(method_ptr->key);
|
354
|
+
}
|
355
|
+
|
312
356
|
/* call-seq:
|
313
357
|
allocations -> array
|
314
358
|
|
@@ -334,7 +378,7 @@ static VALUE prof_method_measurement(VALUE self)
|
|
334
378
|
/* call-seq:
|
335
379
|
source_file => string
|
336
380
|
|
337
|
-
|
381
|
+
Returns the source file of the method
|
338
382
|
*/
|
339
383
|
static VALUE prof_method_source_file(VALUE self)
|
340
384
|
{
|
@@ -420,7 +464,7 @@ static VALUE prof_method_dump(VALUE self)
|
|
420
464
|
rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(method_data->klass_flags));
|
421
465
|
rb_hash_aset(result, ID2SYM(rb_intern("method_name")), method_data->method_name);
|
422
466
|
|
423
|
-
rb_hash_aset(result, ID2SYM(rb_intern("key")),
|
467
|
+
rb_hash_aset(result, ID2SYM(rb_intern("key")), ULL2NUM(method_data->key));
|
424
468
|
rb_hash_aset(result, ID2SYM(rb_intern("recursive")), prof_method_recursive(self));
|
425
469
|
rb_hash_aset(result, ID2SYM(rb_intern("source_file")), method_data->source_file);
|
426
470
|
rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(method_data->source_line));
|
@@ -441,7 +485,7 @@ static VALUE prof_method_load(VALUE self, VALUE data)
|
|
441
485
|
method_data->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
|
442
486
|
method_data->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
|
443
487
|
method_data->method_name = rb_hash_aref(data, ID2SYM(rb_intern("method_name")));
|
444
|
-
method_data->key =
|
488
|
+
method_data->key = RB_NUM2ULL(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
|
445
489
|
|
446
490
|
method_data->recursive = rb_hash_aref(data, ID2SYM(rb_intern("recursive"))) == Qtrue ? true : false;
|
447
491
|
|
@@ -469,8 +513,16 @@ void rp_init_method_info()
|
|
469
513
|
{
|
470
514
|
/* MethodInfo */
|
471
515
|
cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
|
472
|
-
|
516
|
+
|
517
|
+
rb_define_const(cRpMethodInfo, "MODULE_INCLUDEE", INT2NUM(kModuleIncludee));
|
518
|
+
rb_define_const(cRpMethodInfo, "CLASS_SINGLETON", INT2NUM(kClassSingleton));
|
519
|
+
rb_define_const(cRpMethodInfo, "MODULE_SINGLETON", INT2NUM(kModuleSingleton));
|
520
|
+
rb_define_const(cRpMethodInfo, "OBJECT_SINGLETON", INT2NUM(kObjectSingleton));
|
521
|
+
rb_define_const(cRpMethodInfo, "OTHER_SINGLETON", INT2NUM(kOtherSingleton));
|
522
|
+
|
473
523
|
rb_define_alloc_func(cRpMethodInfo, prof_method_allocate);
|
524
|
+
rb_define_method(cRpMethodInfo, "initialize", prof_method_initialize, 2);
|
525
|
+
rb_define_method(cRpMethodInfo, "hash", prof_method_hash, 0);
|
474
526
|
|
475
527
|
rb_define_method(cRpMethodInfo, "klass_name", prof_method_klass_name, 0);
|
476
528
|
rb_define_method(cRpMethodInfo, "klass_flags", prof_method_klass_flags, 0);
|
data/ext/ruby_prof/rp_method.h
CHANGED
@@ -15,7 +15,7 @@ enum {
|
|
15
15
|
kClassSingleton = 0x2, // Singleton of a class
|
16
16
|
kModuleSingleton = 0x4, // Singleton of a module
|
17
17
|
kObjectSingleton = 0x8, // Singleton of an object
|
18
|
-
kOtherSingleton = 0x10 // Singleton of
|
18
|
+
kOtherSingleton = 0x10 // Singleton of unknown object
|
19
19
|
};
|
20
20
|
|
21
21
|
// Profiling information for each method.
|
data/ext/ruby_prof/rp_profile.c
CHANGED
@@ -779,6 +779,36 @@ static VALUE prof_threads(VALUE self)
|
|
779
779
|
return result;
|
780
780
|
}
|
781
781
|
|
782
|
+
/* call-seq:
|
783
|
+
add_thread(thread) -> thread
|
784
|
+
|
785
|
+
Adds the specified RubyProf thread to the profile. */
|
786
|
+
static VALUE prof_add_thread(VALUE self, VALUE thread)
|
787
|
+
{
|
788
|
+
prof_profile_t* profile_ptr = prof_get_profile(self);
|
789
|
+
|
790
|
+
// This thread is now going to be owned by C
|
791
|
+
thread_data_t* thread_ptr = prof_get_thread(thread);
|
792
|
+
thread_ptr->owner = OWNER_C;
|
793
|
+
|
794
|
+
rb_st_insert(profile_ptr->threads_tbl, thread_ptr->fiber_id, (st_data_t)thread_ptr);
|
795
|
+
return thread;
|
796
|
+
}
|
797
|
+
|
798
|
+
/* call-seq:
|
799
|
+
remove_thread(thread) -> thread
|
800
|
+
|
801
|
+
Removes the specified thread from the profile. This is used to remove threads
|
802
|
+
after they have been merged togher. Retuns the removed thread. */
|
803
|
+
static VALUE prof_remove_thread(VALUE self, VALUE thread)
|
804
|
+
{
|
805
|
+
prof_profile_t* profile_ptr = prof_get_profile(self);
|
806
|
+
thread_data_t* thread_ptr = prof_get_thread(thread);
|
807
|
+
VALUE fiber_id = thread_ptr->fiber_id;
|
808
|
+
rb_st_delete(profile_ptr->threads_tbl, (st_data_t*)&fiber_id, NULL);
|
809
|
+
return thread;
|
810
|
+
}
|
811
|
+
|
782
812
|
/* Document-method: RubyProf::Profile#Profile
|
783
813
|
call-seq:
|
784
814
|
profile(&block) -> self
|
@@ -897,19 +927,22 @@ void rp_init_profile(void)
|
|
897
927
|
|
898
928
|
rb_define_singleton_method(cProfile, "profile", prof_profile_class, -1);
|
899
929
|
rb_define_method(cProfile, "initialize", prof_initialize, -1);
|
930
|
+
rb_define_method(cProfile, "profile", prof_profile_object, 0);
|
900
931
|
rb_define_method(cProfile, "start", prof_start, 0);
|
901
932
|
rb_define_method(cProfile, "stop", prof_stop, 0);
|
902
933
|
rb_define_method(cProfile, "resume", prof_resume, 0);
|
903
934
|
rb_define_method(cProfile, "pause", prof_pause, 0);
|
904
935
|
rb_define_method(cProfile, "running?", prof_running, 0);
|
905
936
|
rb_define_method(cProfile, "paused?", prof_paused, 0);
|
906
|
-
rb_define_method(cProfile, "threads", prof_threads, 0);
|
907
|
-
rb_define_method(cProfile, "exclude_method!", prof_exclude_method, 2);
|
908
|
-
rb_define_method(cProfile, "profile", prof_profile_object, 0);
|
909
937
|
|
938
|
+
rb_define_method(cProfile, "exclude_method!", prof_exclude_method, 2);
|
910
939
|
rb_define_method(cProfile, "measure_mode", prof_profile_measure_mode, 0);
|
911
940
|
rb_define_method(cProfile, "track_allocations?", prof_profile_track_allocations, 0);
|
912
941
|
|
942
|
+
rb_define_method(cProfile, "threads", prof_threads, 0);
|
943
|
+
rb_define_method(cProfile, "add_thread", prof_add_thread, 1);
|
944
|
+
rb_define_method(cProfile, "remove_thread", prof_remove_thread, 1);
|
945
|
+
|
913
946
|
rb_define_method(cProfile, "_dump_data", prof_profile_dump, 0);
|
914
947
|
rb_define_method(cProfile, "_load_data", prof_profile_load, 1);
|
915
948
|
}
|
data/ext/ruby_prof/rp_thread.c
CHANGED
@@ -25,6 +25,7 @@ VALUE cRpThread;
|
|
25
25
|
thread_data_t* thread_data_create(void)
|
26
26
|
{
|
27
27
|
thread_data_t* result = ALLOC(thread_data_t);
|
28
|
+
result->owner = OWNER_C;
|
28
29
|
result->stack = prof_stack_create();
|
29
30
|
result->method_table = method_table_create();
|
30
31
|
result->call_tree = NULL;
|
@@ -76,15 +77,6 @@ void prof_thread_mark(void* data)
|
|
76
77
|
rb_st_foreach(thread->method_table, mark_methods, 0);
|
77
78
|
}
|
78
79
|
|
79
|
-
void prof_thread_ruby_gc_free(void* data)
|
80
|
-
{
|
81
|
-
if (data)
|
82
|
-
{
|
83
|
-
thread_data_t* thread_data = (thread_data_t*)data;
|
84
|
-
thread_data->object = Qnil;
|
85
|
-
}
|
86
|
-
}
|
87
|
-
|
88
80
|
static void prof_thread_free(thread_data_t* thread_data)
|
89
81
|
{
|
90
82
|
/* Has this method object been accessed by Ruby? If
|
@@ -105,6 +97,27 @@ static void prof_thread_free(thread_data_t* thread_data)
|
|
105
97
|
xfree(thread_data);
|
106
98
|
}
|
107
99
|
|
100
|
+
void prof_thread_ruby_gc_free(void* data)
|
101
|
+
{
|
102
|
+
thread_data_t* thread_data = (thread_data_t*)data;
|
103
|
+
|
104
|
+
if (!thread_data)
|
105
|
+
{
|
106
|
+
// Object has already been freed by C code
|
107
|
+
return;
|
108
|
+
}
|
109
|
+
else if (thread_data->owner == OWNER_RUBY)
|
110
|
+
{
|
111
|
+
// Ruby owns this object, we need to free the underlying C struct
|
112
|
+
prof_thread_free(thread_data);
|
113
|
+
}
|
114
|
+
else
|
115
|
+
{
|
116
|
+
// The Ruby object is being freed, but not the underlying C structure. So unlink the two.
|
117
|
+
thread_data->object = Qnil;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
108
121
|
static const rb_data_type_t thread_type =
|
109
122
|
{
|
110
123
|
.wrap_struct_name = "ThreadInfo",
|
@@ -130,6 +143,7 @@ VALUE prof_thread_wrap(thread_data_t* thread)
|
|
130
143
|
static VALUE prof_thread_allocate(VALUE klass)
|
131
144
|
{
|
132
145
|
thread_data_t* thread_data = thread_data_create();
|
146
|
+
thread_data->owner = OWNER_RUBY;
|
133
147
|
thread_data->object = prof_thread_wrap(thread_data);
|
134
148
|
return thread_data->object;
|
135
149
|
}
|
@@ -171,7 +185,8 @@ thread_data_t* threads_table_lookup(void* prof, VALUE fiber)
|
|
171
185
|
thread_data_t* result = NULL;
|
172
186
|
st_data_t val;
|
173
187
|
|
174
|
-
|
188
|
+
VALUE fiber_id = rb_obj_id(fiber);
|
189
|
+
if (rb_st_lookup(profile->threads_tbl, fiber_id, &val))
|
175
190
|
{
|
176
191
|
result = (thread_data_t*)val;
|
177
192
|
}
|
@@ -188,7 +203,7 @@ thread_data_t* threads_table_insert(void* prof, VALUE fiber)
|
|
188
203
|
result->fiber = fiber;
|
189
204
|
result->fiber_id = rb_obj_id(fiber);
|
190
205
|
result->thread_id = rb_obj_id(thread);
|
191
|
-
rb_st_insert(profile->threads_tbl, (st_data_t)
|
206
|
+
rb_st_insert(profile->threads_tbl, (st_data_t)result->fiber_id, (st_data_t)result);
|
192
207
|
|
193
208
|
// Are we tracing this thread?
|
194
209
|
if (profile->include_threads_tbl && !rb_st_lookup(profile->include_threads_tbl, thread, 0))
|
@@ -267,6 +282,26 @@ static int collect_methods(st_data_t key, st_data_t value, st_data_t result)
|
|
267
282
|
}
|
268
283
|
|
269
284
|
// ====== RubyProf::Thread ======
|
285
|
+
/* call-seq:
|
286
|
+
new(call_tree, thread, fiber) -> thread
|
287
|
+
|
288
|
+
Creates a new RubyProf thread instance. +call_tree+ is the root call_tree instance,
|
289
|
+
+thread+ is a reference to a Ruby thread and +fiber+ is a reference to a Ruby fiber.*/
|
290
|
+
static VALUE prof_thread_initialize(VALUE self, VALUE call_tree, VALUE thread, VALUE fiber)
|
291
|
+
{
|
292
|
+
thread_data_t* thread_ptr = prof_get_thread(self);
|
293
|
+
|
294
|
+
// This call tree must now be managed by C
|
295
|
+
thread_ptr->call_tree = prof_get_call_tree(call_tree);
|
296
|
+
thread_ptr->call_tree->owner = OWNER_C;
|
297
|
+
|
298
|
+
thread_ptr->fiber = fiber;
|
299
|
+
thread_ptr->fiber_id = rb_obj_id(fiber);
|
300
|
+
thread_ptr->thread_id = rb_obj_id(thread);
|
301
|
+
|
302
|
+
return self;
|
303
|
+
}
|
304
|
+
|
270
305
|
/* call-seq:
|
271
306
|
id -> number
|
272
307
|
|
@@ -290,7 +325,7 @@ static VALUE prof_fiber_id(VALUE self)
|
|
290
325
|
/* call-seq:
|
291
326
|
call_tree -> CallTree
|
292
327
|
|
293
|
-
Returns the root
|
328
|
+
Returns the root call tree. */
|
294
329
|
static VALUE prof_call_tree(VALUE self)
|
295
330
|
{
|
296
331
|
thread_data_t* thread = prof_get_thread(self);
|
@@ -313,12 +348,22 @@ static VALUE prof_thread_methods(VALUE self)
|
|
313
348
|
return thread->methods;
|
314
349
|
}
|
315
350
|
|
351
|
+
static VALUE prof_thread_merge(VALUE self, VALUE other)
|
352
|
+
{
|
353
|
+
thread_data_t* self_ptr = prof_get_thread(self);
|
354
|
+
thread_data_t* other_ptr = prof_get_thread(other);
|
355
|
+
prof_call_tree_merge_internal(self_ptr->call_tree, other_ptr->call_tree);
|
356
|
+
|
357
|
+
return other;
|
358
|
+
}
|
359
|
+
|
316
360
|
/* :nodoc: */
|
317
361
|
static VALUE prof_thread_dump(VALUE self)
|
318
362
|
{
|
319
363
|
thread_data_t* thread_data = RTYPEDDATA_DATA(self);
|
320
364
|
|
321
365
|
VALUE result = rb_hash_new();
|
366
|
+
rb_hash_aset(result, ID2SYM(rb_intern("owner")), INT2FIX(thread_data->owner));
|
322
367
|
rb_hash_aset(result, ID2SYM(rb_intern("fiber_id")), thread_data->fiber_id);
|
323
368
|
rb_hash_aset(result, ID2SYM(rb_intern("methods")), prof_thread_methods(self));
|
324
369
|
rb_hash_aset(result, ID2SYM(rb_intern("call_tree")), prof_call_tree(self));
|
@@ -331,6 +376,8 @@ static VALUE prof_thread_load(VALUE self, VALUE data)
|
|
331
376
|
{
|
332
377
|
thread_data_t* thread_data = RTYPEDDATA_DATA(self);
|
333
378
|
|
379
|
+
thread_data->owner = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("owner"))));
|
380
|
+
|
334
381
|
VALUE call_tree = rb_hash_aref(data, ID2SYM(rb_intern("call_tree")));
|
335
382
|
thread_data->call_tree = prof_get_call_tree(call_tree);
|
336
383
|
|
@@ -350,13 +397,14 @@ static VALUE prof_thread_load(VALUE self, VALUE data)
|
|
350
397
|
void rp_init_thread(void)
|
351
398
|
{
|
352
399
|
cRpThread = rb_define_class_under(mProf, "Thread", rb_cObject);
|
353
|
-
rb_undef_method(CLASS_OF(cRpThread), "new");
|
354
400
|
rb_define_alloc_func(cRpThread, prof_thread_allocate);
|
401
|
+
rb_define_method(cRpThread, "initialize", prof_thread_initialize, 3);
|
355
402
|
|
356
403
|
rb_define_method(cRpThread, "id", prof_thread_id, 0);
|
357
404
|
rb_define_method(cRpThread, "call_tree", prof_call_tree, 0);
|
358
405
|
rb_define_method(cRpThread, "fiber_id", prof_fiber_id, 0);
|
359
406
|
rb_define_method(cRpThread, "methods", prof_thread_methods, 0);
|
407
|
+
rb_define_method(cRpThread, "merge!", prof_thread_merge, 1);
|
360
408
|
rb_define_method(cRpThread, "_dump_data", prof_thread_dump, 0);
|
361
409
|
rb_define_method(cRpThread, "_load_data", prof_thread_load, 1);
|
362
410
|
}
|
data/ext/ruby_prof/rp_thread.h
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
/* Profiling information for a thread. */
|
11
11
|
typedef struct thread_data_t
|
12
12
|
{
|
13
|
-
|
13
|
+
prof_owner_t owner; /* Who owns this object */
|
14
14
|
VALUE object; /* Cache to wrapped object */
|
15
15
|
VALUE fiber; /* Fiber */
|
16
16
|
prof_stack_t* stack; /* Stack of frames */
|
data/ext/ruby_prof/ruby_prof.c
CHANGED
@@ -29,7 +29,6 @@
|
|
29
29
|
#include "rp_measurement.h"
|
30
30
|
#include "rp_method.h"
|
31
31
|
#include "rp_call_tree.h"
|
32
|
-
#include "rp_aggregate_call_tree.h"
|
33
32
|
#include "rp_call_trees.h"
|
34
33
|
#include "rp_profile.h"
|
35
34
|
#include "rp_stack.h"
|
@@ -43,7 +42,6 @@ void Init_ruby_prof()
|
|
43
42
|
|
44
43
|
rp_init_allocation();
|
45
44
|
rp_init_call_tree();
|
46
|
-
rp_init_aggregate_call_tree();
|
47
45
|
rp_init_call_trees();
|
48
46
|
rp_init_measure();
|
49
47
|
rp_init_method_info();
|
data/ext/ruby_prof/ruby_prof.h
CHANGED
@@ -23,4 +23,12 @@ extern VALUE mProf;
|
|
23
23
|
// This method is not exposed in Ruby header files - at least not as of Ruby 2.6.3 :(
|
24
24
|
extern size_t rb_obj_memsize_of(VALUE);
|
25
25
|
|
26
|
+
typedef enum
|
27
|
+
{
|
28
|
+
OWNER_UNKNOWN = 0,
|
29
|
+
OWNER_RUBY = 1,
|
30
|
+
OWNER_C = 2
|
31
|
+
} prof_owner_t;
|
32
|
+
|
33
|
+
|
26
34
|
#endif //__RUBY_PROF_H__
|
@@ -66,7 +66,7 @@
|
|
66
66
|
</PropertyGroup>
|
67
67
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
68
68
|
<TargetExt>.so</TargetExt>
|
69
|
-
<OutDir>$(SolutionDir)
|
69
|
+
<OutDir>$(SolutionDir)\..\..\..\lib\</OutDir>
|
70
70
|
</PropertyGroup>
|
71
71
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
72
72
|
<ClCompile>
|
@@ -104,14 +104,14 @@
|
|
104
104
|
</ItemDefinitionGroup>
|
105
105
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
106
106
|
<ClCompile>
|
107
|
-
<AdditionalIncludeDirectories>C:\msys64\usr\local\ruby-3.
|
107
|
+
<AdditionalIncludeDirectories>C:\msys64\usr\local\ruby-3.2.0-vc\include\ruby-3.2.0\x64-mswin64_140;C:\msys64\usr\local\ruby-3.2.0-vc\include\ruby-3.2.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
108
108
|
<Optimization>Disabled</Optimization>
|
109
109
|
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
110
110
|
<WarningLevel>Level3</WarningLevel>
|
111
111
|
</ClCompile>
|
112
112
|
<Link>
|
113
|
-
<AdditionalLibraryDirectories>C:\msys64\usr\local\ruby-3.
|
114
|
-
<AdditionalDependencies>x64-vcruntime140-
|
113
|
+
<AdditionalLibraryDirectories>C:\msys64\usr\local\ruby-3.2.0-vc\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
114
|
+
<AdditionalDependencies>x64-vcruntime140-ruby320.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
115
115
|
<ModuleDefinitionFile>ruby_prof.def</ModuleDefinitionFile>
|
116
116
|
<SubSystem>Console</SubSystem>
|
117
117
|
</Link>
|
@@ -127,7 +127,6 @@
|
|
127
127
|
</Link>
|
128
128
|
</ItemDefinitionGroup>
|
129
129
|
<ItemGroup>
|
130
|
-
<ClInclude Include="..\rp_aggregate_call_tree.h" />
|
131
130
|
<ClInclude Include="..\rp_allocation.h" />
|
132
131
|
<ClInclude Include="..\rp_call_tree.h" />
|
133
132
|
<ClInclude Include="..\rp_call_trees.h" />
|
@@ -139,7 +138,6 @@
|
|
139
138
|
<ClInclude Include="..\ruby_prof.h" />
|
140
139
|
</ItemGroup>
|
141
140
|
<ItemGroup>
|
142
|
-
<ClCompile Include="..\rp_aggregate_call_tree.c" />
|
143
141
|
<ClCompile Include="..\rp_allocation.c" />
|
144
142
|
<ClCompile Include="..\rp_call_tree.c" />
|
145
143
|
<ClCompile Include="..\rp_call_trees.c" />
|
data/lib/3.1/ruby_prof.so
CHANGED
Binary file
|
data/lib/3.2/ruby_prof.so
CHANGED
Binary file
|
@@ -68,8 +68,6 @@ module RubyProf
|
|
68
68
|
|
69
69
|
def print_threads
|
70
70
|
remove_subsidiary_files_from_previous_profile_runs
|
71
|
-
# TODO: merge fibers of a given thread here, instead of relying
|
72
|
-
# on the profiler to merge fibers.
|
73
71
|
@result.threads.each do |thread|
|
74
72
|
print_thread(thread)
|
75
73
|
end
|
data/lib/ruby-prof/profile.rb
CHANGED
@@ -4,7 +4,6 @@ require 'ruby-prof/exclude_common_methods'
|
|
4
4
|
|
5
5
|
module RubyProf
|
6
6
|
class Profile
|
7
|
-
# :nodoc:
|
8
7
|
def measure_mode_string
|
9
8
|
case self.measure_mode
|
10
9
|
when WALL_TIME
|
@@ -33,5 +32,39 @@ module RubyProf
|
|
33
32
|
def exclude_singleton_methods!(mod, *method_or_methods)
|
34
33
|
exclude_methods!(mod.singleton_class, *method_or_methods)
|
35
34
|
end
|
35
|
+
|
36
|
+
# call-seq:
|
37
|
+
# merge! -> self
|
38
|
+
#
|
39
|
+
# Merges RubyProf threads whose root call_trees reference the same target method. This is useful
|
40
|
+
# when profiling code that uses a main thread/fiber to distribute work to multiple workers.
|
41
|
+
# If there are tens or hundreds of workers, viewing results per worker thread/fiber can be
|
42
|
+
# overwhelming. Using +merge!+ will combine the worker times together into one result.
|
43
|
+
#
|
44
|
+
# Note the reported time will be much greater than the actual wall time. For example, if there
|
45
|
+
# are 10 workers that each run for 5 seconds, merged results will show one thread that
|
46
|
+
# ran for 50 seconds.
|
47
|
+
#
|
48
|
+
def merge!
|
49
|
+
# First group threads by their root call tree target (method). If the methods are
|
50
|
+
# different than there is nothing to merge
|
51
|
+
grouped = threads.group_by do |thread|
|
52
|
+
thread.call_tree.target
|
53
|
+
end
|
54
|
+
|
55
|
+
# For each target, get the first thread. Then loop over the remaining threads,
|
56
|
+
# and merge them into the first one and ten delete them. So we will be left with
|
57
|
+
# one thread per target.
|
58
|
+
grouped.each do |target, threads|
|
59
|
+
thread = threads.shift
|
60
|
+
threads.each do |other_thread|
|
61
|
+
thread.merge!(other_thread)
|
62
|
+
remove_thread(other_thread)
|
63
|
+
end
|
64
|
+
thread
|
65
|
+
end
|
66
|
+
|
67
|
+
self
|
68
|
+
end
|
36
69
|
end
|
37
70
|
end
|
data/lib/ruby-prof/version.rb
CHANGED
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../test_helper', __FILE__)
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
# Create a DummyClass with methods so we can create call trees in the test_merge method below
|
7
|
+
class DummyClass
|
8
|
+
%i[root a b aa ab ba bb].each do |method_name|
|
9
|
+
define_method(method_name) do
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_call_tree(method_name)
|
15
|
+
method_info = RubyProf::MethodInfo.new(DummyClass, method_name)
|
16
|
+
RubyProf::CallTree.new(method_info)
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_call_tree(tree_hash)
|
20
|
+
# tree_hash is a hash keyed on the parent method_name whose values are
|
21
|
+
# child methods. Example:
|
22
|
+
#
|
23
|
+
# tree_hash = {:root => [:a, :b],
|
24
|
+
# :a => [:aa, :ab],
|
25
|
+
# :b => [:bb]}
|
26
|
+
#
|
27
|
+
# Note this is a simplified structure for testing. It assumes methods
|
28
|
+
# are only called from one call_tree.
|
29
|
+
|
30
|
+
call_trees = Hash.new
|
31
|
+
tree_hash.each do |method_name, children|
|
32
|
+
parent = call_trees[method_name] ||= create_call_tree(method_name)
|
33
|
+
children.each do |child_method_name|
|
34
|
+
child = call_trees[child_method_name] ||= create_call_tree(child_method_name)
|
35
|
+
parent.add_child(child)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
call_trees
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_call_tree_1
|
43
|
+
#
|
44
|
+
# root
|
45
|
+
# / \
|
46
|
+
# a b
|
47
|
+
# / \ \
|
48
|
+
# aa ab bb
|
49
|
+
#
|
50
|
+
|
51
|
+
# ------ Call Trees 1 -------------
|
52
|
+
tree_hash = {:root => [:a, :b],
|
53
|
+
:a => [:aa, :ab],
|
54
|
+
:b => [:bb]}
|
55
|
+
|
56
|
+
call_trees = build_call_tree(tree_hash)
|
57
|
+
|
58
|
+
# Setup times
|
59
|
+
call_trees[:aa].measurement.total_time = 1.5
|
60
|
+
call_trees[:aa].measurement.self_time = 1.5
|
61
|
+
call_trees[:ab].measurement.total_time = 2.2
|
62
|
+
call_trees[:ab].measurement.self_time = 2.2
|
63
|
+
call_trees[:a].measurement.total_time = 3.7
|
64
|
+
|
65
|
+
call_trees[:aa].target.measurement.total_time = 1.5
|
66
|
+
call_trees[:aa].target.measurement.self_time = 1.5
|
67
|
+
call_trees[:ab].target.measurement.total_time = 2.2
|
68
|
+
call_trees[:ab].target.measurement.self_time = 2.2
|
69
|
+
call_trees[:a].target.measurement.total_time = 3.7
|
70
|
+
|
71
|
+
call_trees[:bb].measurement.total_time = 4.3
|
72
|
+
call_trees[:bb].measurement.self_time = 4.3
|
73
|
+
call_trees[:b].measurement.total_time = 4.3
|
74
|
+
|
75
|
+
call_trees[:bb].target.measurement.total_time = 4.3
|
76
|
+
call_trees[:bb].target.measurement.self_time = 4.3
|
77
|
+
call_trees[:b].target.measurement.total_time = 4.3
|
78
|
+
|
79
|
+
call_trees[:root].measurement.total_time = 8.0
|
80
|
+
call_trees[:root].target.measurement.total_time = 8.0
|
81
|
+
|
82
|
+
call_trees[:root]
|
83
|
+
end
|
84
|
+
|
85
|
+
def create_call_tree_2
|
86
|
+
#
|
87
|
+
# root
|
88
|
+
# / \
|
89
|
+
# a b
|
90
|
+
# \ / \
|
91
|
+
# ab ba bb
|
92
|
+
|
93
|
+
tree_hash = {:root => [:a, :b],
|
94
|
+
:a => [:ab],
|
95
|
+
:b => [:ba, :bb]}
|
96
|
+
|
97
|
+
call_trees = build_call_tree(tree_hash)
|
98
|
+
|
99
|
+
# Setup times
|
100
|
+
call_trees[:ab].measurement.total_time = 0.4
|
101
|
+
call_trees[:ab].measurement.self_time = 0.4
|
102
|
+
call_trees[:a].measurement.total_time = 0.4
|
103
|
+
|
104
|
+
call_trees[:ab].target.measurement.total_time = 0.4
|
105
|
+
call_trees[:ab].target.measurement.self_time = 0.4
|
106
|
+
call_trees[:a].target.measurement.total_time = 0.4
|
107
|
+
|
108
|
+
call_trees[:ba].measurement.total_time = 0.9
|
109
|
+
call_trees[:ba].measurement.self_time = 0.7
|
110
|
+
call_trees[:ba].measurement.wait_time = 0.2
|
111
|
+
call_trees[:bb].measurement.total_time = 2.3
|
112
|
+
call_trees[:bb].measurement.self_time = 2.3
|
113
|
+
call_trees[:b].measurement.total_time = 3.2
|
114
|
+
|
115
|
+
call_trees[:ba].target.measurement.total_time = 0.9
|
116
|
+
call_trees[:ba].target.measurement.self_time = 0.7
|
117
|
+
call_trees[:ba].target.measurement.wait_time = 0.2
|
118
|
+
call_trees[:bb].target.measurement.total_time = 2.3
|
119
|
+
call_trees[:bb].target.measurement.self_time = 2.3
|
120
|
+
call_trees[:b].target.measurement.total_time = 3.2
|
121
|
+
|
122
|
+
call_trees[:root].measurement.total_time = 3.6
|
123
|
+
call_trees[:root].target.measurement.total_time = 3.6
|
124
|
+
|
125
|
+
call_trees[:root]
|
126
|
+
end
|