ruby-prof 0.18.0-x64-mingw32 → 1.1.0-x64-mingw32
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 +32 -0
- data/LICENSE +2 -2
- data/README.rdoc +1 -483
- data/Rakefile +3 -6
- data/bin/ruby-prof +65 -30
- data/ext/ruby_prof/extconf.rb +6 -38
- data/ext/ruby_prof/rp_allocation.c +279 -0
- data/ext/ruby_prof/rp_allocation.h +31 -0
- data/ext/ruby_prof/rp_call_info.c +129 -283
- data/ext/ruby_prof/rp_call_info.h +16 -34
- data/ext/ruby_prof/rp_measure_allocations.c +25 -49
- data/ext/ruby_prof/rp_measure_memory.c +21 -56
- data/ext/ruby_prof/rp_measure_process_time.c +35 -39
- data/ext/ruby_prof/rp_measure_wall_time.c +36 -19
- data/ext/ruby_prof/rp_measurement.c +230 -0
- data/ext/ruby_prof/rp_measurement.h +50 -0
- data/ext/ruby_prof/rp_method.c +389 -389
- data/ext/ruby_prof/rp_method.h +34 -39
- data/ext/ruby_prof/rp_profile.c +895 -0
- data/ext/ruby_prof/rp_profile.h +37 -0
- data/ext/ruby_prof/rp_stack.c +103 -80
- data/ext/ruby_prof/rp_stack.h +5 -12
- data/ext/ruby_prof/rp_thread.c +143 -83
- data/ext/ruby_prof/rp_thread.h +15 -6
- data/ext/ruby_prof/ruby_prof.c +11 -757
- data/ext/ruby_prof/ruby_prof.h +4 -47
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +10 -8
- data/lib/{2.6.3 → 2.6.5}/ruby_prof.so +0 -0
- data/lib/ruby-prof.rb +2 -18
- data/lib/ruby-prof/assets/call_stack_printer.html.erb +713 -0
- data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
- data/lib/ruby-prof/assets/graph_printer.html.erb +356 -0
- data/lib/ruby-prof/call_info.rb +35 -93
- data/lib/ruby-prof/call_info_visitor.rb +19 -21
- data/lib/ruby-prof/compatibility.rb +37 -107
- data/lib/ruby-prof/exclude_common_methods.rb +198 -0
- data/lib/ruby-prof/measurement.rb +14 -0
- data/lib/ruby-prof/method_info.rb +52 -83
- data/lib/ruby-prof/printers/abstract_printer.rb +73 -50
- data/lib/ruby-prof/printers/call_info_printer.rb +13 -3
- data/lib/ruby-prof/printers/call_stack_printer.rb +62 -145
- 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 +13 -15
- data/lib/ruby-prof/printers/multi_printer.rb +66 -23
- data/lib/ruby-prof/profile.rb +10 -3
- data/lib/ruby-prof/rack.rb +0 -3
- data/lib/ruby-prof/thread.rb +12 -12
- data/lib/ruby-prof/version.rb +1 -1
- data/ruby-prof.gemspec +2 -2
- data/test/abstract_printer_test.rb +0 -27
- data/test/alias_test.rb +129 -0
- data/test/basic_test.rb +41 -40
- data/test/call_info_visitor_test.rb +3 -3
- data/test/dynamic_method_test.rb +0 -2
- data/test/fiber_test.rb +11 -17
- data/test/gc_test.rb +96 -0
- data/test/line_number_test.rb +120 -39
- data/test/marshal_test.rb +119 -0
- data/test/measure_allocations.rb +30 -0
- data/test/measure_allocations_test.rb +371 -12
- data/test/measure_allocations_trace_test.rb +385 -0
- data/test/measure_memory_trace_test.rb +756 -0
- data/test/measure_process_time_test.rb +821 -33
- data/test/measure_times.rb +54 -0
- data/test/measure_wall_time_test.rb +349 -145
- data/test/multi_printer_test.rb +1 -34
- data/test/parser_timings.rb +24 -0
- data/test/pause_resume_test.rb +5 -5
- data/test/prime.rb +2 -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 +72 -77
- data/test/stack_printer_test.rb +2 -15
- data/test/start_stop_test.rb +22 -25
- data/test/test_helper.rb +5 -248
- data/test/thread_test.rb +11 -54
- data/test/unique_call_path_test.rb +16 -28
- data/test/yarv_test.rb +1 -0
- metadata +28 -36
- 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_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/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/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
@@ -0,0 +1,230 @@
|
|
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_measurement.h"
|
5
|
+
|
6
|
+
VALUE mMeasure;
|
7
|
+
VALUE cRpMeasurement;
|
8
|
+
|
9
|
+
prof_measurer_t* prof_measurer_allocations(bool track_allocations);
|
10
|
+
prof_measurer_t* prof_measurer_memory(bool track_allocations);
|
11
|
+
prof_measurer_t* prof_measurer_process_time(bool track_allocations);
|
12
|
+
prof_measurer_t* prof_measurer_wall_time(bool track_allocations);
|
13
|
+
|
14
|
+
void rp_init_measure_allocations(void);
|
15
|
+
void rp_init_measure_memory(void);
|
16
|
+
void rp_init_measure_process_time(void);
|
17
|
+
void rp_init_measure_wall_time(void);
|
18
|
+
|
19
|
+
prof_measurer_t* prof_get_measurer(prof_measure_mode_t measure, bool track_allocations)
|
20
|
+
{
|
21
|
+
switch (measure)
|
22
|
+
{
|
23
|
+
case MEASURE_WALL_TIME:
|
24
|
+
return prof_measurer_wall_time(track_allocations);
|
25
|
+
case MEASURE_PROCESS_TIME:
|
26
|
+
return prof_measurer_process_time(track_allocations);
|
27
|
+
case MEASURE_ALLOCATIONS:
|
28
|
+
return prof_measurer_allocations(track_allocations);
|
29
|
+
case MEASURE_MEMORY:
|
30
|
+
return prof_measurer_memory(track_allocations);
|
31
|
+
default:
|
32
|
+
rb_raise(rb_eArgError, "Unknown measure mode: %d", measure);
|
33
|
+
}
|
34
|
+
};
|
35
|
+
|
36
|
+
double prof_measure(prof_measurer_t* measurer, rb_trace_arg_t* trace_arg)
|
37
|
+
{
|
38
|
+
double measurement = measurer->measure(trace_arg);
|
39
|
+
return measurement * measurer->multiplier;
|
40
|
+
}
|
41
|
+
|
42
|
+
/* ======= prof_measurement_t ========*/
|
43
|
+
prof_measurement_t *prof_measurement_create(void)
|
44
|
+
{
|
45
|
+
prof_measurement_t* result = ALLOC(prof_measurement_t);
|
46
|
+
result->total_time = 0;
|
47
|
+
result->self_time = 0;
|
48
|
+
result->wait_time = 0;
|
49
|
+
result->called = 0;
|
50
|
+
result->object = Qnil;
|
51
|
+
return result;
|
52
|
+
}
|
53
|
+
|
54
|
+
static void
|
55
|
+
prof_measurement_ruby_gc_free(void *data)
|
56
|
+
{
|
57
|
+
prof_measurement_t* measurement = (prof_measurement_t*)data;
|
58
|
+
|
59
|
+
/* Has this measurement object been accessed by Ruby? If
|
60
|
+
yes clean it up so to avoid a segmentation fault. */
|
61
|
+
if (measurement->object != Qnil)
|
62
|
+
{
|
63
|
+
RDATA(measurement->object)->dmark = NULL;
|
64
|
+
RDATA(measurement->object)->dfree = NULL;
|
65
|
+
RDATA(measurement->object)->data = NULL;
|
66
|
+
measurement->object = Qnil;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
void
|
71
|
+
prof_measurement_free(prof_measurement_t* measurement)
|
72
|
+
{
|
73
|
+
prof_measurement_ruby_gc_free(measurement);
|
74
|
+
xfree(measurement);
|
75
|
+
}
|
76
|
+
|
77
|
+
size_t
|
78
|
+
prof_measurement_size(const void *data)
|
79
|
+
{
|
80
|
+
return sizeof(prof_measurement_t);
|
81
|
+
}
|
82
|
+
|
83
|
+
void
|
84
|
+
prof_measurement_mark(void *data)
|
85
|
+
{
|
86
|
+
prof_measurement_t* measurement = (prof_measurement_t*)data;
|
87
|
+
if (measurement->object != Qnil)
|
88
|
+
rb_gc_mark(measurement->object);
|
89
|
+
}
|
90
|
+
|
91
|
+
VALUE
|
92
|
+
prof_measurement_wrap(prof_measurement_t* measurement)
|
93
|
+
{
|
94
|
+
if (measurement->object == Qnil)
|
95
|
+
{
|
96
|
+
measurement->object = Data_Wrap_Struct(cRpMeasurement, prof_measurement_mark, prof_measurement_ruby_gc_free, measurement);
|
97
|
+
}
|
98
|
+
return measurement->object;
|
99
|
+
}
|
100
|
+
|
101
|
+
static VALUE
|
102
|
+
prof_measurement_allocate(VALUE klass)
|
103
|
+
{
|
104
|
+
prof_measurement_t *measurement = prof_measurement_create();
|
105
|
+
measurement->object = prof_measurement_wrap(measurement);
|
106
|
+
return measurement->object;
|
107
|
+
}
|
108
|
+
|
109
|
+
prof_measurement_t*
|
110
|
+
prof_get_measurement(VALUE self)
|
111
|
+
{
|
112
|
+
/* Can't use Data_Get_Struct because that triggers the event hook
|
113
|
+
ending up in endless recursion. */
|
114
|
+
prof_measurement_t* result = DATA_PTR(self);
|
115
|
+
|
116
|
+
if (!result)
|
117
|
+
rb_raise(rb_eRuntimeError, "This RubyProf::Measurement instance has already been freed, likely because its profile has been freed.");
|
118
|
+
|
119
|
+
return result;
|
120
|
+
}
|
121
|
+
|
122
|
+
/* call-seq:
|
123
|
+
total_time -> float
|
124
|
+
|
125
|
+
Returns the total amount of time spent in this method and its children. */
|
126
|
+
static VALUE
|
127
|
+
prof_measurement_total_time(VALUE self)
|
128
|
+
{
|
129
|
+
prof_measurement_t* result = prof_get_measurement(self);
|
130
|
+
return rb_float_new(result->total_time);
|
131
|
+
}
|
132
|
+
|
133
|
+
/* call-seq:
|
134
|
+
self_time -> float
|
135
|
+
|
136
|
+
Returns the total amount of time spent in this method. */
|
137
|
+
static VALUE
|
138
|
+
prof_measurement_self_time(VALUE self)
|
139
|
+
{
|
140
|
+
prof_measurement_t* result = prof_get_measurement(self);
|
141
|
+
|
142
|
+
return rb_float_new(result->self_time);
|
143
|
+
}
|
144
|
+
|
145
|
+
/* call-seq:
|
146
|
+
wait_time -> float
|
147
|
+
|
148
|
+
Returns the total amount of time this method waited for other threads. */
|
149
|
+
static VALUE
|
150
|
+
prof_measurement_wait_time(VALUE self)
|
151
|
+
{
|
152
|
+
prof_measurement_t* result = prof_get_measurement(self);
|
153
|
+
|
154
|
+
return rb_float_new(result->wait_time);
|
155
|
+
}
|
156
|
+
|
157
|
+
/* call-seq:
|
158
|
+
called -> int
|
159
|
+
|
160
|
+
Returns the total amount of times this method was called. */
|
161
|
+
static VALUE
|
162
|
+
prof_measurement_called(VALUE self)
|
163
|
+
{
|
164
|
+
prof_measurement_t *result = prof_get_measurement(self);
|
165
|
+
return INT2NUM(result->called);
|
166
|
+
}
|
167
|
+
|
168
|
+
/* call-seq:
|
169
|
+
called=n -> n
|
170
|
+
|
171
|
+
Sets the call count to n. */
|
172
|
+
static VALUE
|
173
|
+
prof_measurement_set_called(VALUE self, VALUE called)
|
174
|
+
{
|
175
|
+
prof_measurement_t *result = prof_get_measurement(self);
|
176
|
+
result->called = NUM2INT(called);
|
177
|
+
return called;
|
178
|
+
}
|
179
|
+
|
180
|
+
/* :nodoc: */
|
181
|
+
static VALUE
|
182
|
+
prof_measurement_dump(VALUE self)
|
183
|
+
{
|
184
|
+
prof_measurement_t* measurement_data = prof_get_measurement(self);
|
185
|
+
VALUE result = rb_hash_new();
|
186
|
+
|
187
|
+
rb_hash_aset(result, ID2SYM(rb_intern("total_time")), rb_float_new(measurement_data->total_time));
|
188
|
+
rb_hash_aset(result, ID2SYM(rb_intern("self_time")), rb_float_new(measurement_data->self_time));
|
189
|
+
rb_hash_aset(result, ID2SYM(rb_intern("wait_time")), rb_float_new(measurement_data->wait_time));
|
190
|
+
rb_hash_aset(result, ID2SYM(rb_intern("called")), INT2FIX(measurement_data->called));
|
191
|
+
|
192
|
+
return result;
|
193
|
+
}
|
194
|
+
|
195
|
+
/* :nodoc: */
|
196
|
+
static VALUE
|
197
|
+
prof_measurement_load(VALUE self, VALUE data)
|
198
|
+
{
|
199
|
+
prof_measurement_t* measurement = prof_get_measurement(self);
|
200
|
+
measurement->object = self;
|
201
|
+
|
202
|
+
measurement->total_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("total_time"))));
|
203
|
+
measurement->self_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("self_time"))));
|
204
|
+
measurement->wait_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("wait_time"))));
|
205
|
+
measurement->called = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("called"))));
|
206
|
+
|
207
|
+
return data;
|
208
|
+
}
|
209
|
+
|
210
|
+
void rp_init_measure()
|
211
|
+
{
|
212
|
+
mMeasure = rb_define_module_under(mProf, "Measure");
|
213
|
+
rp_init_measure_wall_time();
|
214
|
+
rp_init_measure_process_time();
|
215
|
+
rp_init_measure_allocations();
|
216
|
+
rp_init_measure_memory();
|
217
|
+
|
218
|
+
cRpMeasurement = rb_define_class_under(mProf, "Measurement", rb_cData);
|
219
|
+
rb_undef_method(CLASS_OF(cRpMeasurement), "new");
|
220
|
+
rb_define_alloc_func(cRpMeasurement, prof_measurement_allocate);
|
221
|
+
|
222
|
+
rb_define_method(cRpMeasurement, "called", prof_measurement_called, 0);
|
223
|
+
rb_define_method(cRpMeasurement, "called=", prof_measurement_set_called, 1);
|
224
|
+
rb_define_method(cRpMeasurement, "total_time", prof_measurement_total_time, 0);
|
225
|
+
rb_define_method(cRpMeasurement, "self_time", prof_measurement_self_time, 0);
|
226
|
+
rb_define_method(cRpMeasurement, "wait_time", prof_measurement_wait_time, 0);
|
227
|
+
|
228
|
+
rb_define_method(cRpMeasurement, "_dump_data", prof_measurement_dump, 0);
|
229
|
+
rb_define_method(cRpMeasurement, "_load_data", prof_measurement_load, 1);
|
230
|
+
}
|
@@ -0,0 +1,50 @@
|
|
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_measurementMENT_H__
|
5
|
+
#define __rp_measurementMENT_H__
|
6
|
+
|
7
|
+
#include "ruby_prof.h"
|
8
|
+
|
9
|
+
extern VALUE mMeasure;
|
10
|
+
|
11
|
+
typedef double (*get_measurement)(rb_trace_arg_t *trace_arg);
|
12
|
+
|
13
|
+
typedef enum
|
14
|
+
{
|
15
|
+
MEASURE_WALL_TIME,
|
16
|
+
MEASURE_PROCESS_TIME,
|
17
|
+
MEASURE_ALLOCATIONS,
|
18
|
+
MEASURE_MEMORY
|
19
|
+
} prof_measure_mode_t;
|
20
|
+
|
21
|
+
typedef struct
|
22
|
+
{
|
23
|
+
get_measurement measure;
|
24
|
+
prof_measure_mode_t mode;
|
25
|
+
double multiplier;
|
26
|
+
bool track_allocations;
|
27
|
+
} prof_measurer_t;
|
28
|
+
|
29
|
+
/* Callers and callee information for a method. */
|
30
|
+
typedef struct prof_measurement_t
|
31
|
+
{
|
32
|
+
double total_time;
|
33
|
+
double self_time;
|
34
|
+
double wait_time;
|
35
|
+
int called;
|
36
|
+
VALUE object;
|
37
|
+
} prof_measurement_t;
|
38
|
+
|
39
|
+
prof_measurer_t *prof_get_measurer(prof_measure_mode_t measure, bool track_allocations);
|
40
|
+
double prof_measure(prof_measurer_t *measurer, rb_trace_arg_t* trace_arg);
|
41
|
+
|
42
|
+
prof_measurement_t *prof_measurement_create(void);
|
43
|
+
void prof_measurement_free(prof_measurement_t* measurement);
|
44
|
+
VALUE prof_measurement_wrap(prof_measurement_t *measurement);
|
45
|
+
prof_measurement_t* prof_get_measurement(VALUE self);
|
46
|
+
void prof_measurement_mark(void *data);
|
47
|
+
|
48
|
+
void rp_init_measure(void);
|
49
|
+
|
50
|
+
#endif //__rp_measurementMENT_H__
|
data/ext/ruby_prof/rp_method.c
CHANGED
@@ -1,250 +1,229 @@
|
|
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_info.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
|
-
|
11
|
+
VALUE
|
12
|
+
resolve_klass(VALUE klass, unsigned int *klass_flags)
|
17
13
|
{
|
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__");
|
14
|
+
VALUE result = klass;
|
25
15
|
|
26
|
-
|
27
|
-
if (BUILTIN_TYPE(attached) == T_CLASS)
|
16
|
+
if (klass == 0 || klass == Qnil)
|
28
17
|
{
|
29
|
-
|
30
|
-
result = rb_str_new2("<Class::");
|
31
|
-
rb_str_append(result, attached_str);
|
32
|
-
rb_str_cat2(result, ">");
|
18
|
+
result = Qnil;
|
33
19
|
}
|
34
|
-
|
35
|
-
/* Is this for singleton methods on a module? */
|
36
|
-
else if (BUILTIN_TYPE(attached) == T_MODULE)
|
20
|
+
else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
|
37
21
|
{
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
rb_str_cat2(result, ">");
|
42
|
-
}
|
22
|
+
/* We have come across a singleton object. First
|
23
|
+
figure out what it is attached to.*/
|
24
|
+
VALUE attached = rb_iv_get(klass, "__attached__");
|
43
25
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
26
|
+
/* Is this a singleton class acting as a metaclass? */
|
27
|
+
if (BUILTIN_TYPE(attached) == T_CLASS)
|
28
|
+
{
|
29
|
+
*klass_flags |= kClassSingleton;
|
30
|
+
result = attached;
|
31
|
+
}
|
32
|
+
/* Is this for singleton methods on a module? */
|
33
|
+
else if (BUILTIN_TYPE(attached) == T_MODULE)
|
34
|
+
{
|
35
|
+
*klass_flags |= kModuleSingleton;
|
36
|
+
result = attached;
|
37
|
+
}
|
38
|
+
/* Is this for singleton methods on an object? */
|
39
|
+
else if (BUILTIN_TYPE(attached) == T_OBJECT)
|
40
|
+
{
|
41
|
+
*klass_flags |= kObjectSingleton;
|
42
|
+
result = rb_class_superclass(klass);
|
43
|
+
}
|
44
|
+
/* Ok, this could be other things like an array made put onto
|
45
|
+
a singleton object (yeah, it happens, see the singleton
|
46
|
+
objects test case). */
|
47
|
+
else
|
48
|
+
{
|
49
|
+
*klass_flags |= kOtherSingleton;
|
50
|
+
result = klass;
|
51
|
+
}
|
55
52
|
}
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
else
|
53
|
+
/* Is this an include for a module? If so get the actual
|
54
|
+
module class since we want to combine all profiling
|
55
|
+
results for that module. */
|
56
|
+
else if (BUILTIN_TYPE(klass) == T_ICLASS)
|
61
57
|
{
|
62
|
-
|
58
|
+
unsigned int dummy;
|
59
|
+
*klass_flags |= kModuleIncludee;
|
60
|
+
result = resolve_klass(RBASIC(klass)->klass, &dummy);
|
63
61
|
}
|
64
|
-
|
65
62
|
return result;
|
66
63
|
}
|
67
64
|
|
68
|
-
|
69
|
-
|
65
|
+
VALUE
|
66
|
+
resolve_klass_name(VALUE klass, unsigned int* klass_flags)
|
70
67
|
{
|
71
|
-
|
68
|
+
VALUE result = Qnil;
|
72
69
|
|
73
|
-
if (klass ==
|
70
|
+
if (klass == Qnil)
|
74
71
|
{
|
75
72
|
result = rb_str_new2("[global]");
|
76
73
|
}
|
77
|
-
else if (
|
74
|
+
else if (*klass_flags & kOtherSingleton)
|
78
75
|
{
|
79
|
-
result =
|
80
|
-
}
|
81
|
-
else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
|
82
|
-
{
|
83
|
-
result = figure_singleton_name(klass);
|
84
|
-
}
|
85
|
-
else if (BUILTIN_TYPE(klass) == T_CLASS)
|
86
|
-
{
|
87
|
-
result = rb_class_name(klass);
|
76
|
+
result = rb_any_to_s(klass);
|
88
77
|
}
|
89
78
|
else
|
90
79
|
{
|
91
|
-
|
92
|
-
result = rb_str_new2("[unknown]");
|
80
|
+
result = rb_class_name(klass);
|
93
81
|
}
|
94
82
|
|
95
83
|
return result;
|
96
84
|
}
|
97
85
|
|
98
|
-
|
99
|
-
|
86
|
+
st_data_t
|
87
|
+
method_key(VALUE klass, VALUE msym)
|
100
88
|
{
|
101
|
-
|
89
|
+
VALUE resolved_klass = klass;
|
102
90
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
91
|
+
/* Is this an include for a module? If so get the actual
|
92
|
+
module class since we want to combine all profiling
|
93
|
+
results for that module. */
|
94
|
+
if (klass == 0 || klass == Qnil)
|
95
|
+
{
|
96
|
+
resolved_klass = Qnil;
|
97
|
+
}
|
98
|
+
else if (BUILTIN_TYPE(klass) == T_ICLASS)
|
99
|
+
{
|
100
|
+
resolved_klass = RBASIC(klass)->klass;
|
108
101
|
}
|
102
|
+
|
103
|
+
return (resolved_klass << 4) + (msym);
|
109
104
|
}
|
110
105
|
|
111
|
-
|
112
|
-
|
106
|
+
/* ====== Allocation Table ====== */
|
107
|
+
st_table*
|
108
|
+
allocations_table_create()
|
113
109
|
{
|
114
|
-
|
115
|
-
|
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);
|
110
|
+
return st_init_numtable();
|
111
|
+
}
|
123
112
|
|
124
|
-
|
113
|
+
static int
|
114
|
+
allocations_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
|
115
|
+
{
|
116
|
+
prof_allocation_free((prof_allocation_t*)value);
|
117
|
+
return ST_CONTINUE;
|
125
118
|
}
|
126
119
|
|
127
|
-
static
|
128
|
-
|
120
|
+
static int
|
121
|
+
prof_method_collect_allocations(st_data_t key, st_data_t value, st_data_t result)
|
129
122
|
{
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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;
|
123
|
+
prof_allocation_t* allocation = (prof_allocation_t*)value;
|
124
|
+
VALUE arr = (VALUE)result;
|
125
|
+
rb_ary_push(arr, prof_allocation_wrap(allocation));
|
126
|
+
return ST_CONTINUE;
|
141
127
|
}
|
142
128
|
|
143
|
-
static
|
144
|
-
|
129
|
+
static int
|
130
|
+
prof_method_mark_allocations(st_data_t key, st_data_t value, st_data_t data)
|
145
131
|
{
|
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;
|
132
|
+
prof_allocation_t* allocation = (prof_allocation_t*)value;
|
133
|
+
prof_allocation_mark(allocation);
|
134
|
+
return ST_CONTINUE;
|
170
135
|
}
|
171
136
|
|
172
137
|
void
|
173
|
-
|
138
|
+
allocations_table_free(st_table* table)
|
174
139
|
{
|
175
|
-
|
176
|
-
|
177
|
-
results for that module. */
|
178
|
-
if (klass != 0 && BUILTIN_TYPE(klass) == T_ICLASS) {
|
179
|
-
klass = RBASIC(klass)->klass;
|
180
|
-
}
|
181
|
-
|
182
|
-
key->klass = klass;
|
183
|
-
key->mid = mid;
|
184
|
-
key->key = (klass << 4) + (mid << 2);
|
140
|
+
st_foreach(table, allocations_table_free_iterator, 0);
|
141
|
+
st_free_table(table);
|
185
142
|
}
|
186
143
|
|
187
144
|
/* ================ prof_method_t =================*/
|
188
|
-
|
189
|
-
|
190
|
-
prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
|
145
|
+
static prof_method_t*
|
146
|
+
prof_get_method(VALUE self)
|
191
147
|
{
|
192
|
-
|
148
|
+
/* Can't use Data_Get_Struct because that triggers the event hook
|
149
|
+
ending up in endless recursion. */
|
150
|
+
prof_method_t* result = DATA_PTR(self);
|
193
151
|
|
194
|
-
|
195
|
-
|
152
|
+
if (!result)
|
153
|
+
rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
|
196
154
|
|
197
|
-
result
|
198
|
-
|
155
|
+
return result;
|
156
|
+
}
|
199
157
|
|
200
|
-
|
158
|
+
prof_method_t*
|
159
|
+
prof_method_create(VALUE klass, VALUE msym, VALUE source_file, int source_line)
|
160
|
+
{
|
161
|
+
prof_method_t *result = ALLOC(prof_method_t);
|
162
|
+
result->key = method_key(klass, msym);
|
163
|
+
result->klass_flags = 0;
|
164
|
+
|
165
|
+
/* Note we do not call resolve_klass_name now because that causes an object allocation that shows up
|
166
|
+
in the allocation results so we want to avoid it until after the profile run is complete. */
|
167
|
+
result->klass = resolve_klass(klass, &result->klass_flags);
|
168
|
+
result->klass_name = Qnil;
|
169
|
+
result->method_name = msym;
|
170
|
+
result->measurement = prof_measurement_create();
|
171
|
+
|
172
|
+
result->root = false;
|
173
|
+
result->excluded = false;
|
174
|
+
|
175
|
+
result->parent_call_infos = method_table_create();
|
176
|
+
result->child_call_infos = method_table_create();
|
177
|
+
result->allocations_table = allocations_table_create();
|
178
|
+
|
201
179
|
result->visits = 0;
|
180
|
+
result->recursive = false;
|
202
181
|
|
203
182
|
result->object = Qnil;
|
204
183
|
|
205
|
-
|
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;
|
218
|
-
|
219
|
-
result->resolved = 0;
|
220
|
-
result->relation = 0;
|
184
|
+
result->source_file = source_file;
|
185
|
+
result->source_line = source_line;
|
221
186
|
|
222
187
|
return result;
|
223
188
|
}
|
224
189
|
|
225
190
|
prof_method_t*
|
226
|
-
prof_method_create_excluded(VALUE klass,
|
191
|
+
prof_method_create_excluded(VALUE klass, VALUE msym)
|
227
192
|
{
|
228
|
-
prof_method_t
|
229
|
-
|
230
|
-
result->key = ALLOC(prof_method_key_t);
|
231
|
-
method_key(result->key, klass, mid);
|
232
|
-
|
233
|
-
/* Invisible with this flag set. */
|
193
|
+
prof_method_t* result = prof_method_create(klass, msym, Qnil, 0);
|
234
194
|
result->excluded = 1;
|
235
|
-
result
|
195
|
+
return result;
|
196
|
+
}
|
236
197
|
|
237
|
-
|
238
|
-
|
198
|
+
static int
|
199
|
+
prof_method_collect_call_infos(st_data_t key, st_data_t value, st_data_t result)
|
200
|
+
{
|
201
|
+
prof_call_info_t* call_info = (prof_call_info_t*)value;
|
202
|
+
VALUE arr = (VALUE)result;
|
203
|
+
rb_ary_push(arr, prof_call_info_wrap(call_info));
|
204
|
+
return ST_CONTINUE;
|
205
|
+
}
|
239
206
|
|
240
|
-
|
241
|
-
|
242
|
-
|
207
|
+
static int
|
208
|
+
prof_method_mark_call_infos(st_data_t key, st_data_t value, st_data_t data)
|
209
|
+
{
|
210
|
+
prof_call_info_t* call_info = (prof_call_info_t*)value;
|
211
|
+
prof_call_info_mark(call_info);
|
212
|
+
return ST_CONTINUE;
|
213
|
+
}
|
243
214
|
|
244
|
-
|
245
|
-
|
215
|
+
static int
|
216
|
+
call_infos_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
|
217
|
+
{
|
218
|
+
prof_call_info_free((prof_call_info_t*)value);
|
219
|
+
return ST_CONTINUE;
|
220
|
+
}
|
246
221
|
|
247
|
-
|
222
|
+
void
|
223
|
+
call_info_table_free(st_table* table)
|
224
|
+
{
|
225
|
+
st_foreach(table, call_infos_free_iterator, 0);
|
226
|
+
st_free_table(table);
|
248
227
|
}
|
249
228
|
|
250
229
|
/* The underlying c structures are freed when the parent profile is freed.
|
@@ -254,180 +233,101 @@ prof_method_create_excluded(VALUE klass, ID mid)
|
|
254
233
|
for the garbage collector so that if it does get called will nil
|
255
234
|
out our Ruby object reference.*/
|
256
235
|
static void
|
257
|
-
prof_method_ruby_gc_free(
|
236
|
+
prof_method_ruby_gc_free(void *data)
|
258
237
|
{
|
259
|
-
|
238
|
+
prof_method_t* method = (prof_method_t*)data;
|
239
|
+
|
240
|
+
/* Has this method object been accessed by Ruby? If
|
260
241
|
yes clean it up so to avoid a segmentation fault. */
|
261
242
|
if (method->object != Qnil)
|
262
243
|
{
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
244
|
+
RDATA(method->object)->dmark = NULL;
|
245
|
+
RDATA(method->object)->dfree = NULL;
|
246
|
+
RDATA(method->object)->data = NULL;
|
247
|
+
method->object = Qnil;
|
248
|
+
}
|
249
|
+
method->klass_name = Qnil;
|
250
|
+
method->method_name = Qnil;
|
268
251
|
}
|
269
252
|
|
270
253
|
static void
|
271
254
|
prof_method_free(prof_method_t* method)
|
272
255
|
{
|
273
256
|
prof_method_ruby_gc_free(method);
|
257
|
+
allocations_table_free(method->allocations_table);
|
274
258
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
xfree(method->key);
|
259
|
+
/* Remember call infos are referenced by their parent method and child method, so we only want
|
260
|
+
to iterate over one of them to avoid a double freeing */
|
261
|
+
call_info_table_free(method->parent_call_infos);
|
262
|
+
xfree(method->child_call_infos);
|
281
263
|
|
282
|
-
|
283
|
-
|
264
|
+
prof_measurement_free(method->measurement);
|
265
|
+
xfree(method);
|
266
|
+
}
|
284
267
|
|
285
|
-
|
268
|
+
size_t
|
269
|
+
prof_method_size(const void *data)
|
270
|
+
{
|
271
|
+
return sizeof(prof_method_t);
|
286
272
|
}
|
287
273
|
|
288
274
|
void
|
289
|
-
prof_method_mark(
|
275
|
+
prof_method_mark(void *data)
|
290
276
|
{
|
291
|
-
|
292
|
-
|
293
|
-
|
277
|
+
prof_method_t* method = (prof_method_t*)data;
|
278
|
+
rb_gc_mark(method->klass_name);
|
279
|
+
rb_gc_mark(method->method_name);
|
280
|
+
|
281
|
+
if (method->klass != Qnil)
|
282
|
+
rb_gc_mark(method->klass);
|
294
283
|
|
295
|
-
|
296
|
-
rb_gc_mark(method->source_klass);
|
297
|
-
}
|
298
|
-
|
299
|
-
if (method->object) {
|
284
|
+
if (method->object != Qnil)
|
300
285
|
rb_gc_mark(method->object);
|
301
|
-
}
|
302
286
|
|
303
|
-
|
304
|
-
|
305
|
-
|
287
|
+
prof_measurement_mark(method->measurement);
|
288
|
+
|
289
|
+
st_foreach(method->parent_call_infos, prof_method_mark_call_infos, 0);
|
290
|
+
st_foreach(method->child_call_infos, prof_method_mark_call_infos, 0);
|
291
|
+
st_foreach(method->allocations_table, prof_method_mark_allocations, 0);
|
306
292
|
}
|
307
293
|
|
308
294
|
static VALUE
|
309
|
-
|
295
|
+
prof_method_allocate(VALUE klass)
|
310
296
|
{
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
/* We want to group methods according to their source-level
|
316
|
-
definitions, not their implementation class. Follow module
|
317
|
-
inclusions and singleton classes back to a meaningful root
|
318
|
-
while keeping track of these relationships. */
|
319
|
-
|
320
|
-
if (method->resolved) {
|
321
|
-
return method->source_klass;
|
322
|
-
}
|
323
|
-
|
324
|
-
klass = method->key->klass;
|
325
|
-
relation = 0;
|
326
|
-
|
327
|
-
while (1)
|
328
|
-
{
|
329
|
-
/* This is a global/unknown class */
|
330
|
-
if (klass == 0 || klass == Qnil)
|
331
|
-
break;
|
332
|
-
|
333
|
-
/* Is this a singleton class? (most common case) */
|
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;
|
297
|
+
prof_method_t* method_data = prof_method_create(Qnil, Qnil, Qnil, 0);
|
298
|
+
method_data->object = prof_method_wrap(method_data);
|
299
|
+
return method_data->object;
|
384
300
|
}
|
385
301
|
|
386
302
|
VALUE
|
387
|
-
prof_method_wrap(prof_method_t *
|
303
|
+
prof_method_wrap(prof_method_t *method)
|
388
304
|
{
|
389
|
-
if (
|
390
|
-
|
305
|
+
if (method->object == Qnil)
|
306
|
+
{
|
307
|
+
method->object = Data_Wrap_Struct(cRpMethodInfo, prof_method_mark, prof_method_ruby_gc_free, method);
|
391
308
|
}
|
392
|
-
return
|
309
|
+
return method->object;
|
393
310
|
}
|
394
311
|
|
395
|
-
|
396
|
-
|
312
|
+
prof_method_t *
|
313
|
+
prof_method_get(VALUE self)
|
397
314
|
{
|
398
315
|
/* Can't use Data_Get_Struct because that triggers the event hook
|
399
316
|
ending up in endless recursion. */
|
400
317
|
prof_method_t* result = DATA_PTR(self);
|
401
318
|
|
402
|
-
if (!result)
|
319
|
+
if (!result)
|
320
|
+
{
|
403
321
|
rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
|
404
322
|
}
|
405
323
|
|
406
324
|
return result;
|
407
325
|
}
|
408
326
|
|
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;
|
420
|
-
}
|
421
|
-
|
422
|
-
struct st_hash_type type_method_hash = {
|
423
|
-
method_table_cmp,
|
424
|
-
method_table_hash
|
425
|
-
};
|
426
|
-
|
427
327
|
st_table *
|
428
328
|
method_table_create()
|
429
329
|
{
|
430
|
-
|
330
|
+
return st_init_numtable();
|
431
331
|
}
|
432
332
|
|
433
333
|
static int
|
@@ -445,18 +345,21 @@ method_table_free(st_table *table)
|
|
445
345
|
}
|
446
346
|
|
447
347
|
size_t
|
448
|
-
method_table_insert(st_table *table,
|
348
|
+
method_table_insert(st_table *table, st_data_t key, prof_method_t *val)
|
449
349
|
{
|
450
|
-
|
350
|
+
return st_insert(table, (st_data_t) key, (st_data_t) val);
|
451
351
|
}
|
452
352
|
|
453
353
|
prof_method_t *
|
454
|
-
method_table_lookup(st_table *table,
|
354
|
+
method_table_lookup(st_table *table, st_data_t key)
|
455
355
|
{
|
456
356
|
st_data_t val;
|
457
|
-
if (st_lookup(table, (st_data_t)key, &val))
|
357
|
+
if (st_lookup(table, (st_data_t)key, &val))
|
358
|
+
{
|
458
359
|
return (prof_method_t *) val;
|
459
|
-
}
|
360
|
+
}
|
361
|
+
else
|
362
|
+
{
|
460
363
|
return NULL;
|
461
364
|
}
|
462
365
|
}
|
@@ -472,52 +375,75 @@ the RubyProf::Profile object.
|
|
472
375
|
*/
|
473
376
|
|
474
377
|
/* call-seq:
|
475
|
-
|
378
|
+
callers -> array
|
476
379
|
|
477
|
-
|
380
|
+
Returns an array of call info objects that called this method (ie, parents).*/
|
478
381
|
static VALUE
|
479
|
-
|
382
|
+
prof_method_callers(VALUE self)
|
480
383
|
{
|
481
|
-
|
482
|
-
|
384
|
+
prof_method_t* method = prof_get_method(self);
|
385
|
+
VALUE result = rb_ary_new();
|
386
|
+
st_foreach(method->parent_call_infos, prof_method_collect_call_infos, result);
|
387
|
+
return result;
|
483
388
|
}
|
484
389
|
|
485
390
|
/* call-seq:
|
486
|
-
|
391
|
+
callees -> array
|
487
392
|
|
488
|
-
|
489
|
-
|
490
|
-
|
393
|
+
Returns an array of call info objects that this method called (ie, children).*/
|
394
|
+
static VALUE
|
395
|
+
prof_method_callees(VALUE self)
|
491
396
|
{
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
return rb_str_new2(sf);
|
497
|
-
}
|
397
|
+
prof_method_t* method = prof_get_method(self);
|
398
|
+
VALUE result = rb_ary_new();
|
399
|
+
st_foreach(method->child_call_infos, prof_method_collect_call_infos, result);
|
400
|
+
return result;
|
498
401
|
}
|
499
402
|
|
403
|
+
/* call-seq:
|
404
|
+
allocations -> array
|
405
|
+
|
406
|
+
Returns an array of allocation information.*/
|
407
|
+
static VALUE
|
408
|
+
prof_method_allocations(VALUE self)
|
409
|
+
{
|
410
|
+
prof_method_t* method = prof_get_method(self);
|
411
|
+
VALUE result = rb_ary_new();
|
412
|
+
st_foreach(method->allocations_table, prof_method_collect_allocations, result);
|
413
|
+
return result;
|
414
|
+
}
|
500
415
|
|
501
416
|
/* call-seq:
|
502
|
-
|
417
|
+
called -> Measurement
|
503
418
|
|
504
|
-
Returns the
|
419
|
+
Returns the measurement associated with this method. */
|
505
420
|
static VALUE
|
506
|
-
|
421
|
+
prof_method_measurement(VALUE self)
|
422
|
+
{
|
423
|
+
prof_method_t* method = prof_get_method(self);
|
424
|
+
return prof_measurement_wrap(method->measurement);
|
425
|
+
}
|
426
|
+
|
427
|
+
/* call-seq:
|
428
|
+
source_file => string
|
429
|
+
|
430
|
+
return the source file of the method
|
431
|
+
*/
|
432
|
+
static VALUE prof_method_source_file(VALUE self)
|
507
433
|
{
|
508
|
-
prof_method_t
|
509
|
-
return
|
434
|
+
prof_method_t* method = prof_method_get(self);
|
435
|
+
return method->source_file;
|
510
436
|
}
|
511
437
|
|
512
438
|
/* call-seq:
|
513
|
-
|
439
|
+
line_no -> int
|
514
440
|
|
515
|
-
|
441
|
+
returns the line number of the method */
|
516
442
|
static VALUE
|
517
|
-
|
443
|
+
prof_method_line(VALUE self)
|
518
444
|
{
|
519
|
-
prof_method_t
|
520
|
-
return
|
445
|
+
prof_method_t* method = prof_method_get(self);
|
446
|
+
return INT2FIX(method->source_line);
|
521
447
|
}
|
522
448
|
|
523
449
|
/* call-seq:
|
@@ -527,104 +453,178 @@ Returns the name of this method's class. Singleton classes
|
|
527
453
|
will have the form <Object::Object>. */
|
528
454
|
|
529
455
|
static VALUE
|
530
|
-
|
456
|
+
prof_method_klass_name(VALUE self)
|
531
457
|
{
|
532
|
-
prof_method_t *method =
|
533
|
-
|
458
|
+
prof_method_t *method = prof_method_get(self);
|
459
|
+
if (method->klass_name == Qnil)
|
460
|
+
method->klass_name = resolve_klass_name(method->klass, &method->klass_flags);
|
461
|
+
|
462
|
+
return method->klass_name;
|
534
463
|
}
|
535
464
|
|
536
465
|
/* call-seq:
|
537
|
-
|
466
|
+
klass_flags -> integer
|
538
467
|
|
539
|
-
Returns the
|
540
|
-
methods will be returned in the format <Object::Object>#method.*/
|
468
|
+
Returns the klass flags */
|
541
469
|
|
542
470
|
static VALUE
|
543
|
-
|
471
|
+
prof_method_klass_flags(VALUE self)
|
544
472
|
{
|
545
|
-
prof_method_t
|
546
|
-
return
|
473
|
+
prof_method_t* method = prof_method_get(self);
|
474
|
+
return INT2FIX(method->klass_flags);
|
547
475
|
}
|
548
476
|
|
549
477
|
/* call-seq:
|
550
|
-
|
478
|
+
method_name -> string
|
551
479
|
|
552
|
-
Returns the
|
480
|
+
Returns the name of this method in the format Object#method. Singletons
|
481
|
+
methods will be returned in the format <Object::Object>#method.*/
|
553
482
|
|
554
483
|
static VALUE
|
555
|
-
|
484
|
+
prof_method_name(VALUE self)
|
556
485
|
{
|
557
|
-
prof_method_t *method =
|
558
|
-
return
|
486
|
+
prof_method_t *method = prof_method_get(self);
|
487
|
+
return method->method_name;
|
559
488
|
}
|
560
489
|
|
561
490
|
/* call-seq:
|
562
|
-
|
491
|
+
root? -> boolean
|
563
492
|
|
564
|
-
Returns
|
565
|
-
about the current method.*/
|
493
|
+
Returns the true if this method is at the top of the call stack */
|
566
494
|
static VALUE
|
567
|
-
|
495
|
+
prof_method_root(VALUE self)
|
568
496
|
{
|
569
|
-
prof_method_t *method =
|
570
|
-
|
571
|
-
method->call_infos->object = prof_call_infos_wrap(method->call_infos);
|
572
|
-
}
|
573
|
-
return method->call_infos->object;
|
497
|
+
prof_method_t *method = prof_method_get(self);
|
498
|
+
return method->root ? Qtrue : Qfalse;
|
574
499
|
}
|
575
500
|
|
576
501
|
/* call-seq:
|
577
502
|
recursive? -> boolean
|
578
503
|
|
579
|
-
Returns the true if this method is
|
504
|
+
Returns the true if this method is recursively invoked */
|
580
505
|
static VALUE
|
581
506
|
prof_method_recursive(VALUE self)
|
582
507
|
{
|
583
|
-
|
584
|
-
|
508
|
+
prof_method_t* method = prof_method_get(self);
|
509
|
+
return method->recursive ? Qtrue : Qfalse;
|
585
510
|
}
|
586
511
|
|
587
512
|
/* call-seq:
|
588
|
-
|
513
|
+
excluded? -> boolean
|
589
514
|
|
590
|
-
Returns the
|
515
|
+
Returns the true if this method was excluded */
|
591
516
|
static VALUE
|
592
|
-
|
517
|
+
prof_method_excluded(VALUE self)
|
593
518
|
{
|
594
|
-
prof_method_t
|
595
|
-
return
|
519
|
+
prof_method_t* method = prof_method_get(self);
|
520
|
+
return method->excluded ? Qtrue : Qfalse;
|
596
521
|
}
|
597
522
|
|
598
|
-
/*
|
599
|
-
|
523
|
+
/* :nodoc: */
|
524
|
+
static VALUE
|
525
|
+
prof_method_dump(VALUE self)
|
526
|
+
{
|
527
|
+
prof_method_t* method_data = DATA_PTR(self);
|
528
|
+
VALUE result = rb_hash_new();
|
529
|
+
|
530
|
+
rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_method_klass_name(self));
|
531
|
+
rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(method_data->klass_flags));
|
532
|
+
rb_hash_aset(result, ID2SYM(rb_intern("method_name")), method_data->method_name);
|
600
533
|
|
601
|
-
|
534
|
+
rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(method_data->key));
|
535
|
+
rb_hash_aset(result, ID2SYM(rb_intern("root")), prof_method_root(self));
|
536
|
+
rb_hash_aset(result, ID2SYM(rb_intern("recursive")), prof_method_recursive(self));
|
537
|
+
rb_hash_aset(result, ID2SYM(rb_intern("excluded")), prof_method_excluded(self));
|
538
|
+
rb_hash_aset(result, ID2SYM(rb_intern("source_file")), method_data->source_file);
|
539
|
+
rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(method_data->source_line));
|
602
540
|
|
541
|
+
rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(method_data->measurement));
|
542
|
+
|
543
|
+
rb_hash_aset(result, ID2SYM(rb_intern("callers")), prof_method_callers(self));
|
544
|
+
rb_hash_aset(result, ID2SYM(rb_intern("callees")), prof_method_callees(self));
|
545
|
+
|
546
|
+
rb_hash_aset(result, ID2SYM(rb_intern("allocations")), prof_method_allocations(self));
|
547
|
+
|
548
|
+
return result;
|
549
|
+
}
|
550
|
+
|
551
|
+
/* :nodoc: */
|
603
552
|
static VALUE
|
604
|
-
|
553
|
+
prof_method_load(VALUE self, VALUE data)
|
605
554
|
{
|
606
|
-
prof_method_t
|
607
|
-
|
608
|
-
|
555
|
+
prof_method_t* method_data = RDATA(self)->data;
|
556
|
+
method_data->object = self;
|
557
|
+
|
558
|
+
method_data->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
|
559
|
+
method_data->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
|
560
|
+
method_data->method_name = rb_hash_aref(data, ID2SYM(rb_intern("method_name")));
|
561
|
+
method_data->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
|
562
|
+
|
563
|
+
method_data->root = rb_hash_aref(data, ID2SYM(rb_intern("root"))) == Qtrue ? true : false;
|
564
|
+
method_data->recursive = rb_hash_aref(data, ID2SYM(rb_intern("recursive"))) == Qtrue ? true : false;
|
565
|
+
method_data->excluded = rb_hash_aref(data, ID2SYM(rb_intern("excluded"))) == Qtrue ? true : false;
|
566
|
+
|
567
|
+
method_data->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
|
568
|
+
method_data->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
|
569
|
+
|
570
|
+
VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
|
571
|
+
method_data->measurement = prof_get_measurement(measurement);
|
572
|
+
|
573
|
+
VALUE callers = rb_hash_aref(data, ID2SYM(rb_intern("callers")));
|
574
|
+
for (int i = 0; i < rb_array_len(callers); i++)
|
575
|
+
{
|
576
|
+
VALUE call_info = rb_ary_entry(callers, i);
|
577
|
+
prof_call_info_t *call_info_data = prof_get_call_info(call_info);
|
578
|
+
st_data_t key = call_info_data->parent ? call_info_data->parent->key : method_key(Qnil, 0);
|
579
|
+
call_info_table_insert(method_data->parent_call_infos, key, call_info_data);
|
580
|
+
}
|
581
|
+
|
582
|
+
VALUE callees = rb_hash_aref(data, ID2SYM(rb_intern("callees")));
|
583
|
+
for (int i = 0; i < rb_array_len(callees); i++)
|
584
|
+
{
|
585
|
+
VALUE call_info = rb_ary_entry(callees, i);
|
586
|
+
prof_call_info_t *call_info_data = prof_get_call_info(call_info);
|
587
|
+
|
588
|
+
st_data_t key = call_info_data->method ? call_info_data->method->key : method_key(Qnil, 0);
|
589
|
+
call_info_table_insert(method_data->child_call_infos, key, call_info_data);
|
590
|
+
}
|
591
|
+
|
592
|
+
VALUE allocations = rb_hash_aref(data, ID2SYM(rb_intern("allocations")));
|
593
|
+
for (int i = 0; i < rb_array_len(allocations); i++)
|
594
|
+
{
|
595
|
+
VALUE allocation = rb_ary_entry(allocations, i);
|
596
|
+
prof_allocation_t* allocation_data = prof_allocation_get(allocation);
|
597
|
+
|
598
|
+
st_insert(method_data->allocations_table, allocation_data->key, (st_data_t)allocation_data);
|
599
|
+
}
|
600
|
+
return data;
|
609
601
|
}
|
610
602
|
|
611
603
|
void rp_init_method_info()
|
612
604
|
{
|
613
605
|
/* MethodInfo */
|
614
|
-
|
615
|
-
rb_undef_method(CLASS_OF(
|
616
|
-
|
617
|
-
|
618
|
-
rb_define_method(
|
619
|
-
rb_define_method(
|
620
|
-
|
621
|
-
rb_define_method(
|
622
|
-
|
623
|
-
rb_define_method(
|
624
|
-
rb_define_method(
|
625
|
-
rb_define_method(
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
rb_define_method(
|
606
|
+
cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cData);
|
607
|
+
rb_undef_method(CLASS_OF(cRpMethodInfo), "new");
|
608
|
+
rb_define_alloc_func(cRpMethodInfo, prof_method_allocate);
|
609
|
+
|
610
|
+
rb_define_method(cRpMethodInfo, "klass_name", prof_method_klass_name, 0);
|
611
|
+
rb_define_method(cRpMethodInfo, "klass_flags", prof_method_klass_flags, 0);
|
612
|
+
|
613
|
+
rb_define_method(cRpMethodInfo, "method_name", prof_method_name, 0);
|
614
|
+
|
615
|
+
rb_define_method(cRpMethodInfo, "callers", prof_method_callers, 0);
|
616
|
+
rb_define_method(cRpMethodInfo, "callees", prof_method_callees, 0);
|
617
|
+
rb_define_method(cRpMethodInfo, "allocations", prof_method_allocations, 0);
|
618
|
+
|
619
|
+
rb_define_method(cRpMethodInfo, "measurement", prof_method_measurement, 0);
|
620
|
+
|
621
|
+
rb_define_method(cRpMethodInfo, "source_file", prof_method_source_file, 0);
|
622
|
+
rb_define_method(cRpMethodInfo, "line", prof_method_line, 0);
|
623
|
+
|
624
|
+
rb_define_method(cRpMethodInfo, "root?", prof_method_root, 0);
|
625
|
+
rb_define_method(cRpMethodInfo, "recursive?", prof_method_recursive, 0);
|
626
|
+
rb_define_method(cRpMethodInfo, "excluded?", prof_method_excluded, 0);
|
627
|
+
|
628
|
+
rb_define_method(cRpMethodInfo, "_dump_data", prof_method_dump, 0);
|
629
|
+
rb_define_method(cRpMethodInfo, "_load_data", prof_method_load, 1);
|
630
630
|
}
|