ruby-prof 0.16.2 → 0.17.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 +15 -0
- data/README.rdoc +36 -5
- data/bin/ruby-prof +7 -2
- data/doc/LICENSE.html +2 -1
- data/doc/README_rdoc.html +42 -8
- data/doc/Rack.html +2 -1
- data/doc/Rack/RubyProf.html +25 -18
- data/doc/Rack/RubyProf/RackProfiler.html +343 -0
- data/doc/RubyProf.html +14 -2
- data/doc/RubyProf/AbstractPrinter.html +91 -12
- data/doc/RubyProf/AggregateCallInfo.html +2 -1
- data/doc/RubyProf/CallInfo.html +18 -78
- data/doc/RubyProf/CallInfoPrinter.html +2 -1
- data/doc/RubyProf/CallInfoVisitor.html +2 -1
- data/doc/RubyProf/CallStackPrinter.html +35 -29
- data/doc/RubyProf/CallTreePrinter.html +98 -14
- data/doc/RubyProf/Cmd.html +11 -5
- data/doc/RubyProf/DeprecationWarnings.html +148 -0
- data/doc/RubyProf/DotPrinter.html +2 -1
- data/doc/RubyProf/FlatPrinter.html +2 -1
- data/doc/RubyProf/FlatPrinterWithLineNumbers.html +7 -5
- data/doc/RubyProf/GraphHtmlPrinter.html +18 -12
- data/doc/RubyProf/GraphPrinter.html +2 -1
- data/doc/RubyProf/MethodInfo.html +19 -88
- data/doc/RubyProf/MultiPrinter.html +231 -17
- data/doc/RubyProf/Profile.html +184 -39
- data/doc/RubyProf/Profile/ExcludeCommonMethods.html +411 -0
- data/doc/RubyProf/Profile/LegacyMethodElimination.html +158 -0
- data/doc/RubyProf/ProfileTask.html +2 -1
- data/doc/RubyProf/Thread.html +4 -39
- data/doc/created.rid +21 -19
- data/doc/css/fonts.css +6 -6
- data/doc/examples/flat_txt.html +2 -1
- data/doc/examples/graph_html.html +2 -1
- data/doc/examples/graph_txt.html +2 -1
- data/doc/index.html +47 -7
- data/doc/js/darkfish.js +7 -7
- data/doc/js/search_index.js +1 -1
- data/doc/js/search_index.js.gz +0 -0
- data/doc/js/searcher.js +1 -0
- data/doc/js/searcher.js.gz +0 -0
- data/doc/table_of_contents.html +190 -80
- data/ext/ruby_prof/extconf.rb +4 -0
- data/ext/ruby_prof/rp_call_info.c +19 -1
- data/ext/ruby_prof/rp_call_info.h +8 -3
- data/ext/ruby_prof/rp_method.c +282 -57
- data/ext/ruby_prof/rp_method.h +28 -5
- data/ext/ruby_prof/rp_stack.c +69 -24
- data/ext/ruby_prof/rp_stack.h +21 -9
- data/ext/ruby_prof/rp_thread.c +4 -1
- data/ext/ruby_prof/ruby_prof.c +142 -39
- data/ext/ruby_prof/ruby_prof.h +3 -0
- data/lib/ruby-prof.rb +10 -0
- data/lib/ruby-prof/call_info.rb +0 -11
- data/lib/ruby-prof/method_info.rb +4 -12
- data/lib/ruby-prof/printers/abstract_printer.rb +19 -1
- data/lib/ruby-prof/printers/call_info_printer.rb +1 -1
- data/lib/ruby-prof/printers/call_stack_printer.rb +9 -4
- data/lib/ruby-prof/printers/call_tree_printer.rb +15 -2
- data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +23 -4
- data/lib/ruby-prof/printers/graph_html_printer.rb +10 -5
- data/lib/ruby-prof/printers/graph_printer.rb +2 -2
- data/lib/ruby-prof/printers/multi_printer.rb +44 -18
- data/lib/ruby-prof/profile.rb +13 -42
- data/lib/ruby-prof/profile/exclude_common_methods.rb +201 -0
- data/lib/ruby-prof/profile/legacy_method_elimination.rb +49 -0
- data/lib/ruby-prof/rack.rb +130 -51
- data/lib/ruby-prof/thread.rb +0 -6
- data/lib/ruby-prof/version.rb +1 -1
- data/ruby-prof.gemspec +4 -3
- data/test/aggregate_test.rb +1 -1
- data/test/exclude_methods_test.rb +146 -0
- data/test/line_number_test.rb +12 -3
- data/test/multi_printer_test.rb +23 -2
- data/test/no_method_class_test.rb +1 -1
- data/test/printers_test.rb +21 -1
- data/test/rack_test.rb +64 -0
- data/test/recursive_test.rb +15 -15
- data/test/test_helper.rb +11 -0
- metadata +20 -13
data/ext/ruby_prof/extconf.rb
CHANGED
@@ -10,9 +10,13 @@ if RUBY_VERSION < "1.9.3"
|
|
10
10
|
exit(1)
|
11
11
|
end
|
12
12
|
|
13
|
+
# For the love of bitfields...
|
14
|
+
$CFLAGS += ' -std=c99'
|
15
|
+
|
13
16
|
# standard ruby methods
|
14
17
|
have_func("rb_gc_stat")
|
15
18
|
have_func("rb_gc_count")
|
19
|
+
have_func("rb_remove_event_hook_with_data")
|
16
20
|
|
17
21
|
# Alexander Dymo GC patch
|
18
22
|
have_func("rb_os_allocated_objects")
|
@@ -23,11 +23,16 @@ prof_call_info_create(prof_method_t* method, prof_call_info_t* parent)
|
|
23
23
|
result->call_infos = call_info_table_create();
|
24
24
|
result->children = Qnil;
|
25
25
|
|
26
|
-
result->called = 0;
|
27
26
|
result->total_time = 0;
|
28
27
|
result->self_time = 0;
|
29
28
|
result->wait_time = 0;
|
29
|
+
|
30
|
+
result->called = 0;
|
31
|
+
|
32
|
+
result->recursive = 0;
|
33
|
+
result->depth = 0;
|
30
34
|
result->line = 0;
|
35
|
+
|
31
36
|
return result;
|
32
37
|
}
|
33
38
|
static void
|
@@ -162,6 +167,17 @@ prof_call_info_set_called(VALUE self, VALUE called)
|
|
162
167
|
return called;
|
163
168
|
}
|
164
169
|
|
170
|
+
/* call-seq:
|
171
|
+
recursive? -> boolean
|
172
|
+
|
173
|
+
Returns the true if this call info is a recursive invocation */
|
174
|
+
static VALUE
|
175
|
+
prof_call_info_recursive(VALUE self)
|
176
|
+
{
|
177
|
+
prof_call_info_t *result = prof_get_call_info(self);
|
178
|
+
return result->recursive ? Qtrue : Qfalse;
|
179
|
+
}
|
180
|
+
|
165
181
|
/* call-seq:
|
166
182
|
depth -> int
|
167
183
|
|
@@ -402,6 +418,8 @@ void rp_init_call_info()
|
|
402
418
|
rb_define_method(cCallInfo, "add_self_time", prof_call_info_add_self_time, 1);
|
403
419
|
rb_define_method(cCallInfo, "wait_time", prof_call_info_wait_time, 0);
|
404
420
|
rb_define_method(cCallInfo, "add_wait_time", prof_call_info_add_wait_time, 1);
|
421
|
+
|
422
|
+
rb_define_method(cCallInfo, "recursive?", prof_call_info_recursive, 0);
|
405
423
|
rb_define_method(cCallInfo, "depth", prof_call_info_depth, 0);
|
406
424
|
rb_define_method(cCallInfo, "line", prof_call_info_line, 0);
|
407
425
|
}
|
@@ -15,14 +15,19 @@ typedef struct prof_call_info_t
|
|
15
15
|
prof_method_t *target; /* Use target instead of method to avoid conflict with Ruby method */
|
16
16
|
struct prof_call_info_t *parent;
|
17
17
|
st_table *call_infos;
|
18
|
-
|
19
|
-
int depth;
|
18
|
+
|
20
19
|
double total_time;
|
21
20
|
double self_time;
|
22
21
|
double wait_time;
|
23
|
-
|
22
|
+
|
24
23
|
VALUE object;
|
25
24
|
VALUE children;
|
25
|
+
|
26
|
+
int called;
|
27
|
+
|
28
|
+
unsigned int recursive : 1;
|
29
|
+
unsigned int depth : 15;
|
30
|
+
unsigned int line : 16;
|
26
31
|
} prof_call_info_t;
|
27
32
|
|
28
33
|
/* Array of call_info objects */
|
data/ext/ruby_prof/rp_method.c
CHANGED
@@ -3,31 +3,41 @@
|
|
3
3
|
|
4
4
|
#include "ruby_prof.h"
|
5
5
|
|
6
|
+
#define RP_REL_GET(r, off) ((r) & (1 << (off)))
|
7
|
+
#define RP_REL_SET(r, off) \
|
8
|
+
do { \
|
9
|
+
r |= (1 << (off)); \
|
10
|
+
} while (0)
|
11
|
+
|
6
12
|
VALUE cMethodInfo;
|
7
13
|
|
8
14
|
/* ================ Helper Functions =================*/
|
9
15
|
static VALUE
|
10
16
|
figure_singleton_name(VALUE klass)
|
11
17
|
{
|
12
|
-
VALUE
|
18
|
+
volatile VALUE attached, super;
|
19
|
+
volatile VALUE attached_str, super_str;
|
20
|
+
volatile VALUE result = Qnil;
|
13
21
|
|
14
22
|
/* We have come across a singleton object. First
|
15
23
|
figure out what it is attached to.*/
|
16
|
-
|
24
|
+
attached = rb_iv_get(klass, "__attached__");
|
17
25
|
|
18
26
|
/* Is this a singleton class acting as a metaclass? */
|
19
27
|
if (BUILTIN_TYPE(attached) == T_CLASS)
|
20
28
|
{
|
29
|
+
attached_str = rb_class_name(attached);
|
21
30
|
result = rb_str_new2("<Class::");
|
22
|
-
rb_str_append(result,
|
31
|
+
rb_str_append(result, attached_str);
|
23
32
|
rb_str_cat2(result, ">");
|
24
33
|
}
|
25
34
|
|
26
35
|
/* Is this for singleton methods on a module? */
|
27
36
|
else if (BUILTIN_TYPE(attached) == T_MODULE)
|
28
37
|
{
|
38
|
+
attached_str = rb_class_name(attached);
|
29
39
|
result = rb_str_new2("<Module::");
|
30
|
-
rb_str_append(result,
|
40
|
+
rb_str_append(result, attached_str);
|
31
41
|
rb_str_cat2(result, ">");
|
32
42
|
}
|
33
43
|
|
@@ -37,9 +47,10 @@ figure_singleton_name(VALUE klass)
|
|
37
47
|
/* Make sure to get the super class so that we don't
|
38
48
|
mistakenly grab a T_ICLASS which would lead to
|
39
49
|
unknown method errors. */
|
40
|
-
|
50
|
+
super = rb_class_superclass(klass);
|
51
|
+
super_str = rb_class_name(super);
|
41
52
|
result = rb_str_new2("<Object::");
|
42
|
-
rb_str_append(result,
|
53
|
+
rb_str_append(result, super_str);
|
43
54
|
rb_str_cat2(result, ">");
|
44
55
|
}
|
45
56
|
|
@@ -48,7 +59,7 @@ figure_singleton_name(VALUE klass)
|
|
48
59
|
objects test case). */
|
49
60
|
else
|
50
61
|
{
|
51
|
-
result =
|
62
|
+
result = rb_any_to_s(klass);
|
52
63
|
}
|
53
64
|
|
54
65
|
return result;
|
@@ -57,15 +68,15 @@ figure_singleton_name(VALUE klass)
|
|
57
68
|
static VALUE
|
58
69
|
klass_name(VALUE klass)
|
59
70
|
{
|
60
|
-
VALUE result = Qnil;
|
71
|
+
volatile VALUE result = Qnil;
|
61
72
|
|
62
73
|
if (klass == 0 || klass == Qnil)
|
63
74
|
{
|
64
|
-
result = rb_str_new2("
|
75
|
+
result = rb_str_new2("[global]");
|
65
76
|
}
|
66
77
|
else if (BUILTIN_TYPE(klass) == T_MODULE)
|
67
78
|
{
|
68
|
-
result =
|
79
|
+
result = rb_class_name(klass);
|
69
80
|
}
|
70
81
|
else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
|
71
82
|
{
|
@@ -73,12 +84,12 @@ klass_name(VALUE klass)
|
|
73
84
|
}
|
74
85
|
else if (BUILTIN_TYPE(klass) == T_CLASS)
|
75
86
|
{
|
76
|
-
result =
|
87
|
+
result = rb_class_name(klass);
|
77
88
|
}
|
78
89
|
else
|
79
90
|
{
|
80
91
|
/* Should never happen. */
|
81
|
-
result = rb_str_new2("
|
92
|
+
result = rb_str_new2("[unknown]");
|
82
93
|
}
|
83
94
|
|
84
95
|
return result;
|
@@ -87,26 +98,79 @@ klass_name(VALUE klass)
|
|
87
98
|
static VALUE
|
88
99
|
method_name(ID mid)
|
89
100
|
{
|
90
|
-
VALUE
|
101
|
+
volatile VALUE name = Qnil;
|
91
102
|
|
92
|
-
if (mid == 0)
|
93
|
-
result = rb_str_new2("[No method]");
|
94
103
|
#ifdef ID_ALLOCATOR
|
95
|
-
|
96
|
-
|
104
|
+
if (mid == ID_ALLOCATOR) {
|
105
|
+
return rb_str_new2("allocate");
|
106
|
+
}
|
97
107
|
#endif
|
98
|
-
else
|
99
|
-
result = rb_String(ID2SYM(mid));
|
100
108
|
|
101
|
-
|
109
|
+
if (RTEST(mid)) {
|
110
|
+
name = rb_id2str(mid);
|
111
|
+
return rb_str_dup(name);
|
112
|
+
} else {
|
113
|
+
return rb_str_new2("[no method]");
|
114
|
+
}
|
102
115
|
}
|
103
116
|
|
104
117
|
static VALUE
|
105
118
|
full_name(VALUE klass, ID mid)
|
106
119
|
{
|
107
|
-
VALUE
|
120
|
+
volatile VALUE klass_str, method_str;
|
121
|
+
volatile VALUE result = Qnil;
|
122
|
+
|
123
|
+
klass_str = klass_name(klass);
|
124
|
+
method_str = method_name(mid);
|
125
|
+
|
126
|
+
result = rb_str_dup(klass_str);
|
108
127
|
rb_str_cat2(result, "#");
|
109
|
-
rb_str_append(result,
|
128
|
+
rb_str_append(result, method_str);
|
129
|
+
|
130
|
+
return result;
|
131
|
+
}
|
132
|
+
|
133
|
+
static VALUE
|
134
|
+
source_klass_name(VALUE source_klass)
|
135
|
+
{
|
136
|
+
volatile VALUE klass_str;
|
137
|
+
volatile VALUE result = Qnil;
|
138
|
+
|
139
|
+
if (RTEST(source_klass)) {
|
140
|
+
klass_str = rb_class_name(source_klass);
|
141
|
+
result = rb_str_dup(klass_str);
|
142
|
+
} else {
|
143
|
+
result = rb_str_new2("[global]");
|
144
|
+
}
|
145
|
+
|
146
|
+
return result;
|
147
|
+
}
|
148
|
+
|
149
|
+
static VALUE
|
150
|
+
calltree_name(VALUE source_klass, int relation, ID mid)
|
151
|
+
{
|
152
|
+
volatile VALUE klass_str, klass_path, joiner;
|
153
|
+
volatile VALUE method_str;
|
154
|
+
volatile VALUE result = Qnil;
|
155
|
+
|
156
|
+
klass_str = source_klass_name(source_klass);
|
157
|
+
method_str = method_name(mid);
|
158
|
+
|
159
|
+
klass_path = rb_str_split(klass_str, "::");
|
160
|
+
joiner = rb_str_new2("/");
|
161
|
+
result = rb_ary_join(klass_path, joiner);
|
162
|
+
|
163
|
+
rb_str_cat2(result, "::");
|
164
|
+
|
165
|
+
if (RP_REL_GET(relation, kObjectSingleton)) {
|
166
|
+
rb_str_cat2(result, "*");
|
167
|
+
}
|
168
|
+
|
169
|
+
if (RP_REL_GET(relation, kModuleSingleton)) {
|
170
|
+
rb_str_cat2(result, "^");
|
171
|
+
}
|
172
|
+
|
173
|
+
rb_str_append(result, method_str);
|
110
174
|
|
111
175
|
return result;
|
112
176
|
}
|
@@ -117,8 +181,9 @@ method_key(prof_method_key_t* key, VALUE klass, ID mid)
|
|
117
181
|
/* Is this an include for a module? If so get the actual
|
118
182
|
module class since we want to combine all profiling
|
119
183
|
results for that module. */
|
120
|
-
if (klass != 0)
|
121
|
-
klass =
|
184
|
+
if (klass != 0 && BUILTIN_TYPE(klass) == T_ICLASS) {
|
185
|
+
klass = RBASIC(klass)->klass;
|
186
|
+
}
|
122
187
|
|
123
188
|
key->klass = klass;
|
124
189
|
key->mid = mid;
|
@@ -126,17 +191,22 @@ method_key(prof_method_key_t* key, VALUE klass, ID mid)
|
|
126
191
|
}
|
127
192
|
|
128
193
|
/* ================ prof_method_t =================*/
|
194
|
+
|
129
195
|
prof_method_t*
|
130
196
|
prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
|
131
197
|
{
|
132
198
|
prof_method_t *result = ALLOC(prof_method_t);
|
133
|
-
result->object = Qnil;
|
134
|
-
result->call_infos = prof_call_infos_create();
|
135
199
|
|
136
200
|
result->key = ALLOC(prof_method_key_t);
|
137
201
|
method_key(result->key, klass, mid);
|
138
202
|
|
139
|
-
|
203
|
+
result->excluded = 0;
|
204
|
+
result->recursive = 0;
|
205
|
+
|
206
|
+
result->call_infos = prof_call_infos_create();
|
207
|
+
result->visits = 0;
|
208
|
+
|
209
|
+
result->object = Qnil;
|
140
210
|
|
141
211
|
if (source_file != NULL)
|
142
212
|
{
|
@@ -145,21 +215,49 @@ prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
|
|
145
215
|
|
146
216
|
MEMCPY(buffer, source_file, char, len);
|
147
217
|
result->source_file = buffer;
|
148
|
-
}
|
149
|
-
else
|
150
|
-
{
|
218
|
+
} else {
|
151
219
|
result->source_file = source_file;
|
152
220
|
}
|
221
|
+
|
222
|
+
result->source_klass = Qnil;
|
153
223
|
result->line = line;
|
154
224
|
|
225
|
+
result->resolved = 0;
|
226
|
+
result->relation = 0;
|
227
|
+
|
155
228
|
return result;
|
156
229
|
}
|
157
230
|
|
158
|
-
|
231
|
+
prof_method_t*
|
232
|
+
prof_method_create_excluded(VALUE klass, ID mid)
|
233
|
+
{
|
234
|
+
prof_method_t *result = ALLOC(prof_method_t);
|
235
|
+
|
236
|
+
result->key = ALLOC(prof_method_key_t);
|
237
|
+
method_key(result->key, klass, mid);
|
238
|
+
|
239
|
+
/* Invisible with this flag set. */
|
240
|
+
result->excluded = 1;
|
241
|
+
result->recursive = 0;
|
242
|
+
|
243
|
+
result->call_infos = 0;
|
244
|
+
result->visits = 0;
|
245
|
+
|
246
|
+
result->object = Qnil;
|
247
|
+
result->source_klass = Qnil;
|
248
|
+
result->line = 0;
|
249
|
+
|
250
|
+
result->resolved = 0;
|
251
|
+
result->relation = 0;
|
252
|
+
|
253
|
+
return result;
|
254
|
+
}
|
255
|
+
|
256
|
+
/* The underlying c structures are freed when the parent profile is freed.
|
159
257
|
However, on shutdown the Ruby GC frees objects in any will-nilly order.
|
160
258
|
That means the ruby thread object wrapping the c thread struct may
|
161
259
|
be freed before the parent profile. Thus we add in a free function
|
162
|
-
for the garbage collector so that if it does get called will nil
|
260
|
+
for the garbage collector so that if it does get called will nil
|
163
261
|
out our Ruby object reference.*/
|
164
262
|
static void
|
165
263
|
prof_method_ruby_gc_free(prof_method_t* method)
|
@@ -171,7 +269,7 @@ prof_method_ruby_gc_free(prof_method_t* method)
|
|
171
269
|
RDATA(method->object)->data = NULL;
|
172
270
|
RDATA(method->object)->dfree = NULL;
|
173
271
|
RDATA(method->object)->dmark = NULL;
|
174
|
-
|
272
|
+
}
|
175
273
|
method->object = Qnil;
|
176
274
|
}
|
177
275
|
|
@@ -179,11 +277,16 @@ static void
|
|
179
277
|
prof_method_free(prof_method_t* method)
|
180
278
|
{
|
181
279
|
prof_method_ruby_gc_free(method);
|
182
|
-
|
183
|
-
|
280
|
+
|
281
|
+
if (method->call_infos) {
|
282
|
+
prof_call_infos_free(method->call_infos);
|
283
|
+
xfree(method->call_infos);
|
284
|
+
}
|
184
285
|
|
185
286
|
xfree(method->key);
|
287
|
+
|
186
288
|
method->key = NULL;
|
289
|
+
method->source_klass = Qnil;
|
187
290
|
|
188
291
|
xfree(method);
|
189
292
|
}
|
@@ -191,17 +294,105 @@ prof_method_free(prof_method_t* method)
|
|
191
294
|
void
|
192
295
|
prof_method_mark(prof_method_t *method)
|
193
296
|
{
|
194
|
-
if (method->
|
297
|
+
if (method->key->klass) {
|
298
|
+
rb_gc_mark(method->key->klass);
|
299
|
+
}
|
300
|
+
|
301
|
+
if (method->source_klass) {
|
302
|
+
rb_gc_mark(method->source_klass);
|
303
|
+
}
|
304
|
+
|
305
|
+
if (method->object) {
|
195
306
|
rb_gc_mark(method->object);
|
307
|
+
}
|
196
308
|
|
197
|
-
|
309
|
+
if (method->call_infos) {
|
310
|
+
prof_call_infos_mark(method->call_infos);
|
311
|
+
}
|
312
|
+
}
|
313
|
+
|
314
|
+
static VALUE
|
315
|
+
resolve_source_klass(prof_method_t* method)
|
316
|
+
{
|
317
|
+
volatile VALUE klass, next_klass;
|
318
|
+
volatile VALUE attached;
|
319
|
+
unsigned int relation;
|
320
|
+
|
321
|
+
/* We want to group methods according to their source-level
|
322
|
+
definitions, not their implementation class. Follow module
|
323
|
+
inclusions and singleton classes back to a meaningful root
|
324
|
+
while keeping track of these relationships. */
|
325
|
+
|
326
|
+
if (method->resolved) {
|
327
|
+
return method->source_klass;
|
328
|
+
}
|
329
|
+
|
330
|
+
klass = method->key->klass;
|
331
|
+
relation = 0;
|
332
|
+
|
333
|
+
while (1)
|
334
|
+
{
|
335
|
+
/* This is a global/unknown class */
|
336
|
+
if (klass == 0 || klass == Qnil)
|
337
|
+
break;
|
338
|
+
|
339
|
+
/* Is this a singleton class? (most common case) */
|
340
|
+
if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
|
341
|
+
{
|
342
|
+
/* We have come across a singleton object. First
|
343
|
+
figure out what it is attached to.*/
|
344
|
+
attached = rb_iv_get(klass, "__attached__");
|
345
|
+
|
346
|
+
/* Is this a singleton class acting as a metaclass?
|
347
|
+
Or for singleton methods on a module? */
|
348
|
+
if (BUILTIN_TYPE(attached) == T_CLASS ||
|
349
|
+
BUILTIN_TYPE(attached) == T_MODULE)
|
350
|
+
{
|
351
|
+
RP_REL_SET(relation, kModuleSingleton);
|
352
|
+
klass = attached;
|
353
|
+
}
|
354
|
+
/* Is this for singleton methods on an object? */
|
355
|
+
else if (BUILTIN_TYPE(attached) == T_OBJECT)
|
356
|
+
{
|
357
|
+
RP_REL_SET(relation, kObjectSingleton);
|
358
|
+
next_klass = rb_class_superclass(klass);
|
359
|
+
klass = next_klass;
|
360
|
+
}
|
361
|
+
/* This is a singleton of an instance of a builtin type. */
|
362
|
+
else
|
363
|
+
{
|
364
|
+
RP_REL_SET(relation, kObjectSingleton);
|
365
|
+
next_klass = rb_class_superclass(klass);
|
366
|
+
klass = next_klass;
|
367
|
+
}
|
368
|
+
}
|
369
|
+
/* Is this an include for a module? If so get the actual
|
370
|
+
module class since we want to combine all profiling
|
371
|
+
results for that module. */
|
372
|
+
else if (BUILTIN_TYPE(klass) == T_ICLASS)
|
373
|
+
{
|
374
|
+
RP_REL_SET(relation, kModuleIncludee);
|
375
|
+
next_klass = RBASIC(klass)->klass;
|
376
|
+
klass = next_klass;
|
377
|
+
}
|
378
|
+
/* No transformations apply; so bail. */
|
379
|
+
else
|
380
|
+
{
|
381
|
+
break;
|
382
|
+
}
|
383
|
+
}
|
384
|
+
|
385
|
+
method->resolved = 1;
|
386
|
+
method->relation = relation;
|
387
|
+
method->source_klass = klass;
|
388
|
+
|
389
|
+
return klass;
|
198
390
|
}
|
199
391
|
|
200
392
|
VALUE
|
201
393
|
prof_method_wrap(prof_method_t *result)
|
202
394
|
{
|
203
|
-
if (result->object == Qnil)
|
204
|
-
{
|
395
|
+
if (result->object == Qnil) {
|
205
396
|
result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_ruby_gc_free, result);
|
206
397
|
}
|
207
398
|
return result->object;
|
@@ -214,8 +405,9 @@ get_prof_method(VALUE self)
|
|
214
405
|
ending up in endless recursion. */
|
215
406
|
prof_method_t* result = DATA_PTR(self);
|
216
407
|
|
217
|
-
if (!result)
|
408
|
+
if (!result) {
|
218
409
|
rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
|
410
|
+
}
|
219
411
|
|
220
412
|
return result;
|
221
413
|
}
|
@@ -247,7 +439,7 @@ method_table_create()
|
|
247
439
|
static int
|
248
440
|
method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
|
249
441
|
{
|
250
|
-
prof_method_free((prof_method_t*)value);
|
442
|
+
prof_method_free((prof_method_t *)value);
|
251
443
|
return ST_CONTINUE;
|
252
444
|
}
|
253
445
|
|
@@ -258,7 +450,6 @@ method_table_free(st_table *table)
|
|
258
450
|
st_free_table(table);
|
259
451
|
}
|
260
452
|
|
261
|
-
|
262
453
|
size_t
|
263
454
|
method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
|
264
455
|
{
|
@@ -269,12 +460,9 @@ prof_method_t *
|
|
269
460
|
method_table_lookup(st_table *table, const prof_method_key_t* key)
|
270
461
|
{
|
271
462
|
st_data_t val;
|
272
|
-
if (st_lookup(table, (st_data_t)key, &val))
|
273
|
-
{
|
463
|
+
if (st_lookup(table, (st_data_t)key, &val)) {
|
274
464
|
return (prof_method_t *) val;
|
275
|
-
}
|
276
|
-
else
|
277
|
-
{
|
465
|
+
} else {
|
278
466
|
return NULL;
|
279
467
|
}
|
280
468
|
}
|
@@ -296,7 +484,8 @@ the RubyProf::Profile object.
|
|
296
484
|
static VALUE
|
297
485
|
prof_method_line(VALUE self)
|
298
486
|
{
|
299
|
-
|
487
|
+
int line = get_prof_method(self)->line;
|
488
|
+
return rb_int_new(line);
|
300
489
|
}
|
301
490
|
|
302
491
|
/* call-seq:
|
@@ -307,12 +496,9 @@ return the source file of the method
|
|
307
496
|
static VALUE prof_method_source_file(VALUE self)
|
308
497
|
{
|
309
498
|
const char* sf = get_prof_method(self)->source_file;
|
310
|
-
if(!sf)
|
311
|
-
{
|
499
|
+
if(!sf) {
|
312
500
|
return rb_str_new2("ruby_runtime");
|
313
|
-
}
|
314
|
-
else
|
315
|
-
{
|
501
|
+
} else {
|
316
502
|
return rb_str_new2(sf);
|
317
503
|
}
|
318
504
|
}
|
@@ -387,13 +573,47 @@ static VALUE
|
|
387
573
|
prof_method_call_infos(VALUE self)
|
388
574
|
{
|
389
575
|
prof_method_t *method = get_prof_method(self);
|
390
|
-
if (method->call_infos->object == Qnil)
|
391
|
-
{
|
576
|
+
if (method->call_infos->object == Qnil) {
|
392
577
|
method->call_infos->object = prof_call_infos_wrap(method->call_infos);
|
393
578
|
}
|
394
579
|
return method->call_infos->object;
|
395
580
|
}
|
396
581
|
|
582
|
+
/* call-seq:
|
583
|
+
recursive? -> boolean
|
584
|
+
|
585
|
+
Returns the true if this method is recursive */
|
586
|
+
static VALUE
|
587
|
+
prof_method_recursive(VALUE self)
|
588
|
+
{
|
589
|
+
prof_method_t *method = get_prof_method(self);
|
590
|
+
return method->recursive ? Qtrue : Qfalse;
|
591
|
+
}
|
592
|
+
|
593
|
+
/* call-seq:
|
594
|
+
source_klass -> klass
|
595
|
+
|
596
|
+
Returns the Ruby klass of the natural source-level definition. */
|
597
|
+
static VALUE
|
598
|
+
prof_source_klass(VALUE self)
|
599
|
+
{
|
600
|
+
prof_method_t *method = get_prof_method(self);
|
601
|
+
return resolve_source_klass(method);
|
602
|
+
}
|
603
|
+
|
604
|
+
/* call-seq:
|
605
|
+
calltree_name -> string
|
606
|
+
|
607
|
+
Returns the full name of this method in the calltree format.*/
|
608
|
+
|
609
|
+
static VALUE
|
610
|
+
prof_calltree_name(VALUE self)
|
611
|
+
{
|
612
|
+
prof_method_t *method = get_prof_method(self);
|
613
|
+
volatile VALUE source_klass = resolve_source_klass(method);
|
614
|
+
return calltree_name(source_klass, method->relation, method->key->mid);
|
615
|
+
}
|
616
|
+
|
397
617
|
void rp_init_method_info()
|
398
618
|
{
|
399
619
|
/* MethodInfo */
|
@@ -405,7 +625,12 @@ void rp_init_method_info()
|
|
405
625
|
rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
|
406
626
|
rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
|
407
627
|
rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
|
408
|
-
|
409
|
-
rb_define_method(cMethodInfo, "line", prof_method_line, 0);
|
628
|
+
|
410
629
|
rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
|
630
|
+
rb_define_method(cMethodInfo, "source_klass", prof_source_klass, 0);
|
631
|
+
rb_define_method(cMethodInfo, "source_file", prof_method_source_file, 0);
|
632
|
+
rb_define_method(cMethodInfo, "line", prof_method_line, 0);
|
633
|
+
|
634
|
+
rb_define_method(cMethodInfo, "recursive?", prof_method_recursive, 0);
|
635
|
+
rb_define_method(cMethodInfo, "calltree_name", prof_calltree_name, 0);
|
411
636
|
}
|