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.
- checksums.yaml +4 -4
- data/CHANGES +44 -1
- data/LICENSE +2 -2
- data/README.rdoc +1 -483
- data/Rakefile +3 -6
- data/bin/ruby-prof +111 -128
- data/ext/ruby_prof/extconf.rb +6 -38
- data/ext/ruby_prof/rp_aggregate_call_tree.c +41 -0
- data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
- data/ext/ruby_prof/rp_allocation.c +259 -0
- data/ext/ruby_prof/rp_allocation.h +31 -0
- data/ext/ruby_prof/rp_call_tree.c +353 -0
- data/ext/ruby_prof/rp_call_tree.h +43 -0
- data/ext/ruby_prof/rp_call_trees.c +266 -0
- data/ext/ruby_prof/rp_call_trees.h +29 -0
- data/ext/ruby_prof/rp_measure_allocations.c +25 -51
- data/ext/ruby_prof/rp_measure_memory.c +21 -56
- data/ext/ruby_prof/rp_measure_process_time.c +37 -43
- data/ext/ruby_prof/rp_measure_wall_time.c +40 -21
- data/ext/ruby_prof/rp_measurement.c +221 -0
- data/ext/ruby_prof/rp_measurement.h +50 -0
- data/ext/ruby_prof/rp_method.c +279 -439
- data/ext/ruby_prof/rp_method.h +33 -45
- data/ext/ruby_prof/rp_profile.c +902 -0
- data/ext/ruby_prof/rp_profile.h +36 -0
- data/ext/ruby_prof/rp_stack.c +163 -132
- data/ext/ruby_prof/rp_stack.h +18 -28
- data/ext/ruby_prof/rp_thread.c +192 -124
- data/ext/ruby_prof/rp_thread.h +18 -8
- data/ext/ruby_prof/ruby_prof.c +36 -778
- data/ext/ruby_prof/ruby_prof.h +11 -45
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +18 -12
- data/lib/ruby-prof.rb +4 -21
- data/lib/ruby-prof/assets/call_stack_printer.html.erb +710 -0
- data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
- data/lib/ruby-prof/assets/graph_printer.html.erb +355 -0
- data/lib/ruby-prof/call_tree.rb +57 -0
- data/lib/ruby-prof/call_tree_visitor.rb +36 -0
- data/lib/ruby-prof/compatibility.rb +37 -107
- data/lib/ruby-prof/exclude_common_methods.rb +198 -0
- data/lib/ruby-prof/measurement.rb +17 -0
- data/lib/ruby-prof/method_info.rb +47 -90
- data/lib/ruby-prof/printers/abstract_printer.rb +73 -50
- data/lib/ruby-prof/printers/call_info_printer.rb +24 -12
- data/lib/ruby-prof/printers/call_stack_printer.rb +66 -152
- data/lib/ruby-prof/printers/call_tree_printer.rb +20 -12
- data/lib/ruby-prof/printers/dot_printer.rb +5 -5
- data/lib/ruby-prof/printers/flat_printer.rb +6 -24
- data/lib/ruby-prof/printers/graph_html_printer.rb +6 -192
- data/lib/ruby-prof/printers/graph_printer.rb +11 -14
- data/lib/ruby-prof/printers/multi_printer.rb +66 -23
- data/lib/ruby-prof/profile.rb +10 -3
- data/lib/ruby-prof/thread.rb +5 -20
- data/lib/ruby-prof/version.rb +1 -1
- data/ruby-prof.gemspec +9 -2
- data/test/abstract_printer_test.rb +0 -27
- data/test/alias_test.rb +126 -0
- data/test/basic_test.rb +1 -86
- data/test/call_tree_visitor_test.rb +32 -0
- data/test/call_trees_test.rb +66 -0
- data/test/dynamic_method_test.rb +0 -2
- data/test/exclude_methods_test.rb +17 -12
- data/test/fiber_test.rb +214 -23
- data/test/gc_test.rb +105 -0
- data/test/inverse_call_tree_test.rb +175 -0
- data/test/line_number_test.rb +118 -40
- data/test/marshal_test.rb +115 -0
- data/test/measure_allocations.rb +30 -0
- data/test/measure_allocations_test.rb +361 -12
- data/test/measure_allocations_trace_test.rb +375 -0
- data/test/measure_memory_trace_test.rb +1101 -0
- data/test/measure_process_time_test.rb +757 -33
- data/test/measure_times.rb +56 -0
- data/test/measure_wall_time_test.rb +329 -149
- data/test/multi_printer_test.rb +1 -34
- data/test/pause_resume_test.rb +24 -15
- data/test/prime.rb +1 -1
- data/test/prime_script.rb +6 -0
- data/test/printer_call_stack_test.rb +28 -0
- data/test/printer_call_tree_test.rb +31 -0
- data/test/printer_flat_test.rb +68 -0
- data/test/printer_graph_html_test.rb +60 -0
- data/test/printer_graph_test.rb +41 -0
- data/test/printers_test.rb +32 -166
- data/test/printing_recursive_graph_test.rb +26 -72
- data/test/recursive_test.rb +68 -77
- data/test/stack_printer_test.rb +2 -15
- data/test/start_stop_test.rb +22 -25
- data/test/test_helper.rb +6 -261
- data/test/thread_test.rb +11 -54
- data/test/unique_call_path_test.rb +25 -107
- data/test/yarv_test.rb +1 -0
- metadata +43 -41
- data/examples/flat.txt +0 -50
- data/examples/graph.dot +0 -84
- data/examples/graph.html +0 -823
- data/examples/graph.txt +0 -139
- data/examples/multi.flat.txt +0 -23
- data/examples/multi.graph.html +0 -760
- data/examples/multi.grind.dat +0 -114
- data/examples/multi.stack.html +0 -547
- data/examples/stack.html +0 -547
- data/ext/ruby_prof/rp_call_info.c +0 -425
- data/ext/ruby_prof/rp_call_info.h +0 -53
- data/ext/ruby_prof/rp_measure.c +0 -40
- data/ext/ruby_prof/rp_measure.h +0 -45
- data/ext/ruby_prof/rp_measure_cpu_time.c +0 -136
- data/ext/ruby_prof/rp_measure_gc_runs.c +0 -73
- data/ext/ruby_prof/rp_measure_gc_time.c +0 -60
- data/lib/ruby-prof/aggregate_call_info.rb +0 -76
- data/lib/ruby-prof/assets/call_stack_printer.css.html +0 -117
- data/lib/ruby-prof/assets/call_stack_printer.js.html +0 -385
- data/lib/ruby-prof/call_info.rb +0 -115
- data/lib/ruby-prof/call_info_visitor.rb +0 -40
- data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +0 -83
- data/lib/ruby-prof/profile/exclude_common_methods.rb +0 -207
- data/lib/ruby-prof/profile/legacy_method_elimination.rb +0 -50
- data/test/aggregate_test.rb +0 -136
- data/test/block_test.rb +0 -74
- data/test/call_info_test.rb +0 -78
- data/test/call_info_visitor_test.rb +0 -31
- data/test/issue137_test.rb +0 -63
- data/test/measure_cpu_time_test.rb +0 -212
- data/test/measure_gc_runs_test.rb +0 -32
- data/test/measure_gc_time_test.rb +0 -36
- data/test/measure_memory_test.rb +0 -33
- data/test/method_elimination_test.rb +0 -84
- data/test/module_test.rb +0 -45
- data/test/stack_test.rb +0 -138
data/ext/ruby_prof/rp_method.c
CHANGED
@@ -1,248 +1,174 @@
|
|
1
1
|
/* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
|
2
2
|
Please see the LICENSE file for copyright and distribution information */
|
3
3
|
|
4
|
-
#include "
|
4
|
+
#include "rp_allocation.h"
|
5
|
+
#include "rp_call_trees.h"
|
6
|
+
#include "rp_method.h"
|
5
7
|
|
6
|
-
|
7
|
-
#define RP_REL_SET(r, off) \
|
8
|
-
do { \
|
9
|
-
r |= (1 << (off)); \
|
10
|
-
} while (0)
|
11
|
-
|
12
|
-
VALUE cMethodInfo;
|
8
|
+
VALUE cRpMethodInfo;
|
13
9
|
|
14
10
|
/* ================ Helper Functions =================*/
|
15
|
-
|
16
|
-
figure_singleton_name(VALUE klass)
|
11
|
+
VALUE resolve_klass(VALUE klass, unsigned int* klass_flags)
|
17
12
|
{
|
18
|
-
|
19
|
-
volatile VALUE attached_str, super_str;
|
20
|
-
volatile VALUE result = Qnil;
|
21
|
-
|
22
|
-
/* We have come across a singleton object. First
|
23
|
-
figure out what it is attached to.*/
|
24
|
-
attached = rb_iv_get(klass, "__attached__");
|
13
|
+
VALUE result = klass;
|
25
14
|
|
26
|
-
|
27
|
-
if (BUILTIN_TYPE(attached) == T_CLASS)
|
15
|
+
if (klass == 0 || klass == Qnil)
|
28
16
|
{
|
29
|
-
|
30
|
-
result = rb_str_new2("<Class::");
|
31
|
-
rb_str_append(result, attached_str);
|
32
|
-
rb_str_cat2(result, ">");
|
17
|
+
result = Qnil;
|
33
18
|
}
|
34
|
-
|
35
|
-
/* Is this for singleton methods on a module? */
|
36
|
-
else if (BUILTIN_TYPE(attached) == T_MODULE)
|
19
|
+
else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
|
37
20
|
{
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
rb_str_cat2(result, ">");
|
42
|
-
}
|
21
|
+
/* We have come across a singleton object. First
|
22
|
+
figure out what it is attached to.*/
|
23
|
+
VALUE attached = rb_iv_get(klass, "__attached__");
|
43
24
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
25
|
+
/* Is this a singleton class acting as a metaclass? */
|
26
|
+
if (BUILTIN_TYPE(attached) == T_CLASS)
|
27
|
+
{
|
28
|
+
*klass_flags |= kClassSingleton;
|
29
|
+
result = attached;
|
30
|
+
}
|
31
|
+
/* Is this for singleton methods on a module? */
|
32
|
+
else if (BUILTIN_TYPE(attached) == T_MODULE)
|
33
|
+
{
|
34
|
+
*klass_flags |= kModuleSingleton;
|
35
|
+
result = attached;
|
36
|
+
}
|
37
|
+
/* Is this for singleton methods on an object? */
|
38
|
+
else if (BUILTIN_TYPE(attached) == T_OBJECT)
|
39
|
+
{
|
40
|
+
*klass_flags |= kObjectSingleton;
|
41
|
+
result = rb_class_superclass(klass);
|
42
|
+
}
|
43
|
+
/* Ok, this could be other things like an array made put onto
|
44
|
+
a singleton object (yeah, it happens, see the singleton
|
45
|
+
objects test case). */
|
46
|
+
else
|
47
|
+
{
|
48
|
+
*klass_flags |= kOtherSingleton;
|
49
|
+
result = klass;
|
50
|
+
}
|
55
51
|
}
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
else
|
52
|
+
/* Is this an include for a module? If so get the actual
|
53
|
+
module class since we want to combine all profiling
|
54
|
+
results for that module. */
|
55
|
+
else if (BUILTIN_TYPE(klass) == T_ICLASS)
|
61
56
|
{
|
62
|
-
|
57
|
+
unsigned int dummy;
|
58
|
+
*klass_flags |= kModuleIncludee;
|
59
|
+
result = resolve_klass(RBASIC(klass)->klass, &dummy);
|
63
60
|
}
|
64
|
-
|
65
61
|
return result;
|
66
62
|
}
|
67
63
|
|
68
|
-
|
69
|
-
klass_name(VALUE klass)
|
64
|
+
VALUE resolve_klass_name(VALUE klass, unsigned int* klass_flags)
|
70
65
|
{
|
71
|
-
|
66
|
+
VALUE result = Qnil;
|
72
67
|
|
73
|
-
if (klass ==
|
68
|
+
if (klass == Qnil)
|
74
69
|
{
|
75
70
|
result = rb_str_new2("[global]");
|
76
71
|
}
|
77
|
-
else if (
|
78
|
-
{
|
79
|
-
result = rb_class_name(klass);
|
80
|
-
}
|
81
|
-
else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
|
72
|
+
else if (*klass_flags & kOtherSingleton)
|
82
73
|
{
|
83
|
-
result =
|
84
|
-
}
|
85
|
-
else if (BUILTIN_TYPE(klass) == T_CLASS)
|
86
|
-
{
|
87
|
-
result = rb_class_name(klass);
|
74
|
+
result = rb_any_to_s(klass);
|
88
75
|
}
|
89
76
|
else
|
90
77
|
{
|
91
|
-
|
92
|
-
result = rb_str_new2("[unknown]");
|
78
|
+
result = rb_class_name(klass);
|
93
79
|
}
|
94
80
|
|
95
81
|
return result;
|
96
82
|
}
|
97
83
|
|
98
|
-
|
99
|
-
method_name(ID mid)
|
84
|
+
st_data_t method_key(VALUE klass, VALUE msym)
|
100
85
|
{
|
101
|
-
|
86
|
+
VALUE resolved_klass = klass;
|
102
87
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
88
|
+
/* Is this an include for a module? If so get the actual
|
89
|
+
module class since we want to combine all profiling
|
90
|
+
results for that module. */
|
91
|
+
if (klass == 0 || klass == Qnil)
|
92
|
+
{
|
93
|
+
resolved_klass = Qnil;
|
108
94
|
}
|
95
|
+
else if (BUILTIN_TYPE(klass) == T_ICLASS)
|
96
|
+
{
|
97
|
+
resolved_klass = RBASIC(klass)->klass;
|
98
|
+
}
|
99
|
+
|
100
|
+
return (resolved_klass << 4) + (msym);
|
109
101
|
}
|
110
102
|
|
111
|
-
|
112
|
-
|
103
|
+
/* ====== Allocation Table ====== */
|
104
|
+
st_table* allocations_table_create()
|
113
105
|
{
|
114
|
-
|
115
|
-
volatile VALUE result = Qnil;
|
116
|
-
|
117
|
-
klass_str = klass_name(klass);
|
118
|
-
method_str = method_name(mid);
|
119
|
-
|
120
|
-
result = rb_str_dup(klass_str);
|
121
|
-
rb_str_cat2(result, "#");
|
122
|
-
rb_str_append(result, method_str);
|
123
|
-
|
124
|
-
return result;
|
106
|
+
return rb_st_init_numtable();
|
125
107
|
}
|
126
108
|
|
127
|
-
static
|
128
|
-
source_klass_name(VALUE source_klass)
|
109
|
+
static int allocations_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
|
129
110
|
{
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
if (RTEST(source_klass)) {
|
134
|
-
klass_str = rb_class_name(source_klass);
|
135
|
-
result = rb_str_dup(klass_str);
|
136
|
-
} else {
|
137
|
-
result = rb_str_new2("[global]");
|
138
|
-
}
|
139
|
-
|
140
|
-
return result;
|
111
|
+
prof_allocation_free((prof_allocation_t*)value);
|
112
|
+
return ST_CONTINUE;
|
141
113
|
}
|
142
114
|
|
143
|
-
static
|
144
|
-
calltree_name(VALUE source_klass, int relation, ID mid)
|
115
|
+
static int prof_method_collect_allocations(st_data_t key, st_data_t value, st_data_t result)
|
145
116
|
{
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
klass_str = source_klass_name(source_klass);
|
151
|
-
method_str = method_name(mid);
|
152
|
-
|
153
|
-
klass_path = rb_str_split(klass_str, "::");
|
154
|
-
joiner = rb_str_new2("/");
|
155
|
-
result = rb_ary_join(klass_path, joiner);
|
156
|
-
|
157
|
-
rb_str_cat2(result, "::");
|
158
|
-
|
159
|
-
if (RP_REL_GET(relation, kObjectSingleton)) {
|
160
|
-
rb_str_cat2(result, "*");
|
161
|
-
}
|
162
|
-
|
163
|
-
if (RP_REL_GET(relation, kModuleSingleton)) {
|
164
|
-
rb_str_cat2(result, "^");
|
165
|
-
}
|
166
|
-
|
167
|
-
rb_str_append(result, method_str);
|
168
|
-
|
169
|
-
return result;
|
117
|
+
prof_allocation_t* allocation = (prof_allocation_t*)value;
|
118
|
+
VALUE arr = (VALUE)result;
|
119
|
+
rb_ary_push(arr, prof_allocation_wrap(allocation));
|
120
|
+
return ST_CONTINUE;
|
170
121
|
}
|
171
122
|
|
172
|
-
|
173
|
-
method_key(prof_method_key_t* key, VALUE klass, ID mid)
|
123
|
+
static int prof_method_mark_allocations(st_data_t key, st_data_t value, st_data_t data)
|
174
124
|
{
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
klass = RBASIC(klass)->klass;
|
180
|
-
}
|
125
|
+
prof_allocation_t* allocation = (prof_allocation_t*)value;
|
126
|
+
prof_allocation_mark(allocation);
|
127
|
+
return ST_CONTINUE;
|
128
|
+
}
|
181
129
|
|
182
|
-
|
183
|
-
|
184
|
-
|
130
|
+
void allocations_table_free(st_table* table)
|
131
|
+
{
|
132
|
+
rb_st_foreach(table, allocations_table_free_iterator, 0);
|
133
|
+
rb_st_free_table(table);
|
185
134
|
}
|
186
135
|
|
187
136
|
/* ================ prof_method_t =================*/
|
188
|
-
|
189
|
-
prof_method_t*
|
190
|
-
prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
|
137
|
+
prof_method_t* prof_get_method(VALUE self)
|
191
138
|
{
|
192
|
-
|
193
|
-
|
194
|
-
result
|
195
|
-
method_key(result->key, klass, mid);
|
196
|
-
|
197
|
-
result->excluded = 0;
|
198
|
-
result->recursive = 0;
|
199
|
-
|
200
|
-
result->call_infos = prof_call_infos_create();
|
201
|
-
result->visits = 0;
|
202
|
-
|
203
|
-
result->object = Qnil;
|
204
|
-
|
205
|
-
if (source_file != NULL)
|
206
|
-
{
|
207
|
-
size_t len = strlen(source_file) + 1;
|
208
|
-
char *buffer = ALLOC_N(char, len);
|
209
|
-
|
210
|
-
MEMCPY(buffer, source_file, char, len);
|
211
|
-
result->source_file = buffer;
|
212
|
-
} else {
|
213
|
-
result->source_file = source_file;
|
214
|
-
}
|
215
|
-
|
216
|
-
result->source_klass = Qnil;
|
217
|
-
result->line = line;
|
139
|
+
/* Can't use Data_Get_Struct because that triggers the event hook
|
140
|
+
ending up in endless recursion. */
|
141
|
+
prof_method_t* result = DATA_PTR(self);
|
218
142
|
|
219
|
-
result
|
220
|
-
|
143
|
+
if (!result)
|
144
|
+
rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
|
221
145
|
|
222
146
|
return result;
|
223
147
|
}
|
224
148
|
|
225
|
-
prof_method_t*
|
226
|
-
prof_method_create_excluded(VALUE klass, ID mid)
|
149
|
+
prof_method_t* prof_method_create(VALUE klass, VALUE msym, VALUE source_file, int source_line)
|
227
150
|
{
|
228
|
-
prof_method_t
|
151
|
+
prof_method_t* result = ALLOC(prof_method_t);
|
152
|
+
result->key = method_key(klass, msym);
|
153
|
+
result->klass_flags = 0;
|
229
154
|
|
230
|
-
|
231
|
-
|
155
|
+
/* Note we do not call resolve_klass_name now because that causes an object allocation that shows up
|
156
|
+
in the allocation results so we want to avoid it until after the profile run is complete. */
|
157
|
+
result->klass = resolve_klass(klass, &result->klass_flags);
|
158
|
+
result->klass_name = Qnil;
|
159
|
+
result->method_name = msym;
|
160
|
+
result->measurement = prof_measurement_create();
|
232
161
|
|
233
|
-
|
234
|
-
result->
|
235
|
-
result->recursive = 0;
|
162
|
+
result->call_trees = prof_call_trees_create();
|
163
|
+
result->allocations_table = allocations_table_create();
|
236
164
|
|
237
|
-
result->call_infos = 0;
|
238
165
|
result->visits = 0;
|
166
|
+
result->recursive = false;
|
239
167
|
|
240
168
|
result->object = Qnil;
|
241
|
-
result->source_klass = Qnil;
|
242
|
-
result->line = 0;
|
243
169
|
|
244
|
-
result->
|
245
|
-
result->
|
170
|
+
result->source_file = source_file;
|
171
|
+
result->source_line = source_line;
|
246
172
|
|
247
173
|
return result;
|
248
174
|
}
|
@@ -253,211 +179,102 @@ prof_method_create_excluded(VALUE klass, ID mid)
|
|
253
179
|
be freed before the parent profile. Thus we add in a free function
|
254
180
|
for the garbage collector so that if it does get called will nil
|
255
181
|
out our Ruby object reference.*/
|
256
|
-
static void
|
257
|
-
prof_method_ruby_gc_free(prof_method_t* method)
|
182
|
+
static void prof_method_ruby_gc_free(void* data)
|
258
183
|
{
|
259
|
-
|
260
|
-
|
261
|
-
if (method->object != Qnil)
|
262
|
-
{
|
263
|
-
RDATA(method->object)->data = NULL;
|
264
|
-
RDATA(method->object)->dfree = NULL;
|
265
|
-
RDATA(method->object)->dmark = NULL;
|
266
|
-
}
|
267
|
-
method->object = Qnil;
|
184
|
+
prof_method_t* method = (prof_method_t*)data;
|
185
|
+
method->object = Qnil;
|
268
186
|
}
|
269
187
|
|
270
|
-
static void
|
271
|
-
prof_method_free(prof_method_t* method)
|
188
|
+
static void prof_method_free(prof_method_t* method)
|
272
189
|
{
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
method->key = NULL;
|
283
|
-
method->source_klass = Qnil;
|
190
|
+
/* Has this method object been accessed by Ruby? If
|
191
|
+
yes clean it up so to avoid a segmentation fault. */
|
192
|
+
if (method->object != Qnil)
|
193
|
+
{
|
194
|
+
RDATA(method->object)->dmark = NULL;
|
195
|
+
RDATA(method->object)->dfree = NULL;
|
196
|
+
RDATA(method->object)->data = NULL;
|
197
|
+
method->object = Qnil;
|
198
|
+
}
|
284
199
|
|
285
|
-
|
200
|
+
allocations_table_free(method->allocations_table);
|
201
|
+
prof_call_trees_free(method->call_trees);
|
202
|
+
prof_measurement_free(method->measurement);
|
203
|
+
xfree(method);
|
286
204
|
}
|
287
205
|
|
288
|
-
void
|
289
|
-
prof_method_mark(prof_method_t *method)
|
206
|
+
size_t prof_method_size(const void* data)
|
290
207
|
{
|
291
|
-
|
292
|
-
rb_gc_mark(method->key->klass);
|
293
|
-
}
|
294
|
-
|
295
|
-
if (method->source_klass) {
|
296
|
-
rb_gc_mark(method->source_klass);
|
297
|
-
}
|
298
|
-
|
299
|
-
if (method->object) {
|
300
|
-
rb_gc_mark(method->object);
|
301
|
-
}
|
302
|
-
|
303
|
-
if (method->call_infos) {
|
304
|
-
prof_call_infos_mark(method->call_infos);
|
305
|
-
}
|
208
|
+
return sizeof(prof_method_t);
|
306
209
|
}
|
307
210
|
|
308
|
-
|
309
|
-
resolve_source_klass(prof_method_t* method)
|
211
|
+
void prof_method_mark(void* data)
|
310
212
|
{
|
311
|
-
|
312
|
-
volatile VALUE attached;
|
313
|
-
unsigned int relation;
|
213
|
+
prof_method_t* method = (prof_method_t*)data;
|
314
214
|
|
315
|
-
|
316
|
-
|
317
|
-
inclusions and singleton classes back to a meaningful root
|
318
|
-
while keeping track of these relationships. */
|
215
|
+
if (method->object != Qnil)
|
216
|
+
rb_gc_mark(method->object);
|
319
217
|
|
320
|
-
|
321
|
-
|
322
|
-
|
218
|
+
rb_gc_mark(method->klass_name);
|
219
|
+
rb_gc_mark(method->method_name);
|
220
|
+
rb_gc_mark(method->source_file);
|
323
221
|
|
324
|
-
|
325
|
-
|
222
|
+
if (method->klass != Qnil)
|
223
|
+
rb_gc_mark(method->klass);
|
326
224
|
|
327
|
-
|
328
|
-
{
|
329
|
-
/* This is a global/unknown class */
|
330
|
-
if (klass == 0 || klass == Qnil)
|
331
|
-
break;
|
225
|
+
prof_measurement_mark(method->measurement);
|
332
226
|
|
333
|
-
|
334
|
-
if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
|
335
|
-
{
|
336
|
-
/* We have come across a singleton object. First
|
337
|
-
figure out what it is attached to.*/
|
338
|
-
attached = rb_iv_get(klass, "__attached__");
|
339
|
-
|
340
|
-
/* Is this a singleton class acting as a metaclass?
|
341
|
-
Or for singleton methods on a module? */
|
342
|
-
if (BUILTIN_TYPE(attached) == T_CLASS ||
|
343
|
-
BUILTIN_TYPE(attached) == T_MODULE)
|
344
|
-
{
|
345
|
-
RP_REL_SET(relation, kModuleSingleton);
|
346
|
-
klass = attached;
|
347
|
-
}
|
348
|
-
/* Is this for singleton methods on an object? */
|
349
|
-
else if (BUILTIN_TYPE(attached) == T_OBJECT)
|
350
|
-
{
|
351
|
-
RP_REL_SET(relation, kObjectSingleton);
|
352
|
-
next_klass = rb_class_superclass(klass);
|
353
|
-
klass = next_klass;
|
354
|
-
}
|
355
|
-
/* This is a singleton of an instance of a builtin type. */
|
356
|
-
else
|
357
|
-
{
|
358
|
-
RP_REL_SET(relation, kObjectSingleton);
|
359
|
-
next_klass = rb_class_superclass(klass);
|
360
|
-
klass = next_klass;
|
361
|
-
}
|
362
|
-
}
|
363
|
-
/* Is this an include for a module? If so get the actual
|
364
|
-
module class since we want to combine all profiling
|
365
|
-
results for that module. */
|
366
|
-
else if (BUILTIN_TYPE(klass) == T_ICLASS)
|
367
|
-
{
|
368
|
-
RP_REL_SET(relation, kModuleIncludee);
|
369
|
-
next_klass = RBASIC(klass)->klass;
|
370
|
-
klass = next_klass;
|
371
|
-
}
|
372
|
-
/* No transformations apply; so bail. */
|
373
|
-
else
|
374
|
-
{
|
375
|
-
break;
|
376
|
-
}
|
377
|
-
}
|
378
|
-
|
379
|
-
method->resolved = 1;
|
380
|
-
method->relation = relation;
|
381
|
-
method->source_klass = klass;
|
382
|
-
|
383
|
-
return klass;
|
227
|
+
rb_st_foreach(method->allocations_table, prof_method_mark_allocations, 0);
|
384
228
|
}
|
385
229
|
|
386
|
-
VALUE
|
387
|
-
prof_method_wrap(prof_method_t *result)
|
230
|
+
static VALUE prof_method_allocate(VALUE klass)
|
388
231
|
{
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
return result->object;
|
232
|
+
prof_method_t* method_data = prof_method_create(Qnil, Qnil, Qnil, 0);
|
233
|
+
method_data->object = prof_method_wrap(method_data);
|
234
|
+
return method_data->object;
|
393
235
|
}
|
394
236
|
|
395
|
-
|
396
|
-
get_prof_method(VALUE self)
|
237
|
+
VALUE prof_method_wrap(prof_method_t* method)
|
397
238
|
{
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
|
404
|
-
}
|
405
|
-
|
406
|
-
return result;
|
407
|
-
}
|
408
|
-
|
409
|
-
/* ================ Method Table =================*/
|
410
|
-
int
|
411
|
-
method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2)
|
412
|
-
{
|
413
|
-
return (key1->klass != key2->klass) || (key1->mid != key2->mid);
|
414
|
-
}
|
415
|
-
|
416
|
-
st_index_t
|
417
|
-
method_table_hash(prof_method_key_t *key)
|
418
|
-
{
|
419
|
-
return key->key;
|
239
|
+
if (method->object == Qnil)
|
240
|
+
{
|
241
|
+
method->object = Data_Wrap_Struct(cRpMethodInfo, prof_method_mark, prof_method_ruby_gc_free, method);
|
242
|
+
}
|
243
|
+
return method->object;
|
420
244
|
}
|
421
245
|
|
422
|
-
|
423
|
-
method_table_cmp,
|
424
|
-
method_table_hash
|
425
|
-
};
|
426
|
-
|
427
|
-
st_table *
|
428
|
-
method_table_create()
|
246
|
+
st_table* method_table_create()
|
429
247
|
{
|
430
|
-
|
248
|
+
return rb_st_init_numtable();
|
431
249
|
}
|
432
250
|
|
433
|
-
static int
|
434
|
-
method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
|
251
|
+
static int method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
|
435
252
|
{
|
436
|
-
prof_method_free((prof_method_t
|
253
|
+
prof_method_free((prof_method_t*)value);
|
437
254
|
return ST_CONTINUE;
|
438
255
|
}
|
439
256
|
|
440
|
-
void
|
441
|
-
method_table_free(st_table *table)
|
257
|
+
void method_table_free(st_table* table)
|
442
258
|
{
|
443
|
-
|
444
|
-
|
259
|
+
rb_st_foreach(table, method_table_free_iterator, 0);
|
260
|
+
rb_st_free_table(table);
|
445
261
|
}
|
446
262
|
|
447
|
-
size_t
|
448
|
-
method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
|
263
|
+
size_t method_table_insert(st_table* table, st_data_t key, prof_method_t* val)
|
449
264
|
{
|
450
|
-
|
265
|
+
return rb_st_insert(table, (st_data_t)key, (st_data_t)val);
|
451
266
|
}
|
452
267
|
|
453
|
-
prof_method_t *
|
454
|
-
method_table_lookup(st_table *table, const prof_method_key_t* key)
|
268
|
+
prof_method_t* method_table_lookup(st_table* table, st_data_t key)
|
455
269
|
{
|
456
270
|
st_data_t val;
|
457
|
-
if (
|
458
|
-
|
459
|
-
|
460
|
-
|
271
|
+
if (rb_st_lookup(table, (st_data_t)key, &val))
|
272
|
+
{
|
273
|
+
return (prof_method_t*)val;
|
274
|
+
}
|
275
|
+
else
|
276
|
+
{
|
277
|
+
return NULL;
|
461
278
|
}
|
462
279
|
}
|
463
280
|
|
@@ -472,52 +289,46 @@ the RubyProf::Profile object.
|
|
472
289
|
*/
|
473
290
|
|
474
291
|
/* call-seq:
|
475
|
-
|
292
|
+
allocations -> array
|
476
293
|
|
477
|
-
|
478
|
-
static VALUE
|
479
|
-
prof_method_line(VALUE self)
|
294
|
+
Returns an array of allocation information.*/
|
295
|
+
static VALUE prof_method_allocations(VALUE self)
|
480
296
|
{
|
481
|
-
|
482
|
-
|
297
|
+
prof_method_t* method = prof_get_method(self);
|
298
|
+
VALUE result = rb_ary_new();
|
299
|
+
rb_st_foreach(method->allocations_table, prof_method_collect_allocations, result);
|
300
|
+
return result;
|
483
301
|
}
|
484
302
|
|
485
303
|
/* call-seq:
|
486
|
-
|
304
|
+
called -> Measurement
|
487
305
|
|
488
|
-
|
489
|
-
|
490
|
-
static VALUE prof_method_source_file(VALUE self)
|
306
|
+
Returns the measurement associated with this method. */
|
307
|
+
static VALUE prof_method_measurement(VALUE self)
|
491
308
|
{
|
492
|
-
|
493
|
-
|
494
|
-
return rb_str_new2("ruby_runtime");
|
495
|
-
} else {
|
496
|
-
return rb_str_new2(sf);
|
497
|
-
}
|
309
|
+
prof_method_t* method = prof_get_method(self);
|
310
|
+
return prof_measurement_wrap(method->measurement);
|
498
311
|
}
|
499
312
|
|
500
|
-
|
501
313
|
/* call-seq:
|
502
|
-
|
314
|
+
source_file => string
|
503
315
|
|
504
|
-
|
505
|
-
|
506
|
-
|
316
|
+
return the source file of the method
|
317
|
+
*/
|
318
|
+
static VALUE prof_method_source_file(VALUE self)
|
507
319
|
{
|
508
|
-
prof_method_t
|
509
|
-
return
|
320
|
+
prof_method_t* method = prof_get_method(self);
|
321
|
+
return method->source_file;
|
510
322
|
}
|
511
323
|
|
512
324
|
/* call-seq:
|
513
|
-
|
325
|
+
line_no -> int
|
514
326
|
|
515
|
-
|
516
|
-
static VALUE
|
517
|
-
prof_method_id(VALUE self)
|
327
|
+
returns the line number of the method */
|
328
|
+
static VALUE prof_method_line(VALUE self)
|
518
329
|
{
|
519
|
-
prof_method_t
|
520
|
-
return
|
330
|
+
prof_method_t* method = prof_get_method(self);
|
331
|
+
return INT2FIX(method->source_line);
|
521
332
|
}
|
522
333
|
|
523
334
|
/* call-seq:
|
@@ -526,105 +337,134 @@ prof_method_id(VALUE self)
|
|
526
337
|
Returns the name of this method's class. Singleton classes
|
527
338
|
will have the form <Object::Object>. */
|
528
339
|
|
529
|
-
static VALUE
|
530
|
-
prof_klass_name(VALUE self)
|
340
|
+
static VALUE prof_method_klass_name(VALUE self)
|
531
341
|
{
|
532
|
-
prof_method_t
|
533
|
-
|
342
|
+
prof_method_t* method = prof_get_method(self);
|
343
|
+
if (method->klass_name == Qnil)
|
344
|
+
method->klass_name = resolve_klass_name(method->klass, &method->klass_flags);
|
345
|
+
|
346
|
+
return method->klass_name;
|
534
347
|
}
|
535
348
|
|
536
349
|
/* call-seq:
|
537
|
-
|
350
|
+
klass_flags -> integer
|
538
351
|
|
539
|
-
Returns the
|
540
|
-
methods will be returned in the format <Object::Object>#method.*/
|
352
|
+
Returns the klass flags */
|
541
353
|
|
542
|
-
static VALUE
|
543
|
-
prof_method_name(VALUE self)
|
354
|
+
static VALUE prof_method_klass_flags(VALUE self)
|
544
355
|
{
|
545
|
-
prof_method_t
|
546
|
-
return
|
356
|
+
prof_method_t* method = prof_get_method(self);
|
357
|
+
return INT2FIX(method->klass_flags);
|
547
358
|
}
|
548
359
|
|
549
360
|
/* call-seq:
|
550
|
-
|
361
|
+
method_name -> string
|
551
362
|
|
552
|
-
Returns the
|
363
|
+
Returns the name of this method in the format Object#method. Singletons
|
364
|
+
methods will be returned in the format <Object::Object>#method.*/
|
553
365
|
|
554
|
-
static VALUE
|
555
|
-
prof_full_name(VALUE self)
|
366
|
+
static VALUE prof_method_name(VALUE self)
|
556
367
|
{
|
557
|
-
prof_method_t
|
558
|
-
return
|
368
|
+
prof_method_t* method = prof_get_method(self);
|
369
|
+
return method->method_name;
|
559
370
|
}
|
560
371
|
|
561
372
|
/* call-seq:
|
562
|
-
|
373
|
+
recursive? -> boolean
|
563
374
|
|
564
|
-
Returns
|
565
|
-
|
566
|
-
static VALUE
|
567
|
-
prof_method_call_infos(VALUE self)
|
375
|
+
Returns the true if this method is recursively invoked */
|
376
|
+
static VALUE prof_method_recursive(VALUE self)
|
568
377
|
{
|
569
|
-
prof_method_t
|
570
|
-
|
571
|
-
method->call_infos->object = prof_call_infos_wrap(method->call_infos);
|
572
|
-
}
|
573
|
-
return method->call_infos->object;
|
378
|
+
prof_method_t* method = prof_get_method(self);
|
379
|
+
return method->recursive ? Qtrue : Qfalse;
|
574
380
|
}
|
575
381
|
|
576
382
|
/* call-seq:
|
577
|
-
|
383
|
+
call_trees -> CallTrees
|
578
384
|
|
579
|
-
|
580
|
-
static VALUE
|
581
|
-
prof_method_recursive(VALUE self)
|
385
|
+
Returns the CallTrees associated with this method. */
|
386
|
+
static VALUE prof_method_call_trees(VALUE self)
|
582
387
|
{
|
583
|
-
|
584
|
-
|
388
|
+
prof_method_t* method = prof_get_method(self);
|
389
|
+
return prof_call_trees_wrap(method->call_trees);
|
585
390
|
}
|
586
391
|
|
587
|
-
/*
|
588
|
-
|
589
|
-
|
590
|
-
Returns the Ruby klass of the natural source-level definition. */
|
591
|
-
static VALUE
|
592
|
-
prof_source_klass(VALUE self)
|
392
|
+
/* :nodoc: */
|
393
|
+
static VALUE prof_method_dump(VALUE self)
|
593
394
|
{
|
594
|
-
prof_method_t
|
595
|
-
|
596
|
-
}
|
395
|
+
prof_method_t* method_data = DATA_PTR(self);
|
396
|
+
VALUE result = rb_hash_new();
|
597
397
|
|
598
|
-
|
599
|
-
|
398
|
+
rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_method_klass_name(self));
|
399
|
+
rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(method_data->klass_flags));
|
400
|
+
rb_hash_aset(result, ID2SYM(rb_intern("method_name")), method_data->method_name);
|
401
|
+
|
402
|
+
rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(method_data->key));
|
403
|
+
rb_hash_aset(result, ID2SYM(rb_intern("recursive")), prof_method_recursive(self));
|
404
|
+
rb_hash_aset(result, ID2SYM(rb_intern("source_file")), method_data->source_file);
|
405
|
+
rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(method_data->source_line));
|
406
|
+
|
407
|
+
rb_hash_aset(result, ID2SYM(rb_intern("call_trees")), prof_call_trees_wrap(method_data->call_trees));
|
408
|
+
rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(method_data->measurement));
|
409
|
+
rb_hash_aset(result, ID2SYM(rb_intern("allocations")), prof_method_allocations(self));
|
600
410
|
|
601
|
-
|
411
|
+
return result;
|
412
|
+
}
|
602
413
|
|
603
|
-
|
604
|
-
|
414
|
+
/* :nodoc: */
|
415
|
+
static VALUE prof_method_load(VALUE self, VALUE data)
|
605
416
|
{
|
606
|
-
prof_method_t
|
607
|
-
|
608
|
-
|
417
|
+
prof_method_t* method_data = RDATA(self)->data;
|
418
|
+
method_data->object = self;
|
419
|
+
|
420
|
+
method_data->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
|
421
|
+
method_data->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
|
422
|
+
method_data->method_name = rb_hash_aref(data, ID2SYM(rb_intern("method_name")));
|
423
|
+
method_data->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
|
424
|
+
|
425
|
+
method_data->recursive = rb_hash_aref(data, ID2SYM(rb_intern("recursive"))) == Qtrue ? true : false;
|
426
|
+
|
427
|
+
method_data->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
|
428
|
+
method_data->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
|
429
|
+
|
430
|
+
VALUE call_trees = rb_hash_aref(data, ID2SYM(rb_intern("call_trees")));
|
431
|
+
method_data->call_trees = prof_get_call_trees(call_trees);
|
432
|
+
|
433
|
+
VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
|
434
|
+
method_data->measurement = prof_get_measurement(measurement);
|
435
|
+
|
436
|
+
VALUE allocations = rb_hash_aref(data, ID2SYM(rb_intern("allocations")));
|
437
|
+
for (int i = 0; i < rb_array_len(allocations); i++)
|
438
|
+
{
|
439
|
+
VALUE allocation = rb_ary_entry(allocations, i);
|
440
|
+
prof_allocation_t* allocation_data = prof_allocation_get(allocation);
|
441
|
+
|
442
|
+
rb_st_insert(method_data->allocations_table, allocation_data->key, (st_data_t)allocation_data);
|
443
|
+
}
|
444
|
+
return data;
|
609
445
|
}
|
610
446
|
|
611
447
|
void rp_init_method_info()
|
612
448
|
{
|
613
449
|
/* MethodInfo */
|
614
|
-
|
615
|
-
rb_undef_method(CLASS_OF(
|
450
|
+
cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cData);
|
451
|
+
rb_undef_method(CLASS_OF(cRpMethodInfo), "new");
|
452
|
+
rb_define_alloc_func(cRpMethodInfo, prof_method_allocate);
|
616
453
|
|
617
|
-
rb_define_method(
|
618
|
-
rb_define_method(
|
619
|
-
rb_define_method(
|
620
|
-
rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
|
621
|
-
rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
|
454
|
+
rb_define_method(cRpMethodInfo, "klass_name", prof_method_klass_name, 0);
|
455
|
+
rb_define_method(cRpMethodInfo, "klass_flags", prof_method_klass_flags, 0);
|
456
|
+
rb_define_method(cRpMethodInfo, "method_name", prof_method_name, 0);
|
622
457
|
|
623
|
-
rb_define_method(
|
624
|
-
rb_define_method(cMethodInfo, "source_klass", prof_source_klass, 0);
|
625
|
-
rb_define_method(cMethodInfo, "source_file", prof_method_source_file, 0);
|
626
|
-
rb_define_method(cMethodInfo, "line", prof_method_line, 0);
|
458
|
+
rb_define_method(cRpMethodInfo, "call_trees", prof_method_call_trees, 0);
|
627
459
|
|
628
|
-
rb_define_method(
|
629
|
-
rb_define_method(
|
630
|
-
|
460
|
+
rb_define_method(cRpMethodInfo, "allocations", prof_method_allocations, 0);
|
461
|
+
rb_define_method(cRpMethodInfo, "measurement", prof_method_measurement, 0);
|
462
|
+
|
463
|
+
rb_define_method(cRpMethodInfo, "source_file", prof_method_source_file, 0);
|
464
|
+
rb_define_method(cRpMethodInfo, "line", prof_method_line, 0);
|
465
|
+
|
466
|
+
rb_define_method(cRpMethodInfo, "recursive?", prof_method_recursive, 0);
|
467
|
+
|
468
|
+
rb_define_method(cRpMethodInfo, "_dump_data", prof_method_dump, 0);
|
469
|
+
rb_define_method(cRpMethodInfo, "_load_data", prof_method_load, 1);
|
470
|
+
}
|