ruby-prof 1.1.0-x64-mingw32 → 1.3.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 +19 -1
- data/bin/ruby-prof +100 -152
- data/ext/ruby_prof/rp_aggregate_call_tree.c +59 -0
- data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
- data/ext/ruby_prof/rp_allocation.c +67 -59
- data/ext/ruby_prof/rp_allocation.h +3 -3
- data/ext/ruby_prof/rp_call_tree.c +369 -0
- data/ext/ruby_prof/rp_call_tree.h +43 -0
- data/ext/ruby_prof/rp_call_trees.c +288 -0
- data/ext/ruby_prof/rp_call_trees.h +28 -0
- data/ext/ruby_prof/rp_measure_allocations.c +11 -13
- data/ext/ruby_prof/rp_measure_process_time.c +11 -13
- data/ext/ruby_prof/rp_measure_wall_time.c +17 -15
- data/ext/ruby_prof/rp_measurement.c +47 -40
- data/ext/ruby_prof/rp_measurement.h +7 -7
- data/ext/ruby_prof/rp_method.c +116 -255
- data/ext/ruby_prof/rp_method.h +31 -39
- data/ext/ruby_prof/rp_profile.c +311 -281
- data/ext/ruby_prof/rp_profile.h +1 -2
- data/ext/ruby_prof/rp_stack.c +113 -105
- data/ext/ruby_prof/rp_stack.h +17 -20
- data/ext/ruby_prof/rp_thread.c +136 -111
- data/ext/ruby_prof/rp_thread.h +12 -9
- data/ext/ruby_prof/ruby_prof.c +27 -23
- data/ext/ruby_prof/ruby_prof.h +9 -0
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +11 -7
- data/lib/ruby-prof.rb +2 -3
- data/lib/ruby-prof/assets/call_stack_printer.html.erb +4 -7
- data/lib/ruby-prof/assets/graph_printer.html.erb +5 -6
- data/lib/ruby-prof/{call_info.rb → call_tree.rb} +6 -6
- data/lib/ruby-prof/call_tree_visitor.rb +36 -0
- data/lib/ruby-prof/measurement.rb +5 -2
- data/lib/ruby-prof/method_info.rb +3 -15
- data/lib/ruby-prof/printers/call_info_printer.rb +12 -10
- data/lib/ruby-prof/printers/call_stack_printer.rb +19 -22
- data/lib/ruby-prof/printers/call_tree_printer.rb +1 -1
- data/lib/ruby-prof/printers/dot_printer.rb +3 -3
- data/lib/ruby-prof/printers/graph_printer.rb +3 -4
- data/lib/ruby-prof/printers/multi_printer.rb +2 -2
- data/lib/ruby-prof/rack.rb +3 -0
- data/lib/ruby-prof/thread.rb +3 -18
- data/lib/ruby-prof/version.rb +1 -1
- data/ruby-prof.gemspec +7 -0
- data/test/alias_test.rb +42 -45
- data/test/basic_test.rb +0 -86
- data/test/{call_info_visitor_test.rb → call_tree_visitor_test.rb} +6 -5
- data/test/call_trees_test.rb +66 -0
- data/test/exclude_methods_test.rb +17 -12
- data/test/fiber_test.rb +197 -9
- data/test/gc_test.rb +36 -42
- data/test/inverse_call_tree_test.rb +175 -0
- data/test/line_number_test.rb +67 -70
- data/test/marshal_test.rb +7 -11
- data/test/measure_allocations_test.rb +224 -234
- data/test/measure_allocations_trace_test.rb +224 -234
- data/test/measure_memory_trace_test.rb +814 -469
- data/test/measure_process_time_test.rb +0 -64
- data/test/measure_times.rb +2 -0
- data/test/measure_wall_time_test.rb +34 -58
- data/test/pause_resume_test.rb +19 -10
- data/test/prime.rb +1 -3
- data/test/prime_script.rb +6 -0
- data/test/printers_test.rb +1 -1
- data/test/recursive_test.rb +50 -54
- data/test/start_stop_test.rb +19 -19
- data/test/test_helper.rb +3 -15
- data/test/thread_test.rb +11 -11
- data/test/unique_call_path_test.rb +25 -95
- metadata +19 -10
- data/ext/ruby_prof/rp_call_info.c +0 -271
- data/ext/ruby_prof/rp_call_info.h +0 -35
- data/lib/2.6.5/ruby_prof.so +0 -0
- data/lib/ruby-prof/call_info_visitor.rb +0 -38
- data/test/parser_timings.rb +0 -24
data/ext/ruby_prof/rp_method.h
CHANGED
@@ -9,62 +9,54 @@
|
|
9
9
|
|
10
10
|
extern VALUE cRpMethodInfo;
|
11
11
|
|
12
|
-
|
12
|
+
// Source relation bit offsets.
|
13
13
|
enum {
|
14
|
-
kModuleIncludee = 0x1,
|
15
|
-
kClassSingleton = 0x2,
|
16
|
-
kModuleSingleton = 0x4,
|
17
|
-
kObjectSingleton = 0x8,
|
18
|
-
kOtherSingleton = 0x10
|
14
|
+
kModuleIncludee = 0x1, // Included in module
|
15
|
+
kClassSingleton = 0x2, // Singleton of a class
|
16
|
+
kModuleSingleton = 0x4, // Singleton of a module
|
17
|
+
kObjectSingleton = 0x8, // Singleton of an object
|
18
|
+
kOtherSingleton = 0x10 // Singleton of unkown object
|
19
19
|
};
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
/* Profiling information for each method. */
|
25
|
-
/* Excluded methods have no call_infos, source_klass, or source_file. */
|
26
|
-
typedef struct
|
21
|
+
// Profiling information for each method.
|
22
|
+
// Excluded methods have no call_trees, source_klass, or source_file.
|
23
|
+
typedef struct prof_method_t
|
27
24
|
{
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
bool excluded; /* Exclude from profile? */
|
32
|
-
|
33
|
-
st_table* parent_call_infos; /* Call infos that call this method */
|
34
|
-
st_table* child_call_infos; /* Call infos that this method calls */
|
35
|
-
st_table* allocations_table; /* Tracks object allocations */
|
25
|
+
VALUE profile; // Profile this method is associated with - needed for mark phase
|
26
|
+
struct prof_call_trees_t* call_trees; // Call infos that call this method
|
27
|
+
st_table* allocations_table; // Tracks object allocations
|
36
28
|
|
37
|
-
|
38
|
-
|
39
|
-
VALUE
|
40
|
-
VALUE
|
29
|
+
st_data_t key; // Table key
|
30
|
+
unsigned int klass_flags; // Information about the type of class
|
31
|
+
VALUE klass; // Resolved klass
|
32
|
+
VALUE klass_name; // Resolved klass name for this method
|
33
|
+
VALUE method_name; // Resolved method name for this method
|
41
34
|
|
42
|
-
VALUE object;
|
35
|
+
VALUE object; // Cached ruby object
|
43
36
|
|
44
|
-
bool root; /* Is this a root method */
|
45
37
|
bool recursive;
|
46
|
-
|
47
|
-
|
38
|
+
int visits; // Current visits on the stack
|
39
|
+
VALUE source_file; // Source file
|
40
|
+
int source_line; // Line number
|
48
41
|
|
49
|
-
prof_measurement_t
|
42
|
+
prof_measurement_t* measurement; // Stores measurement data for this method
|
50
43
|
} prof_method_t;
|
51
44
|
|
52
45
|
void rp_init_method_info(void);
|
53
46
|
|
54
47
|
st_data_t method_key(VALUE klass, VALUE msym);
|
55
48
|
|
56
|
-
st_table
|
57
|
-
prof_method_t*
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
prof_method_t
|
62
|
-
prof_method_t *prof_method_get(VALUE self);
|
49
|
+
st_table* method_table_create(void);
|
50
|
+
prof_method_t* method_table_lookup(st_table* table, st_data_t key);
|
51
|
+
size_t method_table_insert(st_table* table, st_data_t key, prof_method_t* val);
|
52
|
+
void method_table_free(st_table* table);
|
53
|
+
prof_method_t* prof_method_create(VALUE profile, VALUE klass, VALUE msym, VALUE source_file, int source_line);
|
54
|
+
prof_method_t* prof_get_method(VALUE self);
|
63
55
|
|
64
|
-
VALUE prof_method_wrap(prof_method_t
|
65
|
-
void prof_method_mark(void
|
56
|
+
VALUE prof_method_wrap(prof_method_t* result);
|
57
|
+
void prof_method_mark(void* data);
|
66
58
|
|
67
|
-
VALUE resolve_klass(VALUE klass, unsigned int
|
59
|
+
VALUE resolve_klass(VALUE klass, unsigned int* klass_flags);
|
68
60
|
VALUE resolve_klass_name(VALUE klass, unsigned int* klass_flags);
|
69
61
|
|
70
62
|
#endif //__RP_METHOD_INFO__
|
data/ext/ruby_prof/rp_profile.c
CHANGED
@@ -1,28 +1,29 @@
|
|
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
|
-
/* Document-class: RubyProf::Profile
|
4
|
+
/* Document-class: RubyProf::Profile
|
5
5
|
|
6
|
-
The Profile class represents a single profiling run and provides the main API for using ruby-prof.
|
7
|
-
After creating a Profile instance, start profiling code by calling the Profile#start method. To finish profiling,
|
8
|
-
call Profile#stop. Once profiling is completed, the Profile instance contains the results.
|
6
|
+
The Profile class represents a single profiling run and provides the main API for using ruby-prof.
|
7
|
+
After creating a Profile instance, start profiling code by calling the Profile#start method. To finish profiling,
|
8
|
+
call Profile#stop. Once profiling is completed, the Profile instance contains the results.
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
profile = RubyProf::Profile.new
|
11
|
+
profile.start
|
12
|
+
...
|
13
|
+
result = profile.stop
|
14
14
|
|
15
|
-
Alternatively, you can use the block syntax:
|
15
|
+
Alternatively, you can use the block syntax:
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
*/
|
17
|
+
profile = RubyProf::Profile.profile do
|
18
|
+
...
|
19
|
+
end
|
20
|
+
*/
|
21
21
|
|
22
22
|
#include <assert.h>
|
23
23
|
|
24
24
|
#include "rp_allocation.h"
|
25
|
-
#include "
|
25
|
+
#include "rp_call_trees.h"
|
26
|
+
#include "rp_call_tree.h"
|
26
27
|
#include "rp_profile.h"
|
27
28
|
#include "rp_method.h"
|
28
29
|
|
@@ -35,69 +36,37 @@ VALUE cProfile;
|
|
35
36
|
*/
|
36
37
|
FILE* trace_file = NULL;
|
37
38
|
|
38
|
-
static
|
39
|
-
excludes_method(st_data_t key, prof_profile_t* profile)
|
40
|
-
{
|
41
|
-
return (profile->exclude_methods_tbl &&
|
42
|
-
method_table_lookup(profile->exclude_methods_tbl, key) != NULL);
|
43
|
-
}
|
44
|
-
|
45
|
-
static prof_method_t*
|
46
|
-
create_method(prof_profile_t* profile, st_data_t key, VALUE klass, VALUE msym, VALUE source_file, int source_line)
|
47
|
-
{
|
48
|
-
prof_method_t* result = NULL;
|
49
|
-
|
50
|
-
if (excludes_method(key, profile))
|
51
|
-
{
|
52
|
-
/* We found a exclusion sentinel so propagate it into the thread's local hash table. */
|
53
|
-
/* TODO(nelgau): Is there a way to avoid this allocation completely so that all these
|
54
|
-
tables share the same exclusion method struct? The first attempt failed due to my
|
55
|
-
ignorance of the whims of the GC. */
|
56
|
-
result = prof_method_create_excluded(klass, msym);
|
57
|
-
}
|
58
|
-
else
|
59
|
-
{
|
60
|
-
result = prof_method_create(klass, msym, source_file, source_line);
|
61
|
-
}
|
62
|
-
|
63
|
-
/* Insert the newly created method, or the exlcusion sentinel. */
|
64
|
-
method_table_insert(profile->last_thread_data->method_table, result->key, result);
|
65
|
-
|
66
|
-
return result;
|
67
|
-
}
|
68
|
-
|
69
|
-
static const char *
|
70
|
-
get_event_name(rb_event_flag_t event)
|
39
|
+
static const char* get_event_name(rb_event_flag_t event)
|
71
40
|
{
|
72
41
|
switch (event) {
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
42
|
+
case RUBY_EVENT_LINE:
|
43
|
+
return "line";
|
44
|
+
case RUBY_EVENT_CLASS:
|
45
|
+
return "class";
|
46
|
+
case RUBY_EVENT_END:
|
47
|
+
return "end";
|
48
|
+
case RUBY_EVENT_CALL:
|
49
|
+
return "call";
|
50
|
+
case RUBY_EVENT_RETURN:
|
51
|
+
return "return";
|
52
|
+
case RUBY_EVENT_B_CALL:
|
53
|
+
return "b-call";
|
54
|
+
case RUBY_EVENT_B_RETURN:
|
55
|
+
return "b-return";
|
56
|
+
case RUBY_EVENT_C_CALL:
|
57
|
+
return "c-call";
|
58
|
+
case RUBY_EVENT_C_RETURN:
|
59
|
+
return "c-return";
|
60
|
+
case RUBY_EVENT_THREAD_BEGIN:
|
61
|
+
return "thread-begin";
|
62
|
+
case RUBY_EVENT_THREAD_END:
|
63
|
+
return "thread-end";
|
64
|
+
case RUBY_EVENT_FIBER_SWITCH:
|
65
|
+
return "fiber-switch";
|
66
|
+
case RUBY_EVENT_RAISE:
|
67
|
+
return "raise";
|
68
|
+
default:
|
69
|
+
return "unknown";
|
101
70
|
}
|
102
71
|
}
|
103
72
|
|
@@ -108,14 +77,14 @@ VALUE get_fiber(prof_profile_t* profile)
|
|
108
77
|
else
|
109
78
|
return rb_fiber_current();
|
110
79
|
}
|
111
|
-
|
112
|
-
thread_data_t* check_fiber(prof_profile_t
|
80
|
+
|
81
|
+
thread_data_t* check_fiber(prof_profile_t* profile, double measurement)
|
113
82
|
{
|
114
83
|
thread_data_t* result = NULL;
|
115
|
-
|
84
|
+
|
116
85
|
/* Get the current thread and fiber information. */
|
117
86
|
VALUE fiber = get_fiber(profile);
|
118
|
-
|
87
|
+
|
119
88
|
/* We need to switch the profiling context if we either had none before,
|
120
89
|
we don't merge fibers and the fiber ids differ, or the thread ids differ. */
|
121
90
|
if (profile->last_thread_data->fiber != fiber)
|
@@ -134,160 +103,215 @@ thread_data_t* check_fiber(prof_profile_t *profile, double measurement)
|
|
134
103
|
return result;
|
135
104
|
}
|
136
105
|
|
137
|
-
static
|
138
|
-
|
106
|
+
static int excludes_method(st_data_t key, prof_profile_t* profile)
|
107
|
+
{
|
108
|
+
return (profile->exclude_methods_tbl &&
|
109
|
+
method_table_lookup(profile->exclude_methods_tbl, key) != NULL);
|
110
|
+
}
|
111
|
+
|
112
|
+
static prof_method_t* create_method(VALUE profile, st_data_t key, VALUE klass, VALUE msym, VALUE source_file, int source_line)
|
113
|
+
{
|
114
|
+
prof_method_t* result = prof_method_create(profile, klass, msym, source_file, source_line);
|
115
|
+
|
116
|
+
prof_profile_t* profile_t = prof_get_profile(profile);
|
117
|
+
method_table_insert(profile_t->last_thread_data->method_table, result->key, result);
|
118
|
+
|
119
|
+
return result;
|
120
|
+
}
|
121
|
+
|
122
|
+
static prof_method_t* check_parent_method(VALUE profile, thread_data_t* thread_data)
|
123
|
+
{
|
124
|
+
VALUE msym = ID2SYM(rb_intern("_inserted_parent_"));
|
125
|
+
st_data_t key = method_key(cProfile, msym);
|
126
|
+
|
127
|
+
prof_method_t* result = method_table_lookup(thread_data->method_table, key);
|
128
|
+
|
129
|
+
if (!result)
|
130
|
+
{
|
131
|
+
result = create_method(profile, key, cProfile, msym, Qnil, 0);
|
132
|
+
}
|
133
|
+
|
134
|
+
return result;
|
135
|
+
}
|
136
|
+
|
137
|
+
prof_method_t* check_method(VALUE profile, rb_trace_arg_t* trace_arg, rb_event_flag_t event, thread_data_t* thread_data)
|
138
|
+
{
|
139
|
+
VALUE klass = rb_tracearg_defined_class(trace_arg);
|
140
|
+
|
141
|
+
/* Special case - skip any methods from the mProf
|
142
|
+
module or cProfile class since they clutter
|
143
|
+
the results but aren't important to them results. */
|
144
|
+
if (klass == cProfile)
|
145
|
+
return NULL;
|
146
|
+
|
147
|
+
#ifdef HAVE_RB_TRACEARG_CALLEE_ID
|
148
|
+
VALUE msym = rb_tracearg_callee_id(trace_arg);
|
149
|
+
#else
|
150
|
+
VALUE msym = rb_tracearg_method_id(trace_arg);
|
151
|
+
#endif
|
152
|
+
|
153
|
+
st_data_t key = method_key(klass, msym);
|
154
|
+
|
155
|
+
prof_profile_t* profile_t = prof_get_profile(profile);
|
156
|
+
if (excludes_method(key, profile_t))
|
157
|
+
return NULL;
|
158
|
+
|
159
|
+
prof_method_t* result = method_table_lookup(thread_data->method_table, key);
|
160
|
+
|
161
|
+
if (!result)
|
162
|
+
{
|
163
|
+
VALUE source_file = (event != RUBY_EVENT_C_CALL ? rb_tracearg_path(trace_arg) : Qnil);
|
164
|
+
int source_line = (event != RUBY_EVENT_C_CALL ? FIX2INT(rb_tracearg_lineno(trace_arg)) : 0);
|
165
|
+
result = create_method(profile, key, klass, msym, source_file, source_line);
|
166
|
+
}
|
167
|
+
|
168
|
+
return result;
|
169
|
+
}
|
170
|
+
|
171
|
+
/* =========== Profiling ================= */
|
172
|
+
static void prof_trace(prof_profile_t* profile, rb_trace_arg_t* trace_arg, double measurement)
|
139
173
|
{
|
140
174
|
static VALUE last_fiber = Qnil;
|
141
175
|
VALUE fiber = get_fiber(profile);
|
142
|
-
|
176
|
+
|
143
177
|
rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
|
144
178
|
const char* event_name = get_event_name(event);
|
145
|
-
|
179
|
+
|
146
180
|
VALUE source_file = rb_tracearg_path(trace_arg);
|
147
181
|
int source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
|
148
|
-
|
149
|
-
|
182
|
+
|
183
|
+
#ifdef HAVE_RB_TRACEARG_CALLEE_ID
|
150
184
|
VALUE msym = rb_tracearg_callee_id(trace_arg);
|
151
|
-
|
185
|
+
#else
|
152
186
|
VALUE msym = rb_tracearg_method_id(trace_arg);
|
153
|
-
|
154
|
-
|
187
|
+
#endif
|
188
|
+
|
155
189
|
unsigned int klass_flags;
|
156
190
|
VALUE klass = rb_tracearg_defined_class(trace_arg);
|
157
191
|
VALUE resolved_klass = resolve_klass(klass, &klass_flags);
|
158
192
|
const char* class_name = "";
|
159
|
-
|
193
|
+
|
160
194
|
if (resolved_klass != Qnil)
|
161
195
|
class_name = rb_class2name(resolved_klass);
|
162
|
-
|
196
|
+
|
163
197
|
if (last_fiber != fiber)
|
164
198
|
{
|
165
199
|
fprintf(trace_file, "\n");
|
166
200
|
}
|
167
|
-
|
201
|
+
|
168
202
|
const char* method_name_char = (msym != Qnil ? rb_id2name(SYM2ID(msym)) : "");
|
169
203
|
const char* source_file_char = (source_file != Qnil ? StringValuePtr(source_file) : "");
|
170
|
-
|
204
|
+
|
171
205
|
fprintf(trace_file, "%2lu:%2f %-8s %s#%s %s:%2d\n",
|
172
|
-
FIX2ULONG(fiber), (double)
|
206
|
+
FIX2ULONG(fiber), (double)measurement,
|
173
207
|
event_name, class_name, method_name_char, source_file_char, source_line);
|
174
208
|
fflush(trace_file);
|
175
209
|
last_fiber = fiber;
|
176
210
|
}
|
177
211
|
|
178
|
-
static void
|
179
|
-
prof_event_hook(VALUE trace_point, void* data)
|
212
|
+
static void prof_event_hook(VALUE trace_point, void* data)
|
180
213
|
{
|
181
|
-
|
182
|
-
|
183
|
-
|
214
|
+
VALUE profile = (VALUE)data;
|
215
|
+
prof_profile_t* profile_t = prof_get_profile(profile);
|
216
|
+
|
184
217
|
rb_trace_arg_t* trace_arg = rb_tracearg_from_tracepoint(trace_point);
|
185
|
-
double measurement = prof_measure(
|
218
|
+
double measurement = prof_measure(profile_t->measurer, trace_arg);
|
186
219
|
rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
|
187
220
|
VALUE self = rb_tracearg_self(trace_arg);
|
188
|
-
|
221
|
+
|
189
222
|
if (trace_file != NULL)
|
190
223
|
{
|
191
|
-
prof_trace(
|
224
|
+
prof_trace(profile_t, trace_arg, measurement);
|
192
225
|
}
|
193
|
-
|
226
|
+
|
194
227
|
/* Special case - skip any methods from the mProf
|
195
228
|
module since they clutter the results but aren't important to them results. */
|
196
229
|
if (self == mProf)
|
197
230
|
return;
|
198
|
-
|
199
|
-
thread_data = check_fiber(
|
200
|
-
|
231
|
+
|
232
|
+
thread_data_t* thread_data = check_fiber(profile_t, measurement);
|
233
|
+
|
201
234
|
if (!thread_data->trace)
|
202
235
|
return;
|
203
|
-
|
204
|
-
/* Get the current frame for the current thread. */
|
205
|
-
frame = thread_data->stack->ptr;
|
206
|
-
|
236
|
+
|
207
237
|
switch (event)
|
208
238
|
{
|
209
239
|
case RUBY_EVENT_LINE:
|
210
240
|
{
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
if (frame->call_info)
|
241
|
+
prof_frame_t* frame = prof_frame_current(thread_data->stack);
|
242
|
+
|
243
|
+
if (!frame)
|
215
244
|
{
|
216
|
-
|
245
|
+
prof_method_t* method = check_method(profile, trace_arg, event, thread_data);
|
246
|
+
|
247
|
+
if (!method)
|
248
|
+
break;
|
249
|
+
|
250
|
+
prof_call_tree_t* call_tree = prof_call_tree_create(method, NULL, method->source_file, method->source_line);
|
251
|
+
prof_add_call_tree(method->call_trees, call_tree);
|
252
|
+
|
253
|
+
if (thread_data->call_tree)
|
217
254
|
{
|
218
|
-
|
219
|
-
frame
|
255
|
+
prof_call_tree_add_parent(thread_data->call_tree, call_tree);
|
256
|
+
frame = prof_frame_unshift(thread_data->stack, call_tree, thread_data->call_tree, measurement);
|
220
257
|
}
|
221
|
-
|
258
|
+
else
|
259
|
+
{
|
260
|
+
frame = prof_frame_push(thread_data->stack, call_tree, measurement, RTEST(profile_t->paused));
|
261
|
+
}
|
262
|
+
|
263
|
+
thread_data->call_tree = call_tree;
|
222
264
|
}
|
223
265
|
|
224
|
-
|
225
|
-
|
226
|
-
|
266
|
+
frame->source_file = rb_tracearg_path(trace_arg);
|
267
|
+
frame->source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
|
268
|
+
|
269
|
+
break;
|
227
270
|
}
|
228
271
|
case RUBY_EVENT_CALL:
|
229
272
|
case RUBY_EVENT_C_CALL:
|
230
273
|
{
|
231
|
-
|
232
|
-
prof_call_info_t* call_info;
|
233
|
-
prof_method_t* method;
|
274
|
+
prof_method_t* method = check_method(profile, trace_arg, event, thread_data);
|
234
275
|
|
235
|
-
|
236
|
-
|
276
|
+
if (!method)
|
277
|
+
break;
|
237
278
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
module or cProfile class since they clutter
|
242
|
-
the results but aren't important to them results. */
|
243
|
-
if (klass == cProfile)
|
244
|
-
return;
|
245
|
-
|
246
|
-
#ifdef HAVE_RB_TRACEARG_CALLEE_ID
|
247
|
-
VALUE msym = rb_tracearg_callee_id(trace_arg);
|
248
|
-
#else
|
249
|
-
VALUE msym = rb_tracearg_method_id(trace_arg);
|
250
|
-
#endif
|
279
|
+
prof_frame_t* frame = prof_frame_current(thread_data->stack);
|
280
|
+
prof_call_tree_t* parent_call_tree = NULL;
|
281
|
+
prof_call_tree_t* call_tree = NULL;
|
251
282
|
|
252
|
-
|
253
|
-
|
254
|
-
method = method_table_lookup(thread_data->method_table, key);
|
255
|
-
|
256
|
-
if (!method)
|
283
|
+
// Frame can be NULL if we are switching from one fiber to another (see FiberTest#fiber_test)
|
284
|
+
if (frame)
|
257
285
|
{
|
258
|
-
|
259
|
-
|
260
|
-
method = create_method(profile, key, klass, msym, source_file, source_line);
|
286
|
+
parent_call_tree = frame->call_tree;
|
287
|
+
call_tree = call_tree_table_lookup(parent_call_tree->children, method->key);
|
261
288
|
}
|
262
|
-
|
263
|
-
if (method->excluded)
|
289
|
+
else if (!frame && thread_data->call_tree)
|
264
290
|
{
|
265
|
-
|
266
|
-
|
291
|
+
// There is no current parent - likely we have returned out of the highest level method we have profiled so far.
|
292
|
+
// This can happen with enumerators (see fiber_test.rb). So create a new dummy parent.
|
293
|
+
prof_method_t* parent_method = check_parent_method(profile, thread_data);
|
294
|
+
parent_call_tree = prof_call_tree_create(parent_method, NULL, Qnil, 0);
|
295
|
+
prof_add_call_tree(parent_method->call_trees, parent_call_tree);
|
296
|
+
prof_call_tree_add_parent(thread_data->call_tree, parent_call_tree);
|
297
|
+
frame = prof_frame_unshift(thread_data->stack, parent_call_tree, thread_data->call_tree, measurement);
|
298
|
+
thread_data->call_tree = parent_call_tree;
|
267
299
|
}
|
268
|
-
|
269
|
-
if (!
|
300
|
+
|
301
|
+
if (!call_tree)
|
270
302
|
{
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
{
|
277
|
-
call_info = call_info_table_lookup(method->parent_call_infos, frame->call_info->method->key);
|
278
|
-
|
279
|
-
if (!call_info)
|
280
|
-
{
|
281
|
-
/* This call info does not yet exist. So create it, then add
|
282
|
-
it to previous callinfo's children and to the current method .*/
|
283
|
-
call_info = prof_call_info_create(method, frame->call_info->method, frame->source_file, frame->source_line);
|
284
|
-
call_info_table_insert(method->parent_call_infos, frame->call_info->method->key, call_info);
|
285
|
-
call_info_table_insert(frame->call_info->method->child_call_infos, method->key, call_info);
|
286
|
-
}
|
303
|
+
// This call info does not yet exist. So create it and add it to previous CallTree's children and the current method.
|
304
|
+
call_tree = prof_call_tree_create(method, parent_call_tree, frame ? frame->source_file : Qnil, frame? frame->source_line : 0);
|
305
|
+
prof_add_call_tree(method->call_trees, call_tree);
|
306
|
+
if (parent_call_tree)
|
307
|
+
prof_call_tree_add_child(parent_call_tree, call_tree);
|
287
308
|
}
|
288
309
|
|
289
|
-
|
290
|
-
|
310
|
+
if (!thread_data->call_tree)
|
311
|
+
thread_data->call_tree = call_tree;
|
312
|
+
|
313
|
+
// Push a new frame onto the stack for a new c-call or ruby call (into a method)
|
314
|
+
prof_frame_t* next_frame = prof_frame_push(thread_data->stack, call_tree, measurement, RTEST(profile_t->paused));
|
291
315
|
next_frame->source_file = method->source_file;
|
292
316
|
next_frame->source_line = method->source_line;
|
293
317
|
break;
|
@@ -295,8 +319,13 @@ prof_event_hook(VALUE trace_point, void* data)
|
|
295
319
|
case RUBY_EVENT_RETURN:
|
296
320
|
case RUBY_EVENT_C_RETURN:
|
297
321
|
{
|
298
|
-
|
299
|
-
|
322
|
+
// We need to check for excluded methods so that we don't pop them off the stack
|
323
|
+
prof_method_t* method = check_method(profile, trace_arg, event, thread_data);
|
324
|
+
|
325
|
+
if (!method)
|
326
|
+
break;
|
327
|
+
|
328
|
+
prof_frame_pop(thread_data->stack, measurement);
|
300
329
|
break;
|
301
330
|
}
|
302
331
|
case RUBY_INTERNAL_EVENT_NEWOBJ:
|
@@ -315,35 +344,33 @@ prof_event_hook(VALUE trace_point, void* data)
|
|
315
344
|
}
|
316
345
|
}
|
317
346
|
|
318
|
-
void
|
319
|
-
prof_install_hook(VALUE self)
|
347
|
+
void prof_install_hook(VALUE self)
|
320
348
|
{
|
321
349
|
prof_profile_t* profile = prof_get_profile(self);
|
322
|
-
|
350
|
+
|
323
351
|
VALUE event_tracepoint = rb_tracepoint_new(Qnil,
|
324
352
|
RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
|
325
353
|
RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN |
|
326
354
|
RUBY_EVENT_LINE,
|
327
|
-
prof_event_hook,
|
355
|
+
prof_event_hook, (void*)self);
|
328
356
|
rb_ary_push(profile->tracepoints, event_tracepoint);
|
329
|
-
|
357
|
+
|
330
358
|
if (profile->measurer->track_allocations)
|
331
359
|
{
|
332
|
-
VALUE allocation_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, prof_event_hook,
|
360
|
+
VALUE allocation_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, prof_event_hook, (void*)self);
|
333
361
|
rb_ary_push(profile->tracepoints, allocation_tracepoint);
|
334
362
|
}
|
335
|
-
|
363
|
+
|
336
364
|
for (int i = 0; i < RARRAY_LEN(profile->tracepoints); i++)
|
337
365
|
{
|
338
366
|
rb_tracepoint_enable(rb_ary_entry(profile->tracepoints, i));
|
339
367
|
}
|
340
368
|
}
|
341
369
|
|
342
|
-
void
|
343
|
-
prof_remove_hook(VALUE self)
|
370
|
+
void prof_remove_hook(VALUE self)
|
344
371
|
{
|
345
372
|
prof_profile_t* profile = prof_get_profile(self);
|
346
|
-
|
373
|
+
|
347
374
|
for (int i = 0; i < RARRAY_LEN(profile->tracepoints); i++)
|
348
375
|
{
|
349
376
|
rb_tracepoint_disable(rb_ary_entry(profile->tracepoints, i));
|
@@ -351,18 +378,16 @@ prof_remove_hook(VALUE self)
|
|
351
378
|
rb_ary_clear(profile->tracepoints);
|
352
379
|
}
|
353
380
|
|
354
|
-
prof_profile_t*
|
355
|
-
prof_get_profile(VALUE self)
|
381
|
+
prof_profile_t* prof_get_profile(VALUE self)
|
356
382
|
{
|
357
383
|
/* Can't use Data_Get_Struct because that triggers the event hook
|
358
384
|
ending up in endless recursion. */
|
359
|
-
return
|
385
|
+
return RTYPEDDATA_DATA(self);
|
360
386
|
}
|
361
387
|
|
362
|
-
static int
|
363
|
-
collect_threads(st_data_t key, st_data_t value, st_data_t result)
|
388
|
+
static int collect_threads(st_data_t key, st_data_t value, st_data_t result)
|
364
389
|
{
|
365
|
-
thread_data_t* thread_data = (thread_data_t*)
|
390
|
+
thread_data_t* thread_data = (thread_data_t*)value;
|
366
391
|
if (thread_data->trace)
|
367
392
|
{
|
368
393
|
VALUE threads_array = (VALUE)result;
|
@@ -372,36 +397,41 @@ collect_threads(st_data_t key, st_data_t value, st_data_t result)
|
|
372
397
|
}
|
373
398
|
|
374
399
|
/* ======== Profile Class ====== */
|
375
|
-
static int
|
376
|
-
mark_threads(st_data_t key, st_data_t value, st_data_t result)
|
400
|
+
static int mark_threads(st_data_t key, st_data_t value, st_data_t result)
|
377
401
|
{
|
378
|
-
thread_data_t
|
402
|
+
thread_data_t* thread = (thread_data_t*)value;
|
379
403
|
prof_thread_mark(thread);
|
380
404
|
return ST_CONTINUE;
|
381
405
|
}
|
382
406
|
|
383
|
-
static int
|
384
|
-
mark_methods(st_data_t key, st_data_t value, st_data_t result)
|
407
|
+
static int prof_profile_mark_methods(st_data_t key, st_data_t value, st_data_t result)
|
385
408
|
{
|
386
|
-
prof_method_t
|
409
|
+
prof_method_t* method = (prof_method_t*)value;
|
387
410
|
prof_method_mark(method);
|
388
411
|
return ST_CONTINUE;
|
389
412
|
}
|
390
413
|
|
391
|
-
static void
|
392
|
-
prof_mark(prof_profile_t *profile)
|
414
|
+
static void prof_profile_mark(void* data)
|
393
415
|
{
|
416
|
+
prof_profile_t* profile = (prof_profile_t*)data;
|
394
417
|
rb_gc_mark(profile->tracepoints);
|
395
|
-
|
396
|
-
|
418
|
+
rb_gc_mark(profile->running);
|
419
|
+
rb_gc_mark(profile->paused);
|
420
|
+
|
421
|
+
// If GC stress is true (useful for debugging), when threads_table_create is called in the
|
422
|
+
// allocate method Ruby will immediately call this mark method. Thus the threads_tbl will be NULL.
|
423
|
+
if (profile->threads_tbl)
|
424
|
+
rb_st_foreach(profile->threads_tbl, mark_threads, 0);
|
425
|
+
|
426
|
+
if (profile->exclude_methods_tbl)
|
427
|
+
rb_st_foreach(profile->exclude_methods_tbl, prof_profile_mark_methods, 0);
|
397
428
|
}
|
398
429
|
|
399
|
-
/* Freeing the profile creates a cascade of freeing.
|
400
|
-
|
401
|
-
|
402
|
-
static void
|
403
|
-
prof_free(prof_profile_t *profile)
|
430
|
+
/* Freeing the profile creates a cascade of freeing. It frees its threads table, which frees
|
431
|
+
each thread and its associated call treee and methods. */
|
432
|
+
static void prof_profile_ruby_gc_free(void* data)
|
404
433
|
{
|
434
|
+
prof_profile_t* profile = (prof_profile_t*)data;
|
405
435
|
profile->last_thread_data = NULL;
|
406
436
|
|
407
437
|
threads_table_free(profile->threads_tbl);
|
@@ -409,13 +439,13 @@ prof_free(prof_profile_t *profile)
|
|
409
439
|
|
410
440
|
if (profile->exclude_threads_tbl)
|
411
441
|
{
|
412
|
-
|
442
|
+
rb_st_free_table(profile->exclude_threads_tbl);
|
413
443
|
profile->exclude_threads_tbl = NULL;
|
414
444
|
}
|
415
445
|
|
416
446
|
if (profile->include_threads_tbl)
|
417
447
|
{
|
418
|
-
|
448
|
+
rb_st_free_table(profile->include_threads_tbl);
|
419
449
|
profile->include_threads_tbl = NULL;
|
420
450
|
}
|
421
451
|
|
@@ -429,12 +459,29 @@ prof_free(prof_profile_t *profile)
|
|
429
459
|
xfree(profile);
|
430
460
|
}
|
431
461
|
|
432
|
-
|
433
|
-
|
462
|
+
size_t prof_profile_size(const void* data)
|
463
|
+
{
|
464
|
+
return sizeof(prof_profile_t);
|
465
|
+
}
|
466
|
+
|
467
|
+
static const rb_data_type_t profile_type =
|
468
|
+
{
|
469
|
+
.wrap_struct_name = "Profile",
|
470
|
+
.function =
|
471
|
+
{
|
472
|
+
.dmark = prof_profile_mark,
|
473
|
+
.dfree = prof_profile_ruby_gc_free,
|
474
|
+
.dsize = prof_profile_size,
|
475
|
+
},
|
476
|
+
.data = NULL,
|
477
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
478
|
+
};
|
479
|
+
|
480
|
+
static VALUE prof_allocate(VALUE klass)
|
434
481
|
{
|
435
482
|
VALUE result;
|
436
483
|
prof_profile_t* profile;
|
437
|
-
result =
|
484
|
+
result = TypedData_Make_Struct(klass, prof_profile_t, &profile_type, profile);
|
438
485
|
profile->threads_tbl = threads_table_create();
|
439
486
|
profile->exclude_threads_tbl = NULL;
|
440
487
|
profile->include_threads_tbl = NULL;
|
@@ -447,14 +494,12 @@ prof_allocate(VALUE klass)
|
|
447
494
|
return result;
|
448
495
|
}
|
449
496
|
|
450
|
-
static void
|
451
|
-
prof_exclude_common_methods(VALUE profile)
|
497
|
+
static void prof_exclude_common_methods(VALUE profile)
|
452
498
|
{
|
453
499
|
rb_funcall(profile, rb_intern("exclude_common_methods!"), 0);
|
454
500
|
}
|
455
501
|
|
456
|
-
static int
|
457
|
-
pop_frames(VALUE key, st_data_t value, st_data_t data)
|
502
|
+
static int pop_frames(VALUE key, st_data_t value, st_data_t data)
|
458
503
|
{
|
459
504
|
thread_data_t* thread_data = (thread_data_t*)value;
|
460
505
|
prof_profile_t* profile = (prof_profile_t*)data;
|
@@ -463,7 +508,7 @@ pop_frames(VALUE key, st_data_t value, st_data_t data)
|
|
463
508
|
if (profile->last_thread_data->fiber != thread_data->fiber)
|
464
509
|
switch_thread(profile, thread_data, measurement);
|
465
510
|
|
466
|
-
while (
|
511
|
+
while (prof_frame_pop(thread_data->stack, measurement));
|
467
512
|
|
468
513
|
return ST_CONTINUE;
|
469
514
|
}
|
@@ -471,7 +516,7 @@ pop_frames(VALUE key, st_data_t value, st_data_t data)
|
|
471
516
|
static void
|
472
517
|
prof_stop_threads(prof_profile_t* profile)
|
473
518
|
{
|
474
|
-
|
519
|
+
rb_st_foreach(profile->threads_tbl, pop_frames, (st_data_t)profile);
|
475
520
|
}
|
476
521
|
|
477
522
|
/* call-seq:
|
@@ -480,19 +525,19 @@ prof_stop_threads(prof_profile_t* profile)
|
|
480
525
|
|
481
526
|
Returns a new profiler. Possible options for the options hash are:
|
482
527
|
|
483
|
-
measure_mode
|
528
|
+
measure_mode: Measure mode. Specifies the profile measure mode.
|
484
529
|
If not specified, defaults to RubyProf::WALL_TIME.
|
485
|
-
|
486
|
-
include_threads:: Focus profiling on only the given threads. This will ignore
|
487
|
-
all other threads.
|
488
|
-
allow_exceptions:: Whether to raise exceptions encountered during profiling,
|
530
|
+
allow_exceptions: Whether to raise exceptions encountered during profiling,
|
489
531
|
or to suppress all exceptions during profiling
|
490
|
-
merge_fibers
|
532
|
+
merge_fibers: Whether profiling data for a given thread's fibers should all be
|
491
533
|
subsumed under a single entry. Basically only useful to produce
|
492
534
|
callgrind profiles.
|
493
|
-
|
494
|
-
|
495
|
-
|
535
|
+
track_allocations: Whether to track object allocations while profiling. True or false.
|
536
|
+
exclude_common: Exclude common methods from the profile. True or false.
|
537
|
+
exclude_threads: Threads to exclude from the profiling results.
|
538
|
+
include_threads: Focus profiling on only the given threads. This will ignore
|
539
|
+
all other threads. */
|
540
|
+
static VALUE prof_initialize(int argc, VALUE* argv, VALUE self)
|
496
541
|
{
|
497
542
|
prof_profile_t* profile = prof_get_profile(self);
|
498
543
|
VALUE mode_or_options;
|
@@ -552,7 +597,7 @@ prof_initialize(int argc, VALUE *argv, VALUE self)
|
|
552
597
|
for (i = 0; i < RARRAY_LEN(exclude_threads); i++)
|
553
598
|
{
|
554
599
|
VALUE thread = rb_ary_entry(exclude_threads, i);
|
555
|
-
|
600
|
+
rb_st_insert(profile->exclude_threads_tbl, thread, Qtrue);
|
556
601
|
}
|
557
602
|
}
|
558
603
|
|
@@ -564,7 +609,7 @@ prof_initialize(int argc, VALUE *argv, VALUE self)
|
|
564
609
|
for (i = 0; i < RARRAY_LEN(include_threads); i++)
|
565
610
|
{
|
566
611
|
VALUE thread = rb_ary_entry(include_threads, i);
|
567
|
-
|
612
|
+
rb_st_insert(profile->include_threads_tbl, thread, Qtrue);
|
568
613
|
}
|
569
614
|
}
|
570
615
|
|
@@ -580,8 +625,7 @@ prof_initialize(int argc, VALUE *argv, VALUE self)
|
|
580
625
|
paused? -> boolean
|
581
626
|
|
582
627
|
Returns whether a profile is currently paused.*/
|
583
|
-
static VALUE
|
584
|
-
prof_paused(VALUE self)
|
628
|
+
static VALUE prof_paused(VALUE self)
|
585
629
|
{
|
586
630
|
prof_profile_t* profile = prof_get_profile(self);
|
587
631
|
return profile->paused;
|
@@ -602,8 +646,7 @@ prof_running(VALUE self)
|
|
602
646
|
mode -> measure_mode
|
603
647
|
|
604
648
|
Returns the measure mode used in this profile.*/
|
605
|
-
static VALUE
|
606
|
-
prof_profile_measure_mode(VALUE self)
|
649
|
+
static VALUE prof_profile_measure_mode(VALUE self)
|
607
650
|
{
|
608
651
|
prof_profile_t* profile = prof_get_profile(self);
|
609
652
|
return INT2NUM(profile->measurer->mode);
|
@@ -613,8 +656,7 @@ prof_profile_measure_mode(VALUE self)
|
|
613
656
|
track_allocations -> boolean
|
614
657
|
|
615
658
|
Returns if object allocations were tracked in this profile.*/
|
616
|
-
static VALUE
|
617
|
-
prof_profile_track_allocations(VALUE self)
|
659
|
+
static VALUE prof_profile_track_allocations(VALUE self)
|
618
660
|
{
|
619
661
|
prof_profile_t* profile = prof_get_profile(self);
|
620
662
|
return profile->measurer->track_allocations ? Qtrue : Qfalse;
|
@@ -624,8 +666,7 @@ prof_profile_track_allocations(VALUE self)
|
|
624
666
|
start -> self
|
625
667
|
|
626
668
|
Starts recording profile data.*/
|
627
|
-
static VALUE
|
628
|
-
prof_start(VALUE self)
|
669
|
+
static VALUE prof_start(VALUE self)
|
629
670
|
{
|
630
671
|
char* trace_file_name;
|
631
672
|
|
@@ -642,20 +683,21 @@ prof_start(VALUE self)
|
|
642
683
|
|
643
684
|
/* open trace file if environment wants it */
|
644
685
|
trace_file_name = getenv("RUBY_PROF_TRACE");
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
686
|
+
|
687
|
+
if (trace_file_name != NULL)
|
688
|
+
{
|
689
|
+
if (strcmp(trace_file_name, "stdout") == 0)
|
690
|
+
{
|
691
|
+
trace_file = stdout;
|
692
|
+
}
|
693
|
+
else if (strcmp(trace_file_name, "stderr") == 0)
|
694
|
+
{
|
695
|
+
trace_file = stderr;
|
696
|
+
}
|
697
|
+
else
|
698
|
+
{
|
699
|
+
trace_file = fopen(trace_file_name, "w");
|
700
|
+
}
|
659
701
|
}
|
660
702
|
|
661
703
|
prof_install_hook(self);
|
@@ -666,8 +708,7 @@ prof_start(VALUE self)
|
|
666
708
|
pause -> self
|
667
709
|
|
668
710
|
Pauses collecting profile data. */
|
669
|
-
static VALUE
|
670
|
-
prof_pause(VALUE self)
|
711
|
+
static VALUE prof_pause(VALUE self)
|
671
712
|
{
|
672
713
|
prof_profile_t* profile = prof_get_profile(self);
|
673
714
|
if (profile->running == Qfalse)
|
@@ -679,7 +720,7 @@ prof_pause(VALUE self)
|
|
679
720
|
{
|
680
721
|
profile->paused = Qtrue;
|
681
722
|
profile->measurement_at_pause_resume = prof_measure(profile->measurer, NULL);
|
682
|
-
|
723
|
+
rb_st_foreach(profile->threads_tbl, pause_thread, (st_data_t)profile);
|
683
724
|
}
|
684
725
|
|
685
726
|
return self;
|
@@ -690,8 +731,7 @@ prof_pause(VALUE self)
|
|
690
731
|
resume(&block) -> self
|
691
732
|
|
692
733
|
Resumes recording profile data.*/
|
693
|
-
static VALUE
|
694
|
-
prof_resume(VALUE self)
|
734
|
+
static VALUE prof_resume(VALUE self)
|
695
735
|
{
|
696
736
|
prof_profile_t* profile = prof_get_profile(self);
|
697
737
|
if (profile->running == Qfalse)
|
@@ -703,7 +743,7 @@ prof_resume(VALUE self)
|
|
703
743
|
{
|
704
744
|
profile->paused = Qfalse;
|
705
745
|
profile->measurement_at_pause_resume = prof_measure(profile->measurer, NULL);
|
706
|
-
|
746
|
+
rb_st_foreach(profile->threads_tbl, unpause_thread, (st_data_t)profile);
|
707
747
|
}
|
708
748
|
|
709
749
|
return rb_block_given_p() ? rb_ensure(rb_yield, self, prof_pause, self) : self;
|
@@ -713,8 +753,7 @@ prof_resume(VALUE self)
|
|
713
753
|
stop -> self
|
714
754
|
|
715
755
|
Stops collecting profile data.*/
|
716
|
-
static VALUE
|
717
|
-
prof_stop(VALUE self)
|
756
|
+
static VALUE prof_stop(VALUE self)
|
718
757
|
{
|
719
758
|
prof_profile_t* profile = prof_get_profile(self);
|
720
759
|
|
@@ -728,15 +767,15 @@ prof_stop(VALUE self)
|
|
728
767
|
/* close trace file if open */
|
729
768
|
if (trace_file != NULL)
|
730
769
|
{
|
731
|
-
|
732
|
-
|
770
|
+
if (trace_file != stderr && trace_file != stdout)
|
771
|
+
{
|
733
772
|
#ifdef _MSC_VER
|
734
|
-
|
773
|
+
_fcloseall();
|
735
774
|
#else
|
736
|
-
|
775
|
+
fclose(trace_file);
|
737
776
|
#endif
|
738
|
-
|
739
|
-
|
777
|
+
}
|
778
|
+
trace_file = NULL;
|
740
779
|
}
|
741
780
|
|
742
781
|
prof_stop_threads(profile);
|
@@ -753,12 +792,11 @@ prof_stop(VALUE self)
|
|
753
792
|
threads -> Array of RubyProf::Thread
|
754
793
|
|
755
794
|
Returns an array of RubyProf::Thread instances that were profiled. */
|
756
|
-
static VALUE
|
757
|
-
prof_threads(VALUE self)
|
795
|
+
static VALUE prof_threads(VALUE self)
|
758
796
|
{
|
759
797
|
VALUE result = rb_ary_new();
|
760
798
|
prof_profile_t* profile = prof_get_profile(self);
|
761
|
-
|
799
|
+
rb_st_foreach(profile->threads_tbl, collect_threads, result);
|
762
800
|
return result;
|
763
801
|
}
|
764
802
|
|
@@ -773,8 +811,7 @@ prof_threads(VALUE self)
|
|
773
811
|
..
|
774
812
|
end
|
775
813
|
*/
|
776
|
-
static VALUE
|
777
|
-
prof_profile_object(VALUE self)
|
814
|
+
static VALUE prof_profile_object(VALUE self)
|
778
815
|
{
|
779
816
|
int result;
|
780
817
|
prof_profile_t* profile = prof_get_profile(self);
|
@@ -794,7 +831,6 @@ prof_profile_object(VALUE self)
|
|
794
831
|
}
|
795
832
|
|
796
833
|
return self;
|
797
|
-
|
798
834
|
}
|
799
835
|
|
800
836
|
/* Document-method: RubyProf::Profile::Profile
|
@@ -809,8 +845,7 @@ prof_profile_object(VALUE self)
|
|
809
845
|
..
|
810
846
|
end
|
811
847
|
*/
|
812
|
-
static VALUE
|
813
|
-
prof_profile_class(int argc, VALUE *argv, VALUE klass)
|
848
|
+
static VALUE prof_profile_class(int argc, VALUE* argv, VALUE klass)
|
814
849
|
{
|
815
850
|
return prof_profile_object(rb_class_new_instance(argc, argv, cProfile));
|
816
851
|
}
|
@@ -820,25 +855,22 @@ prof_profile_class(int argc, VALUE *argv, VALUE klass)
|
|
820
855
|
|
821
856
|
Excludes the method from profiling results.
|
822
857
|
*/
|
823
|
-
static VALUE
|
824
|
-
prof_exclude_method(VALUE self, VALUE klass, VALUE msym)
|
858
|
+
static VALUE prof_exclude_method(VALUE self, VALUE klass, VALUE msym)
|
825
859
|
{
|
826
860
|
prof_profile_t* profile = prof_get_profile(self);
|
827
861
|
|
828
|
-
st_data_t key = method_key(klass, msym);
|
829
|
-
prof_method_t *method;
|
830
|
-
|
831
862
|
if (profile->running == Qtrue)
|
832
863
|
{
|
833
864
|
rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
|
834
865
|
}
|
835
866
|
|
836
|
-
|
867
|
+
st_data_t key = method_key(klass, msym);
|
868
|
+
prof_method_t* method = method_table_lookup(profile->exclude_methods_tbl, key);
|
837
869
|
|
838
870
|
if (!method)
|
839
871
|
{
|
840
|
-
|
841
|
-
|
872
|
+
method = prof_method_create(self, klass, msym, Qnil, 0);
|
873
|
+
method_table_insert(profile->exclude_methods_tbl, method->key, method);
|
842
874
|
}
|
843
875
|
|
844
876
|
return self;
|
@@ -861,8 +893,8 @@ VALUE prof_profile_load(VALUE self, VALUE data)
|
|
861
893
|
for (int i = 0; i < rb_array_len(threads); i++)
|
862
894
|
{
|
863
895
|
VALUE thread = rb_ary_entry(threads, i);
|
864
|
-
thread_data_t* thread_data =
|
865
|
-
|
896
|
+
thread_data_t* thread_data = prof_get_thread(thread);
|
897
|
+
rb_st_insert(profile->threads_tbl, (st_data_t)thread_data->fiber_id, (st_data_t)thread_data);
|
866
898
|
}
|
867
899
|
|
868
900
|
return data;
|
@@ -870,8 +902,6 @@ VALUE prof_profile_load(VALUE self, VALUE data)
|
|
870
902
|
|
871
903
|
void rp_init_profile(void)
|
872
904
|
{
|
873
|
-
mProf = rb_define_module("RubyProf");
|
874
|
-
|
875
905
|
cProfile = rb_define_class_under(mProf, "Profile", rb_cObject);
|
876
906
|
rb_define_alloc_func(cProfile, prof_allocate);
|
877
907
|
|