ruby-prof 1.4.3 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +59 -9
  3. data/{README.rdoc → README.md} +2 -2
  4. data/Rakefile +4 -4
  5. data/bin/ruby-prof +100 -87
  6. data/ext/ruby_prof/rp_allocation.c +140 -85
  7. data/ext/ruby_prof/rp_allocation.h +8 -6
  8. data/ext/ruby_prof/rp_call_tree.c +502 -369
  9. data/ext/ruby_prof/rp_call_tree.h +47 -43
  10. data/ext/ruby_prof/rp_call_trees.c +16 -8
  11. data/ext/ruby_prof/rp_measure_allocations.c +10 -13
  12. data/ext/ruby_prof/rp_measure_memory.c +8 -4
  13. data/ext/ruby_prof/rp_measure_process_time.c +7 -6
  14. data/ext/ruby_prof/rp_measurement.c +147 -20
  15. data/ext/ruby_prof/rp_measurement.h +4 -1
  16. data/ext/ruby_prof/rp_method.c +142 -83
  17. data/ext/ruby_prof/rp_method.h +63 -62
  18. data/ext/ruby_prof/rp_profile.c +933 -900
  19. data/ext/ruby_prof/rp_profile.h +1 -0
  20. data/ext/ruby_prof/rp_thread.c +433 -362
  21. data/ext/ruby_prof/rp_thread.h +39 -39
  22. data/ext/ruby_prof/ruby_prof.c +0 -2
  23. data/ext/ruby_prof/ruby_prof.h +8 -0
  24. data/ext/ruby_prof/vc/ruby_prof.vcxproj +11 -8
  25. data/lib/ruby-prof/assets/call_stack_printer.html.erb +2 -1
  26. data/lib/ruby-prof/compatibility.rb +14 -0
  27. data/lib/ruby-prof/method_info.rb +8 -1
  28. data/lib/ruby-prof/printers/abstract_printer.rb +2 -1
  29. data/lib/ruby-prof/printers/call_tree_printer.rb +4 -10
  30. data/lib/ruby-prof/printers/graph_html_printer.rb +1 -1
  31. data/lib/ruby-prof/printers/multi_printer.rb +17 -17
  32. data/lib/ruby-prof/profile.rb +70 -37
  33. data/lib/ruby-prof/rack.rb +31 -21
  34. data/lib/ruby-prof/version.rb +1 -1
  35. data/lib/ruby-prof.rb +1 -1
  36. data/ruby-prof.gemspec +2 -3
  37. data/test/abstract_printer_test.rb +1 -0
  38. data/test/alias_test.rb +97 -106
  39. data/test/call_tree_builder.rb +126 -0
  40. data/test/call_tree_test.rb +94 -0
  41. data/test/call_tree_visitor_test.rb +1 -6
  42. data/test/call_trees_test.rb +6 -6
  43. data/test/{basic_test.rb → compatibility_test.rb} +8 -2
  44. data/test/duplicate_names_test.rb +5 -5
  45. data/test/dynamic_method_test.rb +24 -15
  46. data/test/enumerable_test.rb +1 -1
  47. data/test/exceptions_test.rb +2 -2
  48. data/test/exclude_methods_test.rb +3 -8
  49. data/test/exclude_threads_test.rb +4 -9
  50. data/test/fiber_test.rb +74 -8
  51. data/test/gc_test.rb +11 -9
  52. data/test/inverse_call_tree_test.rb +33 -34
  53. data/test/line_number_test.rb +37 -61
  54. data/test/marshal_test.rb +16 -3
  55. data/test/measure_allocations.rb +1 -5
  56. data/test/measure_allocations_test.rb +642 -357
  57. data/test/{measure_memory_trace_test.rb → measure_memory_test.rb} +180 -616
  58. data/test/measure_process_time_test.rb +1566 -741
  59. data/test/measure_wall_time_test.rb +179 -193
  60. data/test/measurement_test.rb +82 -0
  61. data/test/merge_test.rb +146 -0
  62. data/test/method_info_test.rb +95 -0
  63. data/test/multi_printer_test.rb +0 -5
  64. data/test/no_method_class_test.rb +1 -1
  65. data/test/pause_resume_test.rb +12 -16
  66. data/test/printer_call_stack_test.rb +2 -2
  67. data/test/printer_call_tree_test.rb +4 -4
  68. data/test/printer_flat_test.rb +1 -1
  69. data/test/printer_graph_html_test.rb +2 -2
  70. data/test/printer_graph_test.rb +2 -2
  71. data/test/printers_test.rb +14 -20
  72. data/test/printing_recursive_graph_test.rb +2 -2
  73. data/test/profile_test.rb +85 -0
  74. data/test/recursive_test.rb +374 -155
  75. data/test/scheduler.rb +363 -0
  76. data/test/singleton_test.rb +1 -1
  77. data/test/stack_printer_test.rb +5 -8
  78. data/test/start_stop_test.rb +11 -14
  79. data/test/test_helper.rb +11 -8
  80. data/test/thread_test.rb +106 -15
  81. data/test/unique_call_path_test.rb +28 -12
  82. data/test/yarv_test.rb +11 -7
  83. metadata +17 -29
  84. data/ext/ruby_prof/rp_aggregate_call_tree.c +0 -59
  85. data/ext/ruby_prof/rp_aggregate_call_tree.h +0 -13
  86. data/test/measure_allocations_trace_test.rb +0 -375
  87. data/test/temp.rb +0 -20
@@ -1,369 +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
- #define INITIAL_CALL_TREES_SIZE 2
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->method = method;
15
- result->parent = parent;
16
- result->object = Qnil;
17
- result->visits = 0;
18
- result->source_line = source_line;
19
- result->source_file = source_file;
20
- result->children = rb_st_init_numtable();
21
- result->measurement = prof_measurement_create();
22
-
23
- return result;
24
- }
25
-
26
- prof_call_tree_t* prof_call_tree_copy(prof_call_tree_t* other)
27
- {
28
- prof_call_tree_t* result = ALLOC(prof_call_tree_t);
29
- result->children = rb_st_init_numtable();
30
- result->object = Qnil;
31
- result->visits = 0;
32
-
33
- result->method = other->method;
34
- result->parent = other->parent;
35
- result->source_line = other->source_line;
36
- result->source_file = other->source_file;
37
-
38
- result->measurement = prof_measurement_create();
39
- result->measurement->called = other->measurement->called;
40
- result->measurement->total_time = other->measurement->total_time;
41
- result->measurement->self_time = other->measurement->self_time;
42
- result->measurement->wait_time = other->measurement->wait_time;
43
- result->measurement->object = Qnil;
44
-
45
- return result;
46
- }
47
-
48
- void prof_call_tree_merge(prof_call_tree_t* result, prof_call_tree_t* other)
49
- {
50
- result->measurement->called += other->measurement->called;
51
- result->measurement->total_time += other->measurement->total_time;
52
- result->measurement->self_time += other->measurement->self_time;
53
- result->measurement->wait_time += other->measurement->wait_time;
54
- }
55
-
56
- static int prof_call_tree_collect_children(st_data_t key, st_data_t value, st_data_t result)
57
- {
58
- prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
59
- VALUE arr = (VALUE)result;
60
- rb_ary_push(arr, prof_call_tree_wrap(call_tree));
61
- return ST_CONTINUE;
62
- }
63
-
64
- static int prof_call_tree_mark_children(st_data_t key, st_data_t value, st_data_t data)
65
- {
66
- prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
67
- rb_st_foreach(call_tree->children, prof_call_tree_mark_children, data);
68
- prof_call_tree_mark(call_tree);
69
- return ST_CONTINUE;
70
- }
71
-
72
- void prof_call_tree_mark(void* data)
73
- {
74
- if (!data)
75
- return;
76
-
77
- prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
78
-
79
- if (call_tree->object != Qnil)
80
- rb_gc_mark(call_tree->object);
81
-
82
- if (call_tree->source_file != Qnil)
83
- rb_gc_mark(call_tree->source_file);
84
-
85
- prof_method_mark(call_tree->method);
86
- prof_measurement_mark(call_tree->measurement);
87
-
88
- // Recurse down through the whole call tree but only from the top node
89
- // to avoid calling mark over and over and over.
90
- if (!call_tree->parent)
91
- rb_st_foreach(call_tree->children, prof_call_tree_mark_children, 0);
92
- }
93
-
94
- static void prof_call_tree_ruby_gc_free(void* data)
95
- {
96
- if (data)
97
- {
98
- prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
99
- call_tree->object = Qnil;
100
- }
101
- }
102
-
103
- static int prof_call_tree_free_children(st_data_t key, st_data_t value, st_data_t data)
104
- {
105
- prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
106
- prof_call_tree_free(call_tree);
107
- return ST_CONTINUE;
108
- }
109
-
110
- void prof_call_tree_free(prof_call_tree_t* call_tree_data)
111
- {
112
- /* Has this call info object been accessed by Ruby? If
113
- yes clean it up so to avoid a segmentation fault. */
114
- if (call_tree_data->object != Qnil)
115
- {
116
- RTYPEDDATA(call_tree_data->object)->data = NULL;
117
- call_tree_data->object = Qnil;
118
- }
119
-
120
- // Free children
121
- rb_st_foreach(call_tree_data->children, prof_call_tree_free_children, 0);
122
- rb_st_free_table(call_tree_data->children);
123
-
124
- // Free measurement
125
- prof_measurement_free(call_tree_data->measurement);
126
-
127
- // Finally free self
128
- xfree(call_tree_data);
129
- }
130
-
131
- size_t prof_call_tree_size(const void* data)
132
- {
133
- return sizeof(prof_call_tree_t);
134
- }
135
-
136
- static const rb_data_type_t call_tree_type =
137
- {
138
- .wrap_struct_name = "CallTree",
139
- .function =
140
- {
141
- .dmark = prof_call_tree_mark,
142
- .dfree = prof_call_tree_ruby_gc_free,
143
- .dsize = prof_call_tree_size,
144
- },
145
- .data = NULL,
146
- .flags = RUBY_TYPED_FREE_IMMEDIATELY
147
- };
148
-
149
- VALUE prof_call_tree_wrap(prof_call_tree_t* call_tree)
150
- {
151
- if (call_tree->object == Qnil)
152
- {
153
- call_tree->object = TypedData_Wrap_Struct(cRpCallTree, &call_tree_type, call_tree);
154
- }
155
- return call_tree->object;
156
- }
157
-
158
- static VALUE prof_call_tree_allocate(VALUE klass)
159
- {
160
- prof_call_tree_t* call_tree = prof_call_tree_create(NULL, NULL, Qnil, 0);
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_figure_depth(prof_call_tree_t* call_tree_data)
197
- {
198
- uint32_t result = 0;
199
-
200
- while (call_tree_data->parent)
201
- {
202
- result++;
203
- call_tree_data = call_tree_data->parent;
204
- }
205
-
206
- return result;
207
- }
208
-
209
- void prof_call_tree_add_parent(prof_call_tree_t* self, prof_call_tree_t* parent)
210
- {
211
- prof_call_tree_add_child(parent, self);
212
- self->parent = parent;
213
- }
214
-
215
- void prof_call_tree_add_child(prof_call_tree_t* self, prof_call_tree_t* child)
216
- {
217
- call_tree_table_insert(self->children, child->method->key, child);
218
- }
219
-
220
- /* ======= RubyProf::CallTree ========*/
221
-
222
- /* call-seq:
223
- parent -> call_tree
224
-
225
- Returns the CallTree parent call_tree object (the method that called this method).*/
226
- static VALUE prof_call_tree_parent(VALUE self)
227
- {
228
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
229
- if (call_tree->parent)
230
- return prof_call_tree_wrap(call_tree->parent);
231
- else
232
- return Qnil;
233
- }
234
-
235
- /* call-seq:
236
- callees -> array
237
-
238
- Returns an array of call info objects that this method called (ie, children).*/
239
- static VALUE prof_call_tree_children(VALUE self)
240
- {
241
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
242
- VALUE result = rb_ary_new();
243
- rb_st_foreach(call_tree->children, prof_call_tree_collect_children, result);
244
- return result;
245
- }
246
-
247
- /* call-seq:
248
- called -> MethodInfo
249
-
250
- Returns the target method. */
251
- static VALUE prof_call_tree_target(VALUE self)
252
- {
253
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
254
- return prof_method_wrap(call_tree->method);
255
- }
256
-
257
- /* call-seq:
258
- called -> Measurement
259
-
260
- Returns the measurement associated with this call_tree. */
261
- static VALUE prof_call_tree_measurement(VALUE self)
262
- {
263
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
264
- return prof_measurement_wrap(call_tree->measurement);
265
- }
266
-
267
- /* call-seq:
268
- depth -> int
269
-
270
- returns the depth of this call info in the call graph */
271
- static VALUE prof_call_tree_depth(VALUE self)
272
- {
273
- prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
274
- uint32_t depth = prof_call_figure_depth(call_tree_data);
275
- return rb_int_new(depth);
276
- }
277
-
278
- /* call-seq:
279
- source_file => string
280
-
281
- return the source file of the method
282
- */
283
- static VALUE prof_call_tree_source_file(VALUE self)
284
- {
285
- prof_call_tree_t* result = prof_get_call_tree(self);
286
- return result->source_file;
287
- }
288
-
289
- /* call-seq:
290
- line_no -> int
291
-
292
- returns the line number of the method */
293
- static VALUE prof_call_tree_line(VALUE self)
294
- {
295
- prof_call_tree_t* result = prof_get_call_tree(self);
296
- return INT2FIX(result->source_line);
297
- }
298
-
299
- /* :nodoc: */
300
- static VALUE prof_call_tree_dump(VALUE self)
301
- {
302
- prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
303
- VALUE result = rb_hash_new();
304
-
305
- rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(call_tree_data->measurement));
306
-
307
- rb_hash_aset(result, ID2SYM(rb_intern("source_file")), call_tree_data->source_file);
308
- rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(call_tree_data->source_line));
309
-
310
- rb_hash_aset(result, ID2SYM(rb_intern("parent")), prof_call_tree_parent(self));
311
- rb_hash_aset(result, ID2SYM(rb_intern("children")), prof_call_tree_children(self));
312
- rb_hash_aset(result, ID2SYM(rb_intern("target")), prof_call_tree_target(self));
313
-
314
- return result;
315
- }
316
-
317
- /* :nodoc: */
318
- static VALUE prof_call_tree_load(VALUE self, VALUE data)
319
- {
320
- VALUE target = Qnil;
321
- VALUE parent = Qnil;
322
- prof_call_tree_t* call_tree = prof_get_call_tree(self);
323
- call_tree->object = self;
324
-
325
- VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
326
- call_tree->measurement = prof_get_measurement(measurement);
327
-
328
- call_tree->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
329
- call_tree->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
330
-
331
- parent = rb_hash_aref(data, ID2SYM(rb_intern("parent")));
332
- if (parent != Qnil)
333
- call_tree->parent = prof_get_call_tree(parent);
334
-
335
- VALUE callees = rb_hash_aref(data, ID2SYM(rb_intern("children")));
336
- for (int i = 0; i < rb_array_len(callees); i++)
337
- {
338
- VALUE call_tree_object = rb_ary_entry(callees, i);
339
- prof_call_tree_t* call_tree_data = prof_get_call_tree(call_tree_object);
340
-
341
- st_data_t key = call_tree_data->method ? call_tree_data->method->key : method_key(Qnil, 0);
342
- call_tree_table_insert(call_tree->children, key, call_tree_data);
343
- }
344
-
345
- target = rb_hash_aref(data, ID2SYM(rb_intern("target")));
346
- call_tree->method = prof_get_method(target);
347
-
348
- return data;
349
- }
350
-
351
- void rp_init_call_tree()
352
- {
353
- /* CallTree */
354
- cRpCallTree = rb_define_class_under(mProf, "CallTree", rb_cObject);
355
- rb_undef_method(CLASS_OF(cRpCallTree), "new");
356
- rb_define_alloc_func(cRpCallTree, prof_call_tree_allocate);
357
-
358
- rb_define_method(cRpCallTree, "parent", prof_call_tree_parent, 0);
359
- rb_define_method(cRpCallTree, "children", prof_call_tree_children, 0);
360
- rb_define_method(cRpCallTree, "target", prof_call_tree_target, 0);
361
- rb_define_method(cRpCallTree, "measurement", prof_call_tree_measurement, 0);
362
-
363
- rb_define_method(cRpCallTree, "depth", prof_call_tree_depth, 0);
364
- rb_define_method(cRpCallTree, "source_file", prof_call_tree_source_file, 0);
365
- rb_define_method(cRpCallTree, "line", prof_call_tree_line, 0);
366
-
367
- rb_define_method(cRpCallTree, "_dump_data", prof_call_tree_dump, 0);
368
- rb_define_method(cRpCallTree, "_load_data", prof_call_tree_load, 1);
369
- }
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
+ }