ruby-prof 1.5.0-x64-mingw-ucrt → 1.6.2-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +19 -0
  3. data/bin/ruby-prof +105 -87
  4. data/ext/ruby_prof/rp_allocation.c +136 -81
  5. data/ext/ruby_prof/rp_allocation.h +8 -6
  6. data/ext/ruby_prof/rp_call_tree.c +502 -457
  7. data/ext/ruby_prof/rp_call_tree.h +47 -44
  8. data/ext/ruby_prof/rp_call_trees.c +1 -1
  9. data/ext/ruby_prof/rp_measurement.c +10 -3
  10. data/ext/ruby_prof/rp_method.c +86 -79
  11. data/ext/ruby_prof/rp_method.h +63 -62
  12. data/ext/ruby_prof/rp_profile.c +933 -948
  13. data/ext/ruby_prof/rp_profile.h +1 -0
  14. data/ext/ruby_prof/rp_thread.c +433 -410
  15. data/ext/ruby_prof/rp_thread.h +39 -39
  16. data/ext/ruby_prof/vc/ruby_prof.vcxproj +6 -3
  17. data/lib/3.1/ruby_prof.so +0 -0
  18. data/lib/3.2/ruby_prof.so +0 -0
  19. data/lib/ruby-prof/compatibility.rb +14 -0
  20. data/lib/ruby-prof/printers/abstract_printer.rb +2 -1
  21. data/lib/ruby-prof/printers/call_tree_printer.rb +1 -1
  22. data/lib/ruby-prof/printers/multi_printer.rb +17 -17
  23. data/lib/ruby-prof/profile.rb +70 -70
  24. data/lib/ruby-prof/rack.rb +31 -21
  25. data/lib/ruby-prof/version.rb +1 -1
  26. data/test/abstract_printer_test.rb +1 -0
  27. data/test/alias_test.rb +6 -11
  28. data/test/call_tree_test.rb +94 -197
  29. data/test/call_tree_visitor_test.rb +1 -6
  30. data/test/call_trees_test.rb +2 -2
  31. data/test/{basic_test.rb → compatibility_test.rb} +8 -2
  32. data/test/duplicate_names_test.rb +1 -1
  33. data/test/dynamic_method_test.rb +1 -6
  34. data/test/enumerable_test.rb +1 -1
  35. data/test/exceptions_test.rb +2 -2
  36. data/test/exclude_methods_test.rb +3 -8
  37. data/test/exclude_threads_test.rb +4 -9
  38. data/test/fiber_test.rb +2 -58
  39. data/test/gc_test.rb +2 -2
  40. data/test/inverse_call_tree_test.rb +33 -34
  41. data/test/line_number_test.rb +1 -1
  42. data/test/marshal_test.rb +3 -3
  43. data/test/measure_allocations_test.rb +8 -17
  44. data/test/measure_memory_test.rb +3 -12
  45. data/test/measure_process_time_test.rb +32 -36
  46. data/test/measure_wall_time_test.rb +176 -181
  47. data/test/merge_test.rb +146 -0
  48. data/test/multi_printer_test.rb +0 -5
  49. data/test/no_method_class_test.rb +1 -1
  50. data/test/pause_resume_test.rb +12 -16
  51. data/test/printer_call_stack_test.rb +2 -2
  52. data/test/printer_call_tree_test.rb +2 -2
  53. data/test/printer_flat_test.rb +1 -1
  54. data/test/printer_graph_html_test.rb +2 -2
  55. data/test/printer_graph_test.rb +2 -2
  56. data/test/printers_test.rb +14 -20
  57. data/test/printing_recursive_graph_test.rb +2 -2
  58. data/test/recursive_test.rb +2 -7
  59. data/test/scheduler.rb +9 -0
  60. data/test/singleton_test.rb +1 -1
  61. data/test/stack_printer_test.rb +5 -8
  62. data/test/start_stop_test.rb +11 -14
  63. data/test/test_helper.rb +7 -0
  64. data/test/thread_test.rb +84 -19
  65. data/test/unique_call_path_test.rb +4 -4
  66. data/test/yarv_test.rb +3 -3
  67. metadata +6 -5
@@ -1,457 +1,502 @@
1
- /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
- Please see the LICENSE file for copyright and distribution information */
3
-
4
- #include "rp_call_tree.h"
5
-
6
- VALUE cRpCallTree;
7
-
8
- /* ======= prof_call_tree_t ========*/
9
- prof_call_tree_t* prof_call_tree_create(prof_method_t* method, prof_call_tree_t* parent, VALUE source_file, int source_line)
10
- {
11
- prof_call_tree_t* result = ALLOC(prof_call_tree_t);
12
- result->owner = OWNER_C;
13
- result->method = method;
14
- result->parent = parent;
15
- result->object = Qnil;
16
- result->visits = 0;
17
- result->source_line = source_line;
18
- result->source_file = source_file;
19
- result->children = rb_st_init_numtable();
20
- result->measurement = prof_measurement_create();
21
-
22
- return result;
23
- }
24
-
25
- prof_call_tree_t* prof_call_tree_copy(prof_call_tree_t* other)
26
- {
27
- prof_call_tree_t* result = prof_call_tree_create(other->method, other->parent, other->source_file, other->source_line);
28
- result->measurement = prof_measurement_copy(other->measurement);
29
-
30
- return result;
31
- }
32
-
33
- static int prof_call_tree_collect_children(st_data_t key, st_data_t value, st_data_t result)
34
- {
35
- prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
36
- VALUE arr = (VALUE)result;
37
- rb_ary_push(arr, prof_call_tree_wrap(call_tree));
38
- return ST_CONTINUE;
39
- }
40
-
41
- static int prof_call_tree_mark_children(st_data_t key, st_data_t value, st_data_t data)
42
- {
43
- prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
44
- rb_st_foreach(call_tree->children, prof_call_tree_mark_children, data);
45
- prof_call_tree_mark(call_tree);
46
- return ST_CONTINUE;
47
- }
48
-
49
- void prof_call_tree_mark(void* data)
50
- {
51
- if (!data)
52
- return;
53
-
54
- prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
55
-
56
- if (call_tree->object != Qnil)
57
- rb_gc_mark(call_tree->object);
58
-
59
- if (call_tree->source_file != Qnil)
60
- rb_gc_mark(call_tree->source_file);
61
-
62
- prof_method_mark(call_tree->method);
63
- prof_measurement_mark(call_tree->measurement);
64
-
65
- // Recurse down through the whole call tree but only from the top node
66
- // to avoid calling mark over and over and over.
67
- if (!call_tree->parent)
68
- rb_st_foreach(call_tree->children, prof_call_tree_mark_children, 0);
69
- }
70
-
71
- static int prof_call_tree_free_children(st_data_t key, st_data_t value, st_data_t data)
72
- {
73
- prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
74
- prof_call_tree_free(call_tree);
75
- return ST_CONTINUE;
76
- }
77
-
78
- void prof_call_tree_free(prof_call_tree_t* call_tree_data)
79
- {
80
- /* Has this call info object been accessed by Ruby? If
81
- yes clean it up so to avoid a segmentation fault. */
82
- if (call_tree_data->object != Qnil)
83
- {
84
- RTYPEDDATA(call_tree_data->object)->data = NULL;
85
- call_tree_data->object = Qnil;
86
- }
87
-
88
- // Free children
89
- rb_st_foreach(call_tree_data->children, prof_call_tree_free_children, 0);
90
- rb_st_free_table(call_tree_data->children);
91
-
92
- // Free measurement
93
- prof_measurement_free(call_tree_data->measurement);
94
-
95
- // Finally free self
96
- xfree(call_tree_data);
97
- }
98
-
99
- static void prof_call_tree_ruby_gc_free(void* data)
100
- {
101
- prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
102
-
103
- if (!call_tree)
104
- {
105
- // Object has already been freed by C code
106
- return;
107
- }
108
- else if (call_tree->owner == OWNER_RUBY)
109
- {
110
- // Ruby owns this object, we need to free the underlying C struct
111
- prof_call_tree_free(call_tree);
112
- }
113
- else
114
- {
115
- // The Ruby object is being freed, but not the underlying C structure. So unlink the two.
116
- call_tree->object = Qnil;
117
- }
118
- }
119
-
120
- size_t prof_call_tree_size(const void* data)
121
- {
122
- return sizeof(prof_call_tree_t);
123
- }
124
-
125
- static const rb_data_type_t call_tree_type =
126
- {
127
- .wrap_struct_name = "CallTree",
128
- .function =
129
- {
130
- .dmark = prof_call_tree_mark,
131
- .dfree = prof_call_tree_ruby_gc_free,
132
- .dsize = prof_call_tree_size,
133
- },
134
- .data = NULL,
135
- .flags = RUBY_TYPED_FREE_IMMEDIATELY
136
- };
137
-
138
- VALUE prof_call_tree_wrap(prof_call_tree_t* call_tree)
139
- {
140
- if (call_tree->object == Qnil)
141
- {
142
- call_tree->object = TypedData_Wrap_Struct(cRpCallTree, &call_tree_type, call_tree);
143
- }
144
- return call_tree->object;
145
- }
146
-
147
- static VALUE prof_call_tree_allocate(VALUE klass)
148
- {
149
- prof_call_tree_t* call_tree = prof_call_tree_create(NULL, NULL, Qnil, 0);
150
- // This object is being created by Ruby
151
- call_tree->owner = OWNER_RUBY;
152
- call_tree->object = prof_call_tree_wrap(call_tree);
153
- return call_tree->object;
154
- }
155
-
156
- prof_call_tree_t* prof_get_call_tree(VALUE self)
157
- {
158
- /* Can't use Data_Get_Struct because that triggers the event hook
159
- ending up in endless recursion. */
160
- prof_call_tree_t* result = RTYPEDDATA_DATA(self);
161
-
162
- if (!result)
163
- rb_raise(rb_eRuntimeError, "This RubyProf::CallTree instance has already been freed, likely because its profile has been freed.");
164
-
165
- return result;
166
- }
167
-
168
- /* ======= Call Tree Table ========*/
169
- static size_t call_tree_table_insert(st_table* table, st_data_t key, prof_call_tree_t* val)
170
- {
171
- return rb_st_insert(table, (st_data_t)key, (st_data_t)val);
172
- }
173
-
174
- prof_call_tree_t* call_tree_table_lookup(st_table* table, st_data_t key)
175
- {
176
- st_data_t val;
177
- if (rb_st_lookup(table, (st_data_t)key, &val))
178
- {
179
- return (prof_call_tree_t*)val;
180
- }
181
- else
182
- {
183
- return NULL;
184
- }
185
- }
186
-
187
- uint32_t prof_call_figure_depth(prof_call_tree_t* call_tree_data)
188
- {
189
- uint32_t result = 0;
190
-
191
- while (call_tree_data->parent)
192
- {
193
- result++;
194
- call_tree_data = call_tree_data->parent;
195
- }
196
-
197
- return result;
198
- }
199
-
200
- void prof_call_tree_add_parent(prof_call_tree_t* self, prof_call_tree_t* parent)
201
- {
202
- prof_call_tree_add_child(parent, self);
203
- self->parent = parent;
204
- }
205
-
206
- void prof_call_tree_add_child(prof_call_tree_t* self, prof_call_tree_t* child)
207
- {
208
- call_tree_table_insert(self->children, child->method->key, child);
209
-
210
- // The child is now managed by C since its parent will free it
211
- child->owner = OWNER_C;
212
- }
213
-
214
- /* ======= RubyProf::CallTree ========*/
215
-
216
- /* call-seq:
217
- new(method_info) -> call_tree
218
-
219
- Creates a new CallTree instance. +Klass+ should be a reference to
220
- a Ruby class and +method_name+ a symbol identifying one of its instance methods.*/
221
- static VALUE prof_call_tree_initialize(VALUE self, VALUE method_info)
222
- {
223
- prof_call_tree_t* call_tree_ptr = prof_get_call_tree(self);
224
- call_tree_ptr->method = prof_get_method(method_info);
225
-
226
- return self;
227
- }
228
-
229
- /* call-seq:
230
- parent -> call_tree
231
-
232
- Returns the CallTree parent call_tree object (the method that called this method).*/
233
- static VALUE prof_call_tree_parent(VALUE self)
234
- {
235
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
236
- if (call_tree->parent)
237
- return prof_call_tree_wrap(call_tree->parent);
238
- else
239
- return Qnil;
240
- }
241
-
242
- /* call-seq:
243
- callees -> array
244
-
245
- Returns an array of call info objects that this method called (ie, children).*/
246
- static VALUE prof_call_tree_children(VALUE self)
247
- {
248
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
249
- VALUE result = rb_ary_new();
250
- rb_st_foreach(call_tree->children, prof_call_tree_collect_children, result);
251
- return result;
252
- }
253
-
254
- /* call-seq:
255
- add_child(call_tree) -> call_tree
256
-
257
- Adds the specified call_tree as a child. If the method represented by the call tree is
258
- already a child than a IndexError is thrown.
259
-
260
- The returned value is the added child*/
261
- static VALUE prof_call_tree_add_child_ruby(VALUE self, VALUE child)
262
- {
263
- prof_call_tree_t* parent_ptr = prof_get_call_tree(self);
264
- prof_call_tree_t* child_ptr = prof_get_call_tree(child);
265
-
266
- prof_call_tree_t* existing_ptr = call_tree_table_lookup(parent_ptr->children, child_ptr->method->key);
267
- if (existing_ptr)
268
- {
269
- rb_raise(rb_eIndexError, "Child call tree already exists");
270
- }
271
-
272
- prof_call_tree_add_parent(child_ptr, parent_ptr);
273
-
274
- return child;
275
- }
276
-
277
- /* call-seq:
278
- called -> MethodInfo
279
-
280
- Returns the target method. */
281
- static VALUE prof_call_tree_target(VALUE self)
282
- {
283
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
284
- return prof_method_wrap(call_tree->method);
285
- }
286
-
287
- /* call-seq:
288
- called -> Measurement
289
-
290
- Returns the measurement associated with this call_tree. */
291
- static VALUE prof_call_tree_measurement(VALUE self)
292
- {
293
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
294
- return prof_measurement_wrap(call_tree->measurement);
295
- }
296
-
297
- /* call-seq:
298
- depth -> int
299
-
300
- returns the depth of this call info in the call graph */
301
- static VALUE prof_call_tree_depth(VALUE self)
302
- {
303
- prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
304
- uint32_t depth = prof_call_figure_depth(call_tree_data);
305
- return rb_int_new(depth);
306
- }
307
-
308
- /* call-seq:
309
- source_file => string
310
-
311
- return the source file of the method
312
- */
313
- static VALUE prof_call_tree_source_file(VALUE self)
314
- {
315
- prof_call_tree_t* result = prof_get_call_tree(self);
316
- return result->source_file;
317
- }
318
-
319
- /* call-seq:
320
- line_no -> int
321
-
322
- returns the line number of the method */
323
- static VALUE prof_call_tree_line(VALUE self)
324
- {
325
- prof_call_tree_t* result = prof_get_call_tree(self);
326
- return INT2FIX(result->source_line);
327
- }
328
-
329
- static int prof_call_tree_merge_children(st_data_t key, st_data_t value, st_data_t data)
330
- {
331
- prof_call_tree_t* other_child = (prof_call_tree_t*)value;
332
- prof_call_tree_t* self = (prof_call_tree_t*)data;
333
-
334
- st_data_t self_child;
335
- if (rb_st_lookup(self->children, other_child->method->key, &self_child))
336
- {
337
- prof_call_tree_merge_internal((prof_call_tree_t*)self_child, other_child);
338
- }
339
- else
340
- {
341
- prof_call_tree_t* copy = prof_call_tree_copy(other_child);
342
- prof_call_tree_add_child(self, copy);
343
- }
344
- return ST_CONTINUE;
345
- }
346
-
347
- void prof_call_tree_merge_internal(prof_call_tree_t* self, prof_call_tree_t* other)
348
- {
349
- // Make sure the methods are the same
350
- if (self->method->key != other->method->key)
351
- return;
352
-
353
- // Make sure the parents are the same.
354
- // 1. They can both be set and be equal
355
- // 2. They can both be unset (null)
356
- if (self->parent && other->parent)
357
- {
358
- if (self->parent->method->key != other->parent->method->key)
359
- return;
360
- }
361
- else if (self->parent || other->parent)
362
- {
363
- return;
364
- }
365
-
366
- prof_measurement_merge_internal(self->measurement, other->measurement);
367
- prof_measurement_merge_internal(self->method->measurement, other->method->measurement);
368
-
369
- rb_st_foreach(other->children, prof_call_tree_merge_children, (st_data_t)self);
370
- }
371
-
372
- VALUE prof_call_tree_merge(VALUE self, VALUE other)
373
- {
374
- prof_call_tree_t* source = prof_get_call_tree(self);
375
- prof_call_tree_t* destination = prof_get_call_tree(other);
376
- prof_call_tree_merge_internal(source, destination);
377
- return other;
378
- }
379
-
380
- /* :nodoc: */
381
- static VALUE prof_call_tree_dump(VALUE self)
382
- {
383
- prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
384
- VALUE result = rb_hash_new();
385
-
386
- rb_hash_aset(result, ID2SYM(rb_intern("owner")), INT2FIX(call_tree_data->owner));
387
-
388
- rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(call_tree_data->measurement));
389
-
390
- rb_hash_aset(result, ID2SYM(rb_intern("source_file")), call_tree_data->source_file);
391
- rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(call_tree_data->source_line));
392
-
393
- rb_hash_aset(result, ID2SYM(rb_intern("parent")), prof_call_tree_parent(self));
394
- rb_hash_aset(result, ID2SYM(rb_intern("children")), prof_call_tree_children(self));
395
- rb_hash_aset(result, ID2SYM(rb_intern("target")), prof_call_tree_target(self));
396
-
397
- return result;
398
- }
399
-
400
- /* :nodoc: */
401
- static VALUE prof_call_tree_load(VALUE self, VALUE data)
402
- {
403
- VALUE target = Qnil;
404
- VALUE parent = Qnil;
405
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
406
- call_tree->object = self;
407
-
408
- call_tree->owner = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("owner"))));
409
-
410
- VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
411
- call_tree->measurement = prof_get_measurement(measurement);
412
-
413
- call_tree->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
414
- call_tree->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
415
-
416
- parent = rb_hash_aref(data, ID2SYM(rb_intern("parent")));
417
- if (parent != Qnil)
418
- call_tree->parent = prof_get_call_tree(parent);
419
-
420
- VALUE callees = rb_hash_aref(data, ID2SYM(rb_intern("children")));
421
- for (int i = 0; i < rb_array_len(callees); i++)
422
- {
423
- VALUE call_tree_object = rb_ary_entry(callees, i);
424
- prof_call_tree_t* call_tree_data = prof_get_call_tree(call_tree_object);
425
-
426
- st_data_t key = call_tree_data->method ? call_tree_data->method->key : method_key(Qnil, 0);
427
- call_tree_table_insert(call_tree->children, key, call_tree_data);
428
- }
429
-
430
- target = rb_hash_aref(data, ID2SYM(rb_intern("target")));
431
- call_tree->method = prof_get_method(target);
432
-
433
- return data;
434
- }
435
-
436
- void rp_init_call_tree()
437
- {
438
- /* CallTree */
439
- cRpCallTree = rb_define_class_under(mProf, "CallTree", rb_cObject);
440
- rb_define_alloc_func(cRpCallTree, prof_call_tree_allocate);
441
- rb_define_method(cRpCallTree, "initialize", prof_call_tree_initialize, 1);
442
-
443
- rb_define_method(cRpCallTree, "target", prof_call_tree_target, 0);
444
- rb_define_method(cRpCallTree, "measurement", prof_call_tree_measurement, 0);
445
- rb_define_method(cRpCallTree, "parent", prof_call_tree_parent, 0);
446
- rb_define_method(cRpCallTree, "children", prof_call_tree_children, 0);
447
- rb_define_method(cRpCallTree, "add_child", prof_call_tree_add_child_ruby, 1);
448
-
449
- rb_define_method(cRpCallTree, "depth", prof_call_tree_depth, 0);
450
- rb_define_method(cRpCallTree, "source_file", prof_call_tree_source_file, 0);
451
- rb_define_method(cRpCallTree, "line", prof_call_tree_line, 0);
452
-
453
- rb_define_method(cRpCallTree, "merge!", prof_call_tree_merge, 1);
454
-
455
- rb_define_method(cRpCallTree, "_dump_data", prof_call_tree_dump, 0);
456
- rb_define_method(cRpCallTree, "_load_data", prof_call_tree_load, 1);
457
- }
1
+ /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #include "rp_call_tree.h"
5
+ #include "rp_call_trees.h"
6
+ #include "rp_thread.h"
7
+
8
+ VALUE cRpCallTree;
9
+
10
+ /* ======= prof_call_tree_t ========*/
11
+ prof_call_tree_t* prof_call_tree_create(prof_method_t* method, prof_call_tree_t* parent, VALUE source_file, int source_line)
12
+ {
13
+ prof_call_tree_t* result = ALLOC(prof_call_tree_t);
14
+ result->owner = OWNER_C;
15
+ result->method = method;
16
+ result->parent = parent;
17
+ result->object = Qnil;
18
+ result->visits = 0;
19
+ result->source_line = source_line;
20
+ result->source_file = source_file;
21
+ result->children = rb_st_init_numtable();
22
+ result->measurement = prof_measurement_create();
23
+
24
+ return result;
25
+ }
26
+
27
+ prof_call_tree_t* prof_call_tree_copy(prof_call_tree_t* other)
28
+ {
29
+ prof_call_tree_t* result = prof_call_tree_create(other->method, other->parent, other->source_file, other->source_line);
30
+ result->measurement = prof_measurement_copy(other->measurement);
31
+
32
+ return result;
33
+ }
34
+
35
+ static int prof_call_tree_collect_children(st_data_t key, st_data_t value, st_data_t result)
36
+ {
37
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
38
+ VALUE arr = (VALUE)result;
39
+ rb_ary_push(arr, prof_call_tree_wrap(call_tree));
40
+ return ST_CONTINUE;
41
+ }
42
+
43
+ static int prof_call_tree_mark_children(st_data_t key, st_data_t value, st_data_t data)
44
+ {
45
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
46
+ rb_st_foreach(call_tree->children, prof_call_tree_mark_children, data);
47
+ prof_call_tree_mark(call_tree);
48
+ return ST_CONTINUE;
49
+ }
50
+
51
+ void prof_call_tree_mark(void* data)
52
+ {
53
+ if (!data)
54
+ return;
55
+
56
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
57
+
58
+ if (call_tree->object != Qnil)
59
+ rb_gc_mark_movable(call_tree->object);
60
+
61
+ if (call_tree->source_file != Qnil)
62
+ rb_gc_mark(call_tree->source_file);
63
+
64
+ prof_method_mark(call_tree->method);
65
+ prof_measurement_mark(call_tree->measurement);
66
+
67
+ // Recurse down through the whole call tree but only from the top node
68
+ // to avoid calling mark over and over and over.
69
+ if (!call_tree->parent)
70
+ rb_st_foreach(call_tree->children, prof_call_tree_mark_children, 0);
71
+ }
72
+
73
+ void prof_call_tree_compact(void* data)
74
+ {
75
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
76
+ call_tree->object = rb_gc_location(call_tree->object);
77
+ }
78
+
79
+ static int prof_call_tree_free_children(st_data_t key, st_data_t value, st_data_t data)
80
+ {
81
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
82
+ prof_call_tree_free(call_tree);
83
+ return ST_CONTINUE;
84
+ }
85
+
86
+ void prof_call_tree_free(prof_call_tree_t* call_tree_data)
87
+ {
88
+ /* Has this call info object been accessed by Ruby? If
89
+ yes clean it up so to avoid a segmentation fault. */
90
+ if (call_tree_data->object != Qnil)
91
+ {
92
+ RTYPEDDATA(call_tree_data->object)->data = NULL;
93
+ call_tree_data->object = Qnil;
94
+ }
95
+
96
+ // Free children
97
+ rb_st_foreach(call_tree_data->children, prof_call_tree_free_children, 0);
98
+ rb_st_free_table(call_tree_data->children);
99
+
100
+ // Free measurement
101
+ prof_measurement_free(call_tree_data->measurement);
102
+
103
+ // Finally free self
104
+ xfree(call_tree_data);
105
+ }
106
+
107
+ static void prof_call_tree_ruby_gc_free(void* data)
108
+ {
109
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
110
+
111
+ if (!call_tree)
112
+ {
113
+ // Object has already been freed by C code
114
+ return;
115
+ }
116
+ else if (call_tree->owner == OWNER_RUBY)
117
+ {
118
+ // Ruby owns this object, we need to free the underlying C struct
119
+ prof_call_tree_free(call_tree);
120
+ }
121
+ else
122
+ {
123
+ // The Ruby object is being freed, but not the underlying C structure. So unlink the two.
124
+ call_tree->object = Qnil;
125
+ }
126
+ }
127
+
128
+ size_t prof_call_tree_size(const void* data)
129
+ {
130
+ return sizeof(prof_call_tree_t);
131
+ }
132
+
133
+ static const rb_data_type_t call_tree_type =
134
+ {
135
+ .wrap_struct_name = "CallTree",
136
+ .function =
137
+ {
138
+ .dmark = prof_call_tree_mark,
139
+ .dfree = prof_call_tree_ruby_gc_free,
140
+ .dsize = prof_call_tree_size,
141
+ .dcompact = prof_call_tree_compact
142
+ },
143
+ .data = NULL,
144
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
145
+ };
146
+
147
+ VALUE prof_call_tree_wrap(prof_call_tree_t* call_tree)
148
+ {
149
+ if (call_tree->object == Qnil)
150
+ {
151
+ call_tree->object = TypedData_Wrap_Struct(cRpCallTree, &call_tree_type, call_tree);
152
+ }
153
+ return call_tree->object;
154
+ }
155
+
156
+ static VALUE prof_call_tree_allocate(VALUE klass)
157
+ {
158
+ prof_call_tree_t* call_tree = prof_call_tree_create(NULL, NULL, Qnil, 0);
159
+ // This object is being created by Ruby
160
+ call_tree->owner = OWNER_RUBY;
161
+ call_tree->object = prof_call_tree_wrap(call_tree);
162
+ return call_tree->object;
163
+ }
164
+
165
+ prof_call_tree_t* prof_get_call_tree(VALUE self)
166
+ {
167
+ /* Can't use Data_Get_Struct because that triggers the event hook
168
+ ending up in endless recursion. */
169
+ prof_call_tree_t* result = RTYPEDDATA_DATA(self);
170
+
171
+ if (!result)
172
+ rb_raise(rb_eRuntimeError, "This RubyProf::CallTree instance has already been freed, likely because its profile has been freed.");
173
+
174
+ return result;
175
+ }
176
+
177
+ /* ======= Call Tree Table ========*/
178
+ static size_t call_tree_table_insert(st_table* table, st_data_t key, prof_call_tree_t* val)
179
+ {
180
+ return rb_st_insert(table, (st_data_t)key, (st_data_t)val);
181
+ }
182
+
183
+ prof_call_tree_t* call_tree_table_lookup(st_table* table, st_data_t key)
184
+ {
185
+ st_data_t val;
186
+ if (rb_st_lookup(table, (st_data_t)key, &val))
187
+ {
188
+ return (prof_call_tree_t*)val;
189
+ }
190
+ else
191
+ {
192
+ return NULL;
193
+ }
194
+ }
195
+
196
+ uint32_t prof_call_tree_figure_depth(prof_call_tree_t* call_tree)
197
+ {
198
+ uint32_t result = 0;
199
+
200
+ while (call_tree->parent)
201
+ {
202
+ result++;
203
+ call_tree = call_tree->parent;
204
+ }
205
+
206
+ return result;
207
+ }
208
+
209
+ int prof_call_tree_collect_methods(st_data_t key, st_data_t value, st_data_t result)
210
+ {
211
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
212
+ VALUE arr = (VALUE)result;
213
+ rb_ary_push(arr, prof_method_wrap(call_tree->method));
214
+
215
+ rb_st_foreach(call_tree->children, prof_call_tree_collect_methods, result);
216
+ return ST_CONTINUE;
217
+ };
218
+
219
+ VALUE prof_call_tree_methods(prof_call_tree_t* call_tree)
220
+ {
221
+ VALUE result = rb_ary_new();
222
+ rb_ary_push(result, prof_method_wrap(call_tree->method));
223
+
224
+ rb_st_foreach(call_tree->children, prof_call_tree_collect_methods, result);
225
+
226
+ return result;
227
+ }
228
+
229
+ void prof_call_tree_add_parent(prof_call_tree_t* self, prof_call_tree_t* parent)
230
+ {
231
+ prof_call_tree_add_child(parent, self);
232
+ self->parent = parent;
233
+ }
234
+
235
+ void prof_call_tree_add_child(prof_call_tree_t* self, prof_call_tree_t* child)
236
+ {
237
+ call_tree_table_insert(self->children, child->method->key, child);
238
+
239
+ // The child is now managed by C since its parent will free it
240
+ child->owner = OWNER_C;
241
+ }
242
+
243
+ /* ======= RubyProf::CallTree ========*/
244
+
245
+ /* call-seq:
246
+ new(method_info) -> call_tree
247
+
248
+ Creates a new CallTree instance. +Klass+ should be a reference to
249
+ a Ruby class and +method_name+ a symbol identifying one of its instance methods.*/
250
+ static VALUE prof_call_tree_initialize(VALUE self, VALUE method_info)
251
+ {
252
+ prof_call_tree_t* call_tree_ptr = prof_get_call_tree(self);
253
+ call_tree_ptr->method = prof_get_method(method_info);
254
+
255
+ return self;
256
+ }
257
+
258
+ /* call-seq:
259
+ parent -> call_tree
260
+
261
+ Returns the CallTree parent call_tree object (the method that called this method).*/
262
+ static VALUE prof_call_tree_parent(VALUE self)
263
+ {
264
+ prof_call_tree_t* call_tree = prof_get_call_tree(self);
265
+ if (call_tree->parent)
266
+ return prof_call_tree_wrap(call_tree->parent);
267
+ else
268
+ return Qnil;
269
+ }
270
+
271
+ /* call-seq:
272
+ callees -> array
273
+
274
+ Returns an array of call info objects that this method called (ie, children).*/
275
+ static VALUE prof_call_tree_children(VALUE self)
276
+ {
277
+ prof_call_tree_t* call_tree = prof_get_call_tree(self);
278
+ VALUE result = rb_ary_new();
279
+ rb_st_foreach(call_tree->children, prof_call_tree_collect_children, result);
280
+ return result;
281
+ }
282
+
283
+ /* call-seq:
284
+ add_child(call_tree) -> call_tree
285
+
286
+ Adds the specified call_tree as a child. If the method represented by the call tree is
287
+ already a child than a IndexError is thrown.
288
+
289
+ The returned value is the added child*/
290
+ static VALUE prof_call_tree_add_child_ruby(VALUE self, VALUE child)
291
+ {
292
+ prof_call_tree_t* parent_ptr = prof_get_call_tree(self);
293
+ prof_call_tree_t* child_ptr = prof_get_call_tree(child);
294
+
295
+ prof_call_tree_t* existing_ptr = call_tree_table_lookup(parent_ptr->children, child_ptr->method->key);
296
+ if (existing_ptr)
297
+ {
298
+ rb_raise(rb_eIndexError, "Child call tree already exists");
299
+ }
300
+
301
+ prof_call_tree_add_parent(child_ptr, parent_ptr);
302
+
303
+ return child;
304
+ }
305
+
306
+ /* call-seq:
307
+ called -> MethodInfo
308
+
309
+ Returns the target method. */
310
+ static VALUE prof_call_tree_target(VALUE self)
311
+ {
312
+ prof_call_tree_t* call_tree = prof_get_call_tree(self);
313
+ return prof_method_wrap(call_tree->method);
314
+ }
315
+
316
+ /* call-seq:
317
+ called -> Measurement
318
+
319
+ Returns the measurement associated with this call_tree. */
320
+ static VALUE prof_call_tree_measurement(VALUE self)
321
+ {
322
+ prof_call_tree_t* call_tree = prof_get_call_tree(self);
323
+ return prof_measurement_wrap(call_tree->measurement);
324
+ }
325
+
326
+ /* call-seq:
327
+ depth -> int
328
+
329
+ returns the depth of this call info in the call graph */
330
+ static VALUE prof_call_tree_depth(VALUE self)
331
+ {
332
+ prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
333
+ uint32_t depth = prof_call_tree_figure_depth(call_tree_data);
334
+ return rb_int_new(depth);
335
+ }
336
+
337
+ /* call-seq:
338
+ source_file => string
339
+
340
+ return the source file of the method
341
+ */
342
+ static VALUE prof_call_tree_source_file(VALUE self)
343
+ {
344
+ prof_call_tree_t* result = prof_get_call_tree(self);
345
+ return result->source_file;
346
+ }
347
+
348
+ /* call-seq:
349
+ line_no -> int
350
+
351
+ returns the line number of the method */
352
+ static VALUE prof_call_tree_line(VALUE self)
353
+ {
354
+ prof_call_tree_t* result = prof_get_call_tree(self);
355
+ return INT2FIX(result->source_line);
356
+ }
357
+
358
+ // Helper class that lets us pass additional information to prof_call_tree_merge_children
359
+ typedef struct self_info_t
360
+ {
361
+ prof_call_tree_t* call_tree;
362
+ st_table* method_table;
363
+ } self_info_t;
364
+
365
+
366
+ static int prof_call_tree_merge_children(st_data_t key, st_data_t value, st_data_t data)
367
+ {
368
+ prof_call_tree_t* other_child_ptr = (prof_call_tree_t*)value;
369
+
370
+ self_info_t* self_info = (self_info_t*)data;
371
+ prof_call_tree_t* self_ptr = self_info->call_tree;
372
+
373
+ prof_call_tree_t* self_child = call_tree_table_lookup(self_ptr->children, other_child_ptr->method->key);
374
+ if (self_child)
375
+ {
376
+ // Merge measurements
377
+ prof_measurement_merge_internal(self_child->measurement, other_child_ptr->measurement);
378
+ }
379
+ else
380
+ {
381
+ // Get pointer to method the other call tree invoked
382
+ prof_method_t* method_ptr = method_table_lookup(self_info->method_table, other_child_ptr->method->key);
383
+
384
+ // Now copy the other call tree, reset its method pointer, and add it as a child
385
+ self_child = prof_call_tree_copy(other_child_ptr);
386
+ self_child->method = method_ptr;
387
+ prof_call_tree_add_child(self_ptr, self_child);
388
+
389
+ // Now tell the method that this call tree invoked it
390
+ prof_add_call_tree(method_ptr->call_trees, self_child);
391
+ }
392
+
393
+ // Recurse down a level to merge children
394
+ self_info_t child_info = { .call_tree = self_child, .method_table = self_info->method_table };
395
+ rb_st_foreach(other_child_ptr->children, prof_call_tree_merge_children, (st_data_t)&child_info);
396
+
397
+ return ST_CONTINUE;
398
+ }
399
+
400
+ void prof_call_tree_merge_internal(prof_call_tree_t* self, prof_call_tree_t* other, st_table* self_method_table)
401
+ {
402
+ // Make sure the methods are the same
403
+ if (self->method->key != other->method->key)
404
+ return;
405
+
406
+ // Make sure the parents are the same.
407
+ // 1. They can both be set and be equal
408
+ // 2. They can both be unset (null)
409
+ if (self->parent && other->parent)
410
+ {
411
+ if (self->parent->method->key != other->parent->method->key)
412
+ return;
413
+ }
414
+ else if (self->parent || other->parent)
415
+ {
416
+ return;
417
+ }
418
+
419
+ // Merge measurements
420
+ prof_measurement_merge_internal(self->measurement, other->measurement);
421
+
422
+ // Now recursively descend through the call trees
423
+ self_info_t self_info = { .call_tree = self, .method_table = self_method_table };
424
+ rb_st_foreach(other->children, prof_call_tree_merge_children, (st_data_t)&self_info);
425
+ }
426
+
427
+ /* :nodoc: */
428
+ static VALUE prof_call_tree_dump(VALUE self)
429
+ {
430
+ prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
431
+ VALUE result = rb_hash_new();
432
+
433
+ rb_hash_aset(result, ID2SYM(rb_intern("owner")), INT2FIX(call_tree_data->owner));
434
+
435
+ rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(call_tree_data->measurement));
436
+
437
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), call_tree_data->source_file);
438
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(call_tree_data->source_line));
439
+
440
+ rb_hash_aset(result, ID2SYM(rb_intern("parent")), prof_call_tree_parent(self));
441
+ rb_hash_aset(result, ID2SYM(rb_intern("children")), prof_call_tree_children(self));
442
+ rb_hash_aset(result, ID2SYM(rb_intern("target")), prof_call_tree_target(self));
443
+
444
+ return result;
445
+ }
446
+
447
+ /* :nodoc: */
448
+ static VALUE prof_call_tree_load(VALUE self, VALUE data)
449
+ {
450
+ VALUE target = Qnil;
451
+ VALUE parent = Qnil;
452
+ prof_call_tree_t* call_tree = prof_get_call_tree(self);
453
+ call_tree->object = self;
454
+
455
+ call_tree->owner = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("owner"))));
456
+
457
+ VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
458
+ call_tree->measurement = prof_get_measurement(measurement);
459
+
460
+ call_tree->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
461
+ call_tree->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
462
+
463
+ parent = rb_hash_aref(data, ID2SYM(rb_intern("parent")));
464
+ if (parent != Qnil)
465
+ call_tree->parent = prof_get_call_tree(parent);
466
+
467
+ VALUE callees = rb_hash_aref(data, ID2SYM(rb_intern("children")));
468
+ for (int i = 0; i < rb_array_len(callees); i++)
469
+ {
470
+ VALUE call_tree_object = rb_ary_entry(callees, i);
471
+ prof_call_tree_t* call_tree_data = prof_get_call_tree(call_tree_object);
472
+
473
+ st_data_t key = call_tree_data->method ? call_tree_data->method->key : method_key(Qnil, 0);
474
+ call_tree_table_insert(call_tree->children, key, call_tree_data);
475
+ }
476
+
477
+ target = rb_hash_aref(data, ID2SYM(rb_intern("target")));
478
+ call_tree->method = prof_get_method(target);
479
+
480
+ return data;
481
+ }
482
+
483
+ void rp_init_call_tree()
484
+ {
485
+ /* CallTree */
486
+ cRpCallTree = rb_define_class_under(mProf, "CallTree", rb_cObject);
487
+ rb_define_alloc_func(cRpCallTree, prof_call_tree_allocate);
488
+ rb_define_method(cRpCallTree, "initialize", prof_call_tree_initialize, 1);
489
+
490
+ rb_define_method(cRpCallTree, "target", prof_call_tree_target, 0);
491
+ rb_define_method(cRpCallTree, "measurement", prof_call_tree_measurement, 0);
492
+ rb_define_method(cRpCallTree, "parent", prof_call_tree_parent, 0);
493
+ rb_define_method(cRpCallTree, "children", prof_call_tree_children, 0);
494
+ rb_define_method(cRpCallTree, "add_child", prof_call_tree_add_child_ruby, 1);
495
+
496
+ rb_define_method(cRpCallTree, "depth", prof_call_tree_depth, 0);
497
+ rb_define_method(cRpCallTree, "source_file", prof_call_tree_source_file, 0);
498
+ rb_define_method(cRpCallTree, "line", prof_call_tree_line, 0);
499
+
500
+ rb_define_method(cRpCallTree, "_dump_data", prof_call_tree_dump, 0);
501
+ rb_define_method(cRpCallTree, "_load_data", prof_call_tree_load, 1);
502
+ }