ruby-prof 0.18.0 → 1.2.0

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.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +44 -1
  3. data/LICENSE +2 -2
  4. data/README.rdoc +1 -483
  5. data/Rakefile +3 -6
  6. data/bin/ruby-prof +111 -128
  7. data/ext/ruby_prof/extconf.rb +6 -38
  8. data/ext/ruby_prof/rp_aggregate_call_tree.c +41 -0
  9. data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
  10. data/ext/ruby_prof/rp_allocation.c +259 -0
  11. data/ext/ruby_prof/rp_allocation.h +31 -0
  12. data/ext/ruby_prof/rp_call_tree.c +353 -0
  13. data/ext/ruby_prof/rp_call_tree.h +43 -0
  14. data/ext/ruby_prof/rp_call_trees.c +266 -0
  15. data/ext/ruby_prof/rp_call_trees.h +29 -0
  16. data/ext/ruby_prof/rp_measure_allocations.c +25 -51
  17. data/ext/ruby_prof/rp_measure_memory.c +21 -56
  18. data/ext/ruby_prof/rp_measure_process_time.c +37 -43
  19. data/ext/ruby_prof/rp_measure_wall_time.c +40 -21
  20. data/ext/ruby_prof/rp_measurement.c +221 -0
  21. data/ext/ruby_prof/rp_measurement.h +50 -0
  22. data/ext/ruby_prof/rp_method.c +279 -439
  23. data/ext/ruby_prof/rp_method.h +33 -45
  24. data/ext/ruby_prof/rp_profile.c +902 -0
  25. data/ext/ruby_prof/rp_profile.h +36 -0
  26. data/ext/ruby_prof/rp_stack.c +163 -132
  27. data/ext/ruby_prof/rp_stack.h +18 -28
  28. data/ext/ruby_prof/rp_thread.c +192 -124
  29. data/ext/ruby_prof/rp_thread.h +18 -8
  30. data/ext/ruby_prof/ruby_prof.c +36 -778
  31. data/ext/ruby_prof/ruby_prof.h +11 -45
  32. data/ext/ruby_prof/vc/ruby_prof.vcxproj +18 -12
  33. data/lib/ruby-prof.rb +4 -21
  34. data/lib/ruby-prof/assets/call_stack_printer.html.erb +710 -0
  35. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  36. data/lib/ruby-prof/assets/graph_printer.html.erb +355 -0
  37. data/lib/ruby-prof/call_tree.rb +57 -0
  38. data/lib/ruby-prof/call_tree_visitor.rb +36 -0
  39. data/lib/ruby-prof/compatibility.rb +37 -107
  40. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  41. data/lib/ruby-prof/measurement.rb +17 -0
  42. data/lib/ruby-prof/method_info.rb +47 -90
  43. data/lib/ruby-prof/printers/abstract_printer.rb +73 -50
  44. data/lib/ruby-prof/printers/call_info_printer.rb +24 -12
  45. data/lib/ruby-prof/printers/call_stack_printer.rb +66 -152
  46. data/lib/ruby-prof/printers/call_tree_printer.rb +20 -12
  47. data/lib/ruby-prof/printers/dot_printer.rb +5 -5
  48. data/lib/ruby-prof/printers/flat_printer.rb +6 -24
  49. data/lib/ruby-prof/printers/graph_html_printer.rb +6 -192
  50. data/lib/ruby-prof/printers/graph_printer.rb +11 -14
  51. data/lib/ruby-prof/printers/multi_printer.rb +66 -23
  52. data/lib/ruby-prof/profile.rb +10 -3
  53. data/lib/ruby-prof/thread.rb +5 -20
  54. data/lib/ruby-prof/version.rb +1 -1
  55. data/ruby-prof.gemspec +9 -2
  56. data/test/abstract_printer_test.rb +0 -27
  57. data/test/alias_test.rb +126 -0
  58. data/test/basic_test.rb +1 -86
  59. data/test/call_tree_visitor_test.rb +32 -0
  60. data/test/call_trees_test.rb +66 -0
  61. data/test/dynamic_method_test.rb +0 -2
  62. data/test/exclude_methods_test.rb +17 -12
  63. data/test/fiber_test.rb +214 -23
  64. data/test/gc_test.rb +105 -0
  65. data/test/inverse_call_tree_test.rb +175 -0
  66. data/test/line_number_test.rb +118 -40
  67. data/test/marshal_test.rb +115 -0
  68. data/test/measure_allocations.rb +30 -0
  69. data/test/measure_allocations_test.rb +361 -12
  70. data/test/measure_allocations_trace_test.rb +375 -0
  71. data/test/measure_memory_trace_test.rb +1101 -0
  72. data/test/measure_process_time_test.rb +757 -33
  73. data/test/measure_times.rb +56 -0
  74. data/test/measure_wall_time_test.rb +329 -149
  75. data/test/multi_printer_test.rb +1 -34
  76. data/test/pause_resume_test.rb +24 -15
  77. data/test/prime.rb +1 -1
  78. data/test/prime_script.rb +6 -0
  79. data/test/printer_call_stack_test.rb +28 -0
  80. data/test/printer_call_tree_test.rb +31 -0
  81. data/test/printer_flat_test.rb +68 -0
  82. data/test/printer_graph_html_test.rb +60 -0
  83. data/test/printer_graph_test.rb +41 -0
  84. data/test/printers_test.rb +32 -166
  85. data/test/printing_recursive_graph_test.rb +26 -72
  86. data/test/recursive_test.rb +68 -77
  87. data/test/stack_printer_test.rb +2 -15
  88. data/test/start_stop_test.rb +22 -25
  89. data/test/test_helper.rb +6 -261
  90. data/test/thread_test.rb +11 -54
  91. data/test/unique_call_path_test.rb +25 -107
  92. data/test/yarv_test.rb +1 -0
  93. metadata +43 -41
  94. data/examples/flat.txt +0 -50
  95. data/examples/graph.dot +0 -84
  96. data/examples/graph.html +0 -823
  97. data/examples/graph.txt +0 -139
  98. data/examples/multi.flat.txt +0 -23
  99. data/examples/multi.graph.html +0 -760
  100. data/examples/multi.grind.dat +0 -114
  101. data/examples/multi.stack.html +0 -547
  102. data/examples/stack.html +0 -547
  103. data/ext/ruby_prof/rp_call_info.c +0 -425
  104. data/ext/ruby_prof/rp_call_info.h +0 -53
  105. data/ext/ruby_prof/rp_measure.c +0 -40
  106. data/ext/ruby_prof/rp_measure.h +0 -45
  107. data/ext/ruby_prof/rp_measure_cpu_time.c +0 -136
  108. data/ext/ruby_prof/rp_measure_gc_runs.c +0 -73
  109. data/ext/ruby_prof/rp_measure_gc_time.c +0 -60
  110. data/lib/ruby-prof/aggregate_call_info.rb +0 -76
  111. data/lib/ruby-prof/assets/call_stack_printer.css.html +0 -117
  112. data/lib/ruby-prof/assets/call_stack_printer.js.html +0 -385
  113. data/lib/ruby-prof/call_info.rb +0 -115
  114. data/lib/ruby-prof/call_info_visitor.rb +0 -40
  115. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +0 -83
  116. data/lib/ruby-prof/profile/exclude_common_methods.rb +0 -207
  117. data/lib/ruby-prof/profile/legacy_method_elimination.rb +0 -50
  118. data/test/aggregate_test.rb +0 -136
  119. data/test/block_test.rb +0 -74
  120. data/test/call_info_test.rb +0 -78
  121. data/test/call_info_visitor_test.rb +0 -31
  122. data/test/issue137_test.rb +0 -63
  123. data/test/measure_cpu_time_test.rb +0 -212
  124. data/test/measure_gc_runs_test.rb +0 -32
  125. data/test/measure_gc_time_test.rb +0 -36
  126. data/test/measure_memory_test.rb +0 -33
  127. data/test/method_elimination_test.rb +0 -84
  128. data/test/module_test.rb +0 -45
  129. data/test/stack_test.rb +0 -138
@@ -0,0 +1,13 @@
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
+ #ifndef __RP_AGGREGATE_CALL_TREE_H__
5
+ #define __RP_AGGREGATE_CALL_TREE_H__
6
+
7
+ #include "ruby_prof.h"
8
+ #include "rp_call_tree.h"
9
+
10
+ void rp_init_aggregate_call_tree(void);
11
+ VALUE prof_aggregate_call_tree_wrap(prof_call_tree_t* call_tree);
12
+
13
+ #endif //__RP_AGGREGATE_CALL_TREE_H__
@@ -0,0 +1,259 @@
1
+ /* Copyright (C) 2005-2013 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_allocation.h"
5
+
6
+ VALUE cRpAllocation;
7
+
8
+ prof_allocation_t* allocations_table_lookup(st_table* table, st_data_t key)
9
+ {
10
+ prof_allocation_t* result = NULL;
11
+ st_data_t value;
12
+ if (rb_st_lookup(table, key, &value))
13
+ {
14
+ result = (prof_allocation_t*)value;
15
+ }
16
+
17
+ return result;
18
+ }
19
+
20
+ void allocations_table_insert(st_table* table, st_data_t key, prof_allocation_t* allocation)
21
+ {
22
+ rb_st_insert(table, (st_data_t)key, (st_data_t)allocation);
23
+ }
24
+
25
+ st_data_t allocations_key(VALUE klass, int source_line)
26
+ {
27
+ return (klass << 4) + source_line;
28
+ }
29
+
30
+ /* ====== prof_allocation_t ====== */
31
+ prof_allocation_t* prof_allocation_create(void)
32
+ {
33
+ prof_allocation_t* result = ALLOC(prof_allocation_t);
34
+ result->count = 0;
35
+ result->klass = Qnil;
36
+ result->klass_name = Qnil;
37
+ result->object = Qnil;
38
+ result->memory = 0;
39
+ result->source_line = 0;
40
+ result->source_file = Qnil;
41
+ result->key = 0;
42
+
43
+ return result;
44
+ }
45
+
46
+ prof_allocation_t* prof_allocate_increment(prof_method_t* method, rb_trace_arg_t* trace_arg)
47
+ {
48
+ VALUE object = rb_tracearg_object(trace_arg);
49
+ if (BUILTIN_TYPE(object) == T_IMEMO)
50
+ return NULL;
51
+
52
+ VALUE klass = rb_obj_class(object);
53
+
54
+ int source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
55
+ st_data_t key = allocations_key(klass, source_line);
56
+
57
+ prof_allocation_t* allocation = allocations_table_lookup(method->allocations_table, key);
58
+ if (!allocation)
59
+ {
60
+ allocation = prof_allocation_create();
61
+ allocation->source_line = source_line;
62
+ allocation->source_file = rb_tracearg_path(trace_arg);
63
+ allocation->klass_flags = 0;
64
+ allocation->klass = resolve_klass(klass, &allocation->klass_flags);
65
+
66
+ allocation->key = key;
67
+ allocations_table_insert(method->allocations_table, key, allocation);
68
+ }
69
+
70
+ allocation->count++;
71
+ allocation->memory += rb_obj_memsize_of(object);
72
+
73
+ return allocation;
74
+ }
75
+
76
+ static void prof_allocation_ruby_gc_free(void* data)
77
+ {
78
+ prof_allocation_t* allocation = (prof_allocation_t*)data;
79
+ allocation->object = Qnil;
80
+ }
81
+
82
+ void prof_allocation_free(prof_allocation_t* allocation)
83
+ {
84
+ /* Has this allocation object been accessed by Ruby? If
85
+ yes clean it up so to avoid a segmentation fault. */
86
+ if (allocation->object != Qnil)
87
+ {
88
+ RDATA(allocation->object)->dmark = NULL;
89
+ RDATA(allocation->object)->dfree = NULL;
90
+ RDATA(allocation->object)->data = NULL;
91
+ allocation->object = Qnil;
92
+ }
93
+
94
+ xfree(allocation);
95
+ }
96
+
97
+ size_t prof_allocation_size(const void* data)
98
+ {
99
+ return sizeof(prof_allocation_t);
100
+ }
101
+
102
+ void prof_allocation_mark(void* data)
103
+ {
104
+ prof_allocation_t* allocation = (prof_allocation_t*)data;
105
+ if (allocation->object != Qnil)
106
+ rb_gc_mark(allocation->object);
107
+
108
+ if (allocation->klass != Qnil)
109
+ rb_gc_mark(allocation->klass);
110
+
111
+ if (allocation->klass_name != Qnil)
112
+ rb_gc_mark(allocation->klass_name);
113
+
114
+ if (allocation->source_file != Qnil)
115
+ rb_gc_mark(allocation->source_file);
116
+ }
117
+
118
+ VALUE prof_allocation_wrap(prof_allocation_t* allocation)
119
+ {
120
+ if (allocation->object == Qnil)
121
+ {
122
+ allocation->object = Data_Wrap_Struct(cRpAllocation, prof_allocation_mark, prof_allocation_ruby_gc_free, allocation);
123
+ }
124
+ return allocation->object;
125
+ }
126
+
127
+ static VALUE prof_allocation_allocate(VALUE klass)
128
+ {
129
+ prof_allocation_t* allocation = prof_allocation_create();
130
+ allocation->object = prof_allocation_wrap(allocation);
131
+ return allocation->object;
132
+ }
133
+
134
+ prof_allocation_t* prof_allocation_get(VALUE self)
135
+ {
136
+ /* Can't use Data_Get_Struct because that triggers the event hook
137
+ ending up in endless recursion. */
138
+ prof_allocation_t* result = DATA_PTR(self);
139
+ if (!result)
140
+ rb_raise(rb_eRuntimeError, "This RubyProf::Allocation instance has already been freed, likely because its profile has been freed.");
141
+
142
+ return result;
143
+ }
144
+
145
+ /* call-seq:
146
+ klass -> Class
147
+
148
+ Returns the type of Class being allocated. */
149
+ static VALUE prof_allocation_klass_name(VALUE self)
150
+ {
151
+ prof_allocation_t* allocation = prof_allocation_get(self);
152
+
153
+ if (allocation->klass_name == Qnil)
154
+ allocation->klass_name = resolve_klass_name(allocation->klass, &allocation->klass_flags);
155
+
156
+ return allocation->klass_name;
157
+ }
158
+
159
+ /* call-seq:
160
+ klass_flags -> integer
161
+
162
+ Returns the klass flags */
163
+
164
+ static VALUE prof_allocation_klass_flags(VALUE self)
165
+ {
166
+ prof_allocation_t* allocation = prof_allocation_get(self);
167
+ return INT2FIX(allocation->klass_flags);
168
+ }
169
+
170
+ /* call-seq:
171
+ source_file -> string
172
+
173
+ Returns the the line number where objects were allocated. */
174
+ static VALUE prof_allocation_source_file(VALUE self)
175
+ {
176
+ prof_allocation_t* allocation = prof_allocation_get(self);
177
+ return allocation->source_file;
178
+ }
179
+
180
+ /* call-seq:
181
+ line -> number
182
+
183
+ Returns the the line number where objects were allocated. */
184
+ static VALUE prof_allocation_source_line(VALUE self)
185
+ {
186
+ prof_allocation_t* allocation = prof_allocation_get(self);
187
+ return INT2FIX(allocation->source_line);
188
+ }
189
+
190
+ /* call-seq:
191
+ count -> number
192
+
193
+ Returns the number of times this class has been allocated. */
194
+ static VALUE prof_allocation_count(VALUE self)
195
+ {
196
+ prof_allocation_t* allocation = prof_allocation_get(self);
197
+ return INT2FIX(allocation->count);
198
+ }
199
+
200
+ /* call-seq:
201
+ memory -> number
202
+
203
+ Returns the amount of memory allocated. */
204
+ static VALUE prof_allocation_memory(VALUE self)
205
+ {
206
+ prof_allocation_t* allocation = prof_allocation_get(self);
207
+ return ULL2NUM(allocation->memory);
208
+ }
209
+
210
+ /* :nodoc: */
211
+ static VALUE prof_allocation_dump(VALUE self)
212
+ {
213
+ prof_allocation_t* allocation = DATA_PTR(self);
214
+
215
+ VALUE result = rb_hash_new();
216
+
217
+ rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(allocation->key));
218
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_allocation_klass_name(self));
219
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(allocation->klass_flags));
220
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), allocation->source_file);
221
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(allocation->source_line));
222
+ rb_hash_aset(result, ID2SYM(rb_intern("count")), INT2FIX(allocation->count));
223
+ rb_hash_aset(result, ID2SYM(rb_intern("memory")), LONG2FIX(allocation->memory));
224
+
225
+ return result;
226
+ }
227
+
228
+ /* :nodoc: */
229
+ static VALUE prof_allocation_load(VALUE self, VALUE data)
230
+ {
231
+ prof_allocation_t* allocation = DATA_PTR(self);
232
+ allocation->object = self;
233
+
234
+ allocation->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
235
+ allocation->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
236
+ allocation->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
237
+ allocation->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
238
+ allocation->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
239
+ allocation->count = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("count"))));
240
+ allocation->memory = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("memory"))));
241
+
242
+ return data;
243
+ }
244
+
245
+ void rp_init_allocation(void)
246
+ {
247
+ cRpAllocation = rb_define_class_under(mProf, "Allocation", rb_cData);
248
+ rb_undef_method(CLASS_OF(cRpAllocation), "new");
249
+ rb_define_alloc_func(cRpAllocation, prof_allocation_allocate);
250
+
251
+ rb_define_method(cRpAllocation, "klass_name", prof_allocation_klass_name, 0);
252
+ rb_define_method(cRpAllocation, "klass_flags", prof_allocation_klass_flags, 0);
253
+ rb_define_method(cRpAllocation, "source_file", prof_allocation_source_file, 0);
254
+ rb_define_method(cRpAllocation, "line", prof_allocation_source_line, 0);
255
+ rb_define_method(cRpAllocation, "count", prof_allocation_count, 0);
256
+ rb_define_method(cRpAllocation, "memory", prof_allocation_memory, 0);
257
+ rb_define_method(cRpAllocation, "_dump_data", prof_allocation_dump, 0);
258
+ rb_define_method(cRpAllocation, "_load_data", prof_allocation_load, 1);
259
+ }
@@ -0,0 +1,31 @@
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
+ #ifndef _RP_ALLOCATION_
5
+ #define _RP_ALLOCATION_
6
+
7
+ #include "ruby_prof.h"
8
+ #include "rp_method.h"
9
+
10
+ typedef struct
11
+ {
12
+ st_data_t key; /* Key in hash table */
13
+ unsigned int klass_flags; /* Information about the type of class */
14
+ VALUE klass; /* Klass that was created */
15
+ VALUE klass_name; /* Name of the class that was created */
16
+ VALUE source_file; /* Line number where allocation happens */
17
+ int source_line; /* Line number where allocation happens */
18
+ int count; /* Number of allocations */
19
+ size_t memory; /* Amount of allocated memory */
20
+ VALUE object; /* Cache to wrapped object */
21
+ } prof_allocation_t;
22
+
23
+ void rp_init_allocation(void);
24
+ void prof_allocation_free(prof_allocation_t* allocation);
25
+ void prof_allocation_mark(void* data);
26
+ VALUE prof_allocation_wrap(prof_allocation_t* allocation);
27
+ prof_allocation_t* prof_allocation_get(VALUE self);
28
+ prof_allocation_t* prof_allocate_increment(prof_method_t* method, rb_trace_arg_t* trace_arg);
29
+
30
+
31
+ #endif //_RP_ALLOCATION_
@@ -0,0 +1,353 @@
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->children = rb_st_init_numtable();
17
+ result->object = Qnil;
18
+ result->measurement = prof_measurement_create();
19
+
20
+ result->visits = 0;
21
+
22
+ result->source_line = source_line;
23
+ result->source_file = source_file;
24
+
25
+ return result;
26
+ }
27
+
28
+ prof_call_tree_t* prof_call_tree_copy(prof_call_tree_t* other)
29
+ {
30
+ prof_call_tree_t* result = ALLOC(prof_call_tree_t);
31
+ result->children = rb_st_init_numtable();
32
+ result->object = Qnil;
33
+ result->visits = 0;
34
+
35
+ result->method = other->method;
36
+ result->parent = other->parent;
37
+ result->source_line = other->source_line;
38
+ result->source_file = other->source_file;
39
+
40
+ result->measurement = prof_measurement_create();
41
+ result->measurement->called = other->measurement->called;
42
+ result->measurement->total_time = other->measurement->total_time;
43
+ result->measurement->self_time = other->measurement->self_time;
44
+ result->measurement->wait_time = other->measurement->wait_time;
45
+ result->measurement->object = Qnil;
46
+
47
+ return result;
48
+ }
49
+
50
+ void prof_call_tree_merge(prof_call_tree_t* result, prof_call_tree_t* other)
51
+ {
52
+ result->measurement->called += other->measurement->called;
53
+ result->measurement->total_time += other->measurement->total_time;
54
+ result->measurement->self_time += other->measurement->self_time;
55
+ result->measurement->wait_time += other->measurement->wait_time;
56
+ }
57
+
58
+ static int prof_call_tree_collect_children(st_data_t key, st_data_t value, st_data_t result)
59
+ {
60
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
61
+ VALUE arr = (VALUE)result;
62
+ rb_ary_push(arr, prof_call_tree_wrap(call_tree));
63
+ return ST_CONTINUE;
64
+ }
65
+
66
+ static int prof_call_tree_mark_children(st_data_t key, st_data_t value, st_data_t data)
67
+ {
68
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
69
+ rb_st_foreach(call_tree->children, prof_call_tree_mark_children, data);
70
+ prof_call_tree_mark(call_tree);
71
+ return ST_CONTINUE;
72
+ }
73
+
74
+ void prof_call_tree_mark(void* data)
75
+ {
76
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
77
+
78
+ if (call_tree->object != Qnil)
79
+ rb_gc_mark(call_tree->object);
80
+
81
+ if (call_tree->source_file != Qnil)
82
+ rb_gc_mark(call_tree->source_file);
83
+
84
+ prof_measurement_mark(call_tree->measurement);
85
+
86
+ // Recurse down through the whole call tree but only from the top node
87
+ // to avoid calling mark over and over and over.
88
+ if (!call_tree->parent)
89
+ rb_st_foreach(call_tree->children, prof_call_tree_mark_children, 0);
90
+ }
91
+
92
+ static void prof_call_tree_ruby_gc_free(void* data)
93
+ {
94
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
95
+ call_tree->object = Qnil;
96
+ }
97
+
98
+ static int prof_call_tree_free_children(st_data_t key, st_data_t value, st_data_t data)
99
+ {
100
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
101
+ prof_call_tree_free(call_tree);
102
+ return ST_CONTINUE;
103
+ }
104
+
105
+ void prof_call_tree_free(prof_call_tree_t* call_tree_data)
106
+ {
107
+ /* Has this call info object been accessed by Ruby? If
108
+ yes clean it up so to avoid a segmentation fault. */
109
+ if (call_tree_data->object != Qnil)
110
+ {
111
+ RDATA(call_tree_data->object)->dmark = NULL;
112
+ RDATA(call_tree_data->object)->dfree = NULL;
113
+ RDATA(call_tree_data->object)->data = NULL;
114
+ call_tree_data->object = Qnil;
115
+ }
116
+
117
+ // Free children
118
+ rb_st_foreach(call_tree_data->children, prof_call_tree_free_children, 0);
119
+ rb_st_free_table(call_tree_data->children);
120
+
121
+ // Free measurement
122
+ prof_measurement_free(call_tree_data->measurement);
123
+
124
+ // Finally free self
125
+ xfree(call_tree_data);
126
+ }
127
+
128
+ size_t prof_call_tree_size(const void* data)
129
+ {
130
+ return sizeof(prof_call_tree_t);
131
+ }
132
+
133
+ VALUE prof_call_tree_wrap(prof_call_tree_t* call_tree)
134
+ {
135
+ if (call_tree->object == Qnil)
136
+ {
137
+ call_tree->object = Data_Wrap_Struct(cRpCallTree, prof_call_tree_mark, prof_call_tree_ruby_gc_free, call_tree);
138
+ }
139
+ return call_tree->object;
140
+ }
141
+
142
+ static VALUE prof_call_tree_allocate(VALUE klass)
143
+ {
144
+ prof_call_tree_t* call_tree = prof_call_tree_create(NULL, NULL, Qnil, 0);
145
+ call_tree->object = prof_call_tree_wrap(call_tree);
146
+ return call_tree->object;
147
+ }
148
+
149
+ prof_call_tree_t* prof_get_call_tree(VALUE self)
150
+ {
151
+ /* Can't use Data_Get_Struct because that triggers the event hook
152
+ ending up in endless recursion. */
153
+ prof_call_tree_t* result = DATA_PTR(self);
154
+
155
+ if (!result)
156
+ rb_raise(rb_eRuntimeError, "This RubyProf::CallTree instance has already been freed, likely because its profile has been freed.");
157
+
158
+ return result;
159
+ }
160
+
161
+ /* ======= Call Tree Table ========*/
162
+ static size_t call_tree_table_insert(st_table* table, st_data_t key, prof_call_tree_t* val)
163
+ {
164
+ return rb_st_insert(table, (st_data_t)key, (st_data_t)val);
165
+ }
166
+
167
+ prof_call_tree_t* call_tree_table_lookup(st_table* table, st_data_t key)
168
+ {
169
+ st_data_t val;
170
+ if (rb_st_lookup(table, (st_data_t)key, &val))
171
+ {
172
+ return (prof_call_tree_t*)val;
173
+ }
174
+ else
175
+ {
176
+ return NULL;
177
+ }
178
+ }
179
+
180
+ uint32_t prof_call_figure_depth(prof_call_tree_t* call_tree_data)
181
+ {
182
+ uint32_t result = 0;
183
+
184
+ while (call_tree_data->parent)
185
+ {
186
+ result++;
187
+ call_tree_data = call_tree_data->parent;
188
+ }
189
+
190
+ return result;
191
+ }
192
+
193
+ void prof_call_tree_add_parent(prof_call_tree_t* self, prof_call_tree_t* parent)
194
+ {
195
+ prof_call_tree_add_child(parent, self);
196
+ self->parent = parent;
197
+ }
198
+
199
+ void prof_call_tree_add_child(prof_call_tree_t* self, prof_call_tree_t* child)
200
+ {
201
+ call_tree_table_insert(self->children, child->method->key, child);
202
+ }
203
+
204
+ /* ======= RubyProf::CallTree ========*/
205
+
206
+ /* call-seq:
207
+ parent -> call_tree
208
+
209
+ Returns the CallTree parent call_tree object (the method that called this method).*/
210
+ static VALUE prof_call_tree_parent(VALUE self)
211
+ {
212
+ prof_call_tree_t* call_tree = prof_get_call_tree(self);
213
+ if (call_tree->parent)
214
+ return prof_call_tree_wrap(call_tree->parent);
215
+ else
216
+ return Qnil;
217
+ }
218
+
219
+ /* call-seq:
220
+ callees -> array
221
+
222
+ Returns an array of call info objects that this method called (ie, children).*/
223
+ static VALUE prof_call_tree_children(VALUE self)
224
+ {
225
+ prof_call_tree_t* call_tree = prof_get_call_tree(self);
226
+ VALUE result = rb_ary_new();
227
+ rb_st_foreach(call_tree->children, prof_call_tree_collect_children, result);
228
+ return result;
229
+ }
230
+
231
+ /* call-seq:
232
+ called -> MethodInfo
233
+
234
+ Returns the target method. */
235
+ static VALUE prof_call_tree_target(VALUE self)
236
+ {
237
+ prof_call_tree_t* call_tree = prof_get_call_tree(self);
238
+ return prof_method_wrap(call_tree->method);
239
+ }
240
+
241
+ /* call-seq:
242
+ called -> Measurement
243
+
244
+ Returns the measurement associated with this call_tree. */
245
+ static VALUE prof_call_tree_measurement(VALUE self)
246
+ {
247
+ prof_call_tree_t* call_tree = prof_get_call_tree(self);
248
+ return prof_measurement_wrap(call_tree->measurement);
249
+ }
250
+
251
+ /* call-seq:
252
+ depth -> int
253
+
254
+ returns the depth of this call info in the call graph */
255
+ static VALUE prof_call_tree_depth(VALUE self)
256
+ {
257
+ prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
258
+ uint32_t depth = prof_call_figure_depth(call_tree_data);
259
+ return rb_int_new(depth);
260
+ }
261
+
262
+ /* call-seq:
263
+ source_file => string
264
+
265
+ return the source file of the method
266
+ */
267
+ static VALUE prof_call_tree_source_file(VALUE self)
268
+ {
269
+ prof_call_tree_t* result = prof_get_call_tree(self);
270
+ return result->source_file;
271
+ }
272
+
273
+ /* call-seq:
274
+ line_no -> int
275
+
276
+ returns the line number of the method */
277
+ static VALUE prof_call_tree_line(VALUE self)
278
+ {
279
+ prof_call_tree_t* result = prof_get_call_tree(self);
280
+ return INT2FIX(result->source_line);
281
+ }
282
+
283
+ /* :nodoc: */
284
+ static VALUE prof_call_tree_dump(VALUE self)
285
+ {
286
+ prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
287
+ VALUE result = rb_hash_new();
288
+
289
+ rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(call_tree_data->measurement));
290
+
291
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), call_tree_data->source_file);
292
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(call_tree_data->source_line));
293
+
294
+ rb_hash_aset(result, ID2SYM(rb_intern("parent")), prof_call_tree_parent(self));
295
+ rb_hash_aset(result, ID2SYM(rb_intern("children")), prof_call_tree_children(self));
296
+ rb_hash_aset(result, ID2SYM(rb_intern("target")), prof_call_tree_target(self));
297
+
298
+ return result;
299
+ }
300
+
301
+ /* :nodoc: */
302
+ static VALUE prof_call_tree_load(VALUE self, VALUE data)
303
+ {
304
+ VALUE target = Qnil;
305
+ VALUE parent = Qnil;
306
+ prof_call_tree_t* call_tree = prof_get_call_tree(self);
307
+ call_tree->object = self;
308
+
309
+ VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
310
+ call_tree->measurement = prof_get_measurement(measurement);
311
+
312
+ call_tree->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
313
+ call_tree->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
314
+
315
+ parent = rb_hash_aref(data, ID2SYM(rb_intern("parent")));
316
+ if (parent != Qnil)
317
+ call_tree->parent = prof_get_call_tree(parent);
318
+
319
+ VALUE callees = rb_hash_aref(data, ID2SYM(rb_intern("children")));
320
+ for (int i = 0; i < rb_array_len(callees); i++)
321
+ {
322
+ VALUE call_tree_object = rb_ary_entry(callees, i);
323
+ prof_call_tree_t* call_tree_data = prof_get_call_tree(call_tree_object);
324
+
325
+ st_data_t key = call_tree_data->method ? call_tree_data->method->key : method_key(Qnil, 0);
326
+ call_tree_table_insert(call_tree->children, key, call_tree_data);
327
+ }
328
+
329
+ target = rb_hash_aref(data, ID2SYM(rb_intern("target")));
330
+ call_tree->method = prof_get_method(target);
331
+
332
+ return data;
333
+ }
334
+
335
+ void rp_init_call_tree()
336
+ {
337
+ /* CallTree */
338
+ cRpCallTree = rb_define_class_under(mProf, "CallTree", rb_cData);
339
+ rb_undef_method(CLASS_OF(cRpCallTree), "new");
340
+ rb_define_alloc_func(cRpCallTree, prof_call_tree_allocate);
341
+
342
+ rb_define_method(cRpCallTree, "parent", prof_call_tree_parent, 0);
343
+ rb_define_method(cRpCallTree, "children", prof_call_tree_children, 0);
344
+ rb_define_method(cRpCallTree, "target", prof_call_tree_target, 0);
345
+ rb_define_method(cRpCallTree, "measurement", prof_call_tree_measurement, 0);
346
+
347
+ rb_define_method(cRpCallTree, "depth", prof_call_tree_depth, 0);
348
+ rb_define_method(cRpCallTree, "source_file", prof_call_tree_source_file, 0);
349
+ rb_define_method(cRpCallTree, "line", prof_call_tree_line, 0);
350
+
351
+ rb_define_method(cRpCallTree, "_dump_data", prof_call_tree_dump, 0);
352
+ rb_define_method(cRpCallTree, "_load_data", prof_call_tree_load, 1);
353
+ }