ruby-prof 1.6.1 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,464 +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_movable(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
- void prof_call_tree_compact(void* data)
72
- {
73
- prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
74
- call_tree->object = rb_gc_location(call_tree->object);
75
- }
76
-
77
- static int prof_call_tree_free_children(st_data_t key, st_data_t value, st_data_t data)
78
- {
79
- prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
80
- prof_call_tree_free(call_tree);
81
- return ST_CONTINUE;
82
- }
83
-
84
- void prof_call_tree_free(prof_call_tree_t* call_tree_data)
85
- {
86
- /* Has this call info object been accessed by Ruby? If
87
- yes clean it up so to avoid a segmentation fault. */
88
- if (call_tree_data->object != Qnil)
89
- {
90
- RTYPEDDATA(call_tree_data->object)->data = NULL;
91
- call_tree_data->object = Qnil;
92
- }
93
-
94
- // Free children
95
- rb_st_foreach(call_tree_data->children, prof_call_tree_free_children, 0);
96
- rb_st_free_table(call_tree_data->children);
97
-
98
- // Free measurement
99
- prof_measurement_free(call_tree_data->measurement);
100
-
101
- // Finally free self
102
- xfree(call_tree_data);
103
- }
104
-
105
- static void prof_call_tree_ruby_gc_free(void* data)
106
- {
107
- prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
108
-
109
- if (!call_tree)
110
- {
111
- // Object has already been freed by C code
112
- return;
113
- }
114
- else if (call_tree->owner == OWNER_RUBY)
115
- {
116
- // Ruby owns this object, we need to free the underlying C struct
117
- prof_call_tree_free(call_tree);
118
- }
119
- else
120
- {
121
- // The Ruby object is being freed, but not the underlying C structure. So unlink the two.
122
- call_tree->object = Qnil;
123
- }
124
- }
125
-
126
- size_t prof_call_tree_size(const void* data)
127
- {
128
- return sizeof(prof_call_tree_t);
129
- }
130
-
131
- static const rb_data_type_t call_tree_type =
132
- {
133
- .wrap_struct_name = "CallTree",
134
- .function =
135
- {
136
- .dmark = prof_call_tree_mark,
137
- .dfree = prof_call_tree_ruby_gc_free,
138
- .dsize = prof_call_tree_size,
139
- .dcompact = prof_call_tree_compact
140
- },
141
- .data = NULL,
142
- .flags = RUBY_TYPED_FREE_IMMEDIATELY
143
- };
144
-
145
- VALUE prof_call_tree_wrap(prof_call_tree_t* call_tree)
146
- {
147
- if (call_tree->object == Qnil)
148
- {
149
- call_tree->object = TypedData_Wrap_Struct(cRpCallTree, &call_tree_type, call_tree);
150
- }
151
- return call_tree->object;
152
- }
153
-
154
- static VALUE prof_call_tree_allocate(VALUE klass)
155
- {
156
- prof_call_tree_t* call_tree = prof_call_tree_create(NULL, NULL, Qnil, 0);
157
- // This object is being created by Ruby
158
- call_tree->owner = OWNER_RUBY;
159
- call_tree->object = prof_call_tree_wrap(call_tree);
160
- return call_tree->object;
161
- }
162
-
163
- prof_call_tree_t* prof_get_call_tree(VALUE self)
164
- {
165
- /* Can't use Data_Get_Struct because that triggers the event hook
166
- ending up in endless recursion. */
167
- prof_call_tree_t* result = RTYPEDDATA_DATA(self);
168
-
169
- if (!result)
170
- rb_raise(rb_eRuntimeError, "This RubyProf::CallTree instance has already been freed, likely because its profile has been freed.");
171
-
172
- return result;
173
- }
174
-
175
- /* ======= Call Tree Table ========*/
176
- static size_t call_tree_table_insert(st_table* table, st_data_t key, prof_call_tree_t* val)
177
- {
178
- return rb_st_insert(table, (st_data_t)key, (st_data_t)val);
179
- }
180
-
181
- prof_call_tree_t* call_tree_table_lookup(st_table* table, st_data_t key)
182
- {
183
- st_data_t val;
184
- if (rb_st_lookup(table, (st_data_t)key, &val))
185
- {
186
- return (prof_call_tree_t*)val;
187
- }
188
- else
189
- {
190
- return NULL;
191
- }
192
- }
193
-
194
- uint32_t prof_call_figure_depth(prof_call_tree_t* call_tree_data)
195
- {
196
- uint32_t result = 0;
197
-
198
- while (call_tree_data->parent)
199
- {
200
- result++;
201
- call_tree_data = call_tree_data->parent;
202
- }
203
-
204
- return result;
205
- }
206
-
207
- void prof_call_tree_add_parent(prof_call_tree_t* self, prof_call_tree_t* parent)
208
- {
209
- prof_call_tree_add_child(parent, self);
210
- self->parent = parent;
211
- }
212
-
213
- void prof_call_tree_add_child(prof_call_tree_t* self, prof_call_tree_t* child)
214
- {
215
- call_tree_table_insert(self->children, child->method->key, child);
216
-
217
- // The child is now managed by C since its parent will free it
218
- child->owner = OWNER_C;
219
- }
220
-
221
- /* ======= RubyProf::CallTree ========*/
222
-
223
- /* call-seq:
224
- new(method_info) -> call_tree
225
-
226
- Creates a new CallTree instance. +Klass+ should be a reference to
227
- a Ruby class and +method_name+ a symbol identifying one of its instance methods.*/
228
- static VALUE prof_call_tree_initialize(VALUE self, VALUE method_info)
229
- {
230
- prof_call_tree_t* call_tree_ptr = prof_get_call_tree(self);
231
- call_tree_ptr->method = prof_get_method(method_info);
232
-
233
- return self;
234
- }
235
-
236
- /* call-seq:
237
- parent -> call_tree
238
-
239
- Returns the CallTree parent call_tree object (the method that called this method).*/
240
- static VALUE prof_call_tree_parent(VALUE self)
241
- {
242
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
243
- if (call_tree->parent)
244
- return prof_call_tree_wrap(call_tree->parent);
245
- else
246
- return Qnil;
247
- }
248
-
249
- /* call-seq:
250
- callees -> array
251
-
252
- Returns an array of call info objects that this method called (ie, children).*/
253
- static VALUE prof_call_tree_children(VALUE self)
254
- {
255
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
256
- VALUE result = rb_ary_new();
257
- rb_st_foreach(call_tree->children, prof_call_tree_collect_children, result);
258
- return result;
259
- }
260
-
261
- /* call-seq:
262
- add_child(call_tree) -> call_tree
263
-
264
- Adds the specified call_tree as a child. If the method represented by the call tree is
265
- already a child than a IndexError is thrown.
266
-
267
- The returned value is the added child*/
268
- static VALUE prof_call_tree_add_child_ruby(VALUE self, VALUE child)
269
- {
270
- prof_call_tree_t* parent_ptr = prof_get_call_tree(self);
271
- prof_call_tree_t* child_ptr = prof_get_call_tree(child);
272
-
273
- prof_call_tree_t* existing_ptr = call_tree_table_lookup(parent_ptr->children, child_ptr->method->key);
274
- if (existing_ptr)
275
- {
276
- rb_raise(rb_eIndexError, "Child call tree already exists");
277
- }
278
-
279
- prof_call_tree_add_parent(child_ptr, parent_ptr);
280
-
281
- return child;
282
- }
283
-
284
- /* call-seq:
285
- called -> MethodInfo
286
-
287
- Returns the target method. */
288
- static VALUE prof_call_tree_target(VALUE self)
289
- {
290
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
291
- return prof_method_wrap(call_tree->method);
292
- }
293
-
294
- /* call-seq:
295
- called -> Measurement
296
-
297
- Returns the measurement associated with this call_tree. */
298
- static VALUE prof_call_tree_measurement(VALUE self)
299
- {
300
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
301
- return prof_measurement_wrap(call_tree->measurement);
302
- }
303
-
304
- /* call-seq:
305
- depth -> int
306
-
307
- returns the depth of this call info in the call graph */
308
- static VALUE prof_call_tree_depth(VALUE self)
309
- {
310
- prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
311
- uint32_t depth = prof_call_figure_depth(call_tree_data);
312
- return rb_int_new(depth);
313
- }
314
-
315
- /* call-seq:
316
- source_file => string
317
-
318
- return the source file of the method
319
- */
320
- static VALUE prof_call_tree_source_file(VALUE self)
321
- {
322
- prof_call_tree_t* result = prof_get_call_tree(self);
323
- return result->source_file;
324
- }
325
-
326
- /* call-seq:
327
- line_no -> int
328
-
329
- returns the line number of the method */
330
- static VALUE prof_call_tree_line(VALUE self)
331
- {
332
- prof_call_tree_t* result = prof_get_call_tree(self);
333
- return INT2FIX(result->source_line);
334
- }
335
-
336
- static int prof_call_tree_merge_children(st_data_t key, st_data_t value, st_data_t data)
337
- {
338
- prof_call_tree_t* other_child = (prof_call_tree_t*)value;
339
- prof_call_tree_t* self = (prof_call_tree_t*)data;
340
-
341
- st_data_t self_child;
342
- if (rb_st_lookup(self->children, other_child->method->key, &self_child))
343
- {
344
- prof_call_tree_merge_internal((prof_call_tree_t*)self_child, other_child);
345
- }
346
- else
347
- {
348
- prof_call_tree_t* copy = prof_call_tree_copy(other_child);
349
- prof_call_tree_add_child(self, copy);
350
- }
351
- return ST_CONTINUE;
352
- }
353
-
354
- void prof_call_tree_merge_internal(prof_call_tree_t* self, prof_call_tree_t* other)
355
- {
356
- // Make sure the methods are the same
357
- if (self->method->key != other->method->key)
358
- return;
359
-
360
- // Make sure the parents are the same.
361
- // 1. They can both be set and be equal
362
- // 2. They can both be unset (null)
363
- if (self->parent && other->parent)
364
- {
365
- if (self->parent->method->key != other->parent->method->key)
366
- return;
367
- }
368
- else if (self->parent || other->parent)
369
- {
370
- return;
371
- }
372
-
373
- prof_measurement_merge_internal(self->measurement, other->measurement);
374
- prof_measurement_merge_internal(self->method->measurement, other->method->measurement);
375
-
376
- rb_st_foreach(other->children, prof_call_tree_merge_children, (st_data_t)self);
377
- }
378
-
379
- VALUE prof_call_tree_merge(VALUE self, VALUE other)
380
- {
381
- prof_call_tree_t* source = prof_get_call_tree(self);
382
- prof_call_tree_t* destination = prof_get_call_tree(other);
383
- prof_call_tree_merge_internal(source, destination);
384
- return other;
385
- }
386
-
387
- /* :nodoc: */
388
- static VALUE prof_call_tree_dump(VALUE self)
389
- {
390
- prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
391
- VALUE result = rb_hash_new();
392
-
393
- rb_hash_aset(result, ID2SYM(rb_intern("owner")), INT2FIX(call_tree_data->owner));
394
-
395
- rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(call_tree_data->measurement));
396
-
397
- rb_hash_aset(result, ID2SYM(rb_intern("source_file")), call_tree_data->source_file);
398
- rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(call_tree_data->source_line));
399
-
400
- rb_hash_aset(result, ID2SYM(rb_intern("parent")), prof_call_tree_parent(self));
401
- rb_hash_aset(result, ID2SYM(rb_intern("children")), prof_call_tree_children(self));
402
- rb_hash_aset(result, ID2SYM(rb_intern("target")), prof_call_tree_target(self));
403
-
404
- return result;
405
- }
406
-
407
- /* :nodoc: */
408
- static VALUE prof_call_tree_load(VALUE self, VALUE data)
409
- {
410
- VALUE target = Qnil;
411
- VALUE parent = Qnil;
412
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
413
- call_tree->object = self;
414
-
415
- call_tree->owner = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("owner"))));
416
-
417
- VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
418
- call_tree->measurement = prof_get_measurement(measurement);
419
-
420
- call_tree->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
421
- call_tree->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
422
-
423
- parent = rb_hash_aref(data, ID2SYM(rb_intern("parent")));
424
- if (parent != Qnil)
425
- call_tree->parent = prof_get_call_tree(parent);
426
-
427
- VALUE callees = rb_hash_aref(data, ID2SYM(rb_intern("children")));
428
- for (int i = 0; i < rb_array_len(callees); i++)
429
- {
430
- VALUE call_tree_object = rb_ary_entry(callees, i);
431
- prof_call_tree_t* call_tree_data = prof_get_call_tree(call_tree_object);
432
-
433
- st_data_t key = call_tree_data->method ? call_tree_data->method->key : method_key(Qnil, 0);
434
- call_tree_table_insert(call_tree->children, key, call_tree_data);
435
- }
436
-
437
- target = rb_hash_aref(data, ID2SYM(rb_intern("target")));
438
- call_tree->method = prof_get_method(target);
439
-
440
- return data;
441
- }
442
-
443
- void rp_init_call_tree()
444
- {
445
- /* CallTree */
446
- cRpCallTree = rb_define_class_under(mProf, "CallTree", rb_cObject);
447
- rb_define_alloc_func(cRpCallTree, prof_call_tree_allocate);
448
- rb_define_method(cRpCallTree, "initialize", prof_call_tree_initialize, 1);
449
-
450
- rb_define_method(cRpCallTree, "target", prof_call_tree_target, 0);
451
- rb_define_method(cRpCallTree, "measurement", prof_call_tree_measurement, 0);
452
- rb_define_method(cRpCallTree, "parent", prof_call_tree_parent, 0);
453
- rb_define_method(cRpCallTree, "children", prof_call_tree_children, 0);
454
- rb_define_method(cRpCallTree, "add_child", prof_call_tree_add_child_ruby, 1);
455
-
456
- rb_define_method(cRpCallTree, "depth", prof_call_tree_depth, 0);
457
- rb_define_method(cRpCallTree, "source_file", prof_call_tree_source_file, 0);
458
- rb_define_method(cRpCallTree, "line", prof_call_tree_line, 0);
459
-
460
- rb_define_method(cRpCallTree, "merge!", prof_call_tree_merge, 1);
461
-
462
- rb_define_method(cRpCallTree, "_dump_data", prof_call_tree_dump, 0);
463
- rb_define_method(cRpCallTree, "_load_data", prof_call_tree_load, 1);
464
- }
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
+ }