ruby-prof 1.5.0-x64-mingw-ucrt → 1.6.2-x64-mingw-ucrt
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES +19 -0
- data/bin/ruby-prof +105 -87
- data/ext/ruby_prof/rp_allocation.c +136 -81
- data/ext/ruby_prof/rp_allocation.h +8 -6
- data/ext/ruby_prof/rp_call_tree.c +502 -457
- data/ext/ruby_prof/rp_call_tree.h +47 -44
- data/ext/ruby_prof/rp_call_trees.c +1 -1
- data/ext/ruby_prof/rp_measurement.c +10 -3
- data/ext/ruby_prof/rp_method.c +86 -79
- data/ext/ruby_prof/rp_method.h +63 -62
- data/ext/ruby_prof/rp_profile.c +933 -948
- data/ext/ruby_prof/rp_profile.h +1 -0
- data/ext/ruby_prof/rp_thread.c +433 -410
- data/ext/ruby_prof/rp_thread.h +39 -39
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +6 -3
- data/lib/3.1/ruby_prof.so +0 -0
- data/lib/3.2/ruby_prof.so +0 -0
- data/lib/ruby-prof/compatibility.rb +14 -0
- data/lib/ruby-prof/printers/abstract_printer.rb +2 -1
- data/lib/ruby-prof/printers/call_tree_printer.rb +1 -1
- data/lib/ruby-prof/printers/multi_printer.rb +17 -17
- data/lib/ruby-prof/profile.rb +70 -70
- data/lib/ruby-prof/rack.rb +31 -21
- data/lib/ruby-prof/version.rb +1 -1
- data/test/abstract_printer_test.rb +1 -0
- data/test/alias_test.rb +6 -11
- data/test/call_tree_test.rb +94 -197
- data/test/call_tree_visitor_test.rb +1 -6
- data/test/call_trees_test.rb +2 -2
- data/test/{basic_test.rb → compatibility_test.rb} +8 -2
- data/test/duplicate_names_test.rb +1 -1
- data/test/dynamic_method_test.rb +1 -6
- data/test/enumerable_test.rb +1 -1
- data/test/exceptions_test.rb +2 -2
- data/test/exclude_methods_test.rb +3 -8
- data/test/exclude_threads_test.rb +4 -9
- data/test/fiber_test.rb +2 -58
- data/test/gc_test.rb +2 -2
- data/test/inverse_call_tree_test.rb +33 -34
- data/test/line_number_test.rb +1 -1
- data/test/marshal_test.rb +3 -3
- data/test/measure_allocations_test.rb +8 -17
- data/test/measure_memory_test.rb +3 -12
- data/test/measure_process_time_test.rb +32 -36
- data/test/measure_wall_time_test.rb +176 -181
- data/test/merge_test.rb +146 -0
- data/test/multi_printer_test.rb +0 -5
- data/test/no_method_class_test.rb +1 -1
- data/test/pause_resume_test.rb +12 -16
- data/test/printer_call_stack_test.rb +2 -2
- data/test/printer_call_tree_test.rb +2 -2
- data/test/printer_flat_test.rb +1 -1
- data/test/printer_graph_html_test.rb +2 -2
- data/test/printer_graph_test.rb +2 -2
- data/test/printers_test.rb +14 -20
- data/test/printing_recursive_graph_test.rb +2 -2
- data/test/recursive_test.rb +2 -7
- data/test/scheduler.rb +9 -0
- data/test/singleton_test.rb +1 -1
- data/test/stack_printer_test.rb +5 -8
- data/test/start_stop_test.rb +11 -14
- data/test/test_helper.rb +7 -0
- data/test/thread_test.rb +84 -19
- data/test/unique_call_path_test.rb +4 -4
- data/test/yarv_test.rb +3 -3
- 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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
result
|
14
|
-
result->
|
15
|
-
result->
|
16
|
-
result->
|
17
|
-
result->
|
18
|
-
result->
|
19
|
-
result->
|
20
|
-
result->
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
.
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
}
|
146
|
-
|
147
|
-
|
148
|
-
{
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
return call_tree->object;
|
154
|
-
}
|
155
|
-
|
156
|
-
|
157
|
-
{
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
{
|
202
|
-
|
203
|
-
|
204
|
-
}
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
}
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
}
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
return
|
398
|
-
}
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
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
|
+
}
|