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