ruby-prof 0.16.2 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES +15 -0
- data/README.rdoc +36 -5
- data/bin/ruby-prof +7 -2
- data/doc/LICENSE.html +2 -1
- data/doc/README_rdoc.html +42 -8
- data/doc/Rack.html +2 -1
- data/doc/Rack/RubyProf.html +25 -18
- data/doc/Rack/RubyProf/RackProfiler.html +343 -0
- data/doc/RubyProf.html +14 -2
- data/doc/RubyProf/AbstractPrinter.html +91 -12
- data/doc/RubyProf/AggregateCallInfo.html +2 -1
- data/doc/RubyProf/CallInfo.html +18 -78
- data/doc/RubyProf/CallInfoPrinter.html +2 -1
- data/doc/RubyProf/CallInfoVisitor.html +2 -1
- data/doc/RubyProf/CallStackPrinter.html +35 -29
- data/doc/RubyProf/CallTreePrinter.html +98 -14
- data/doc/RubyProf/Cmd.html +11 -5
- data/doc/RubyProf/DeprecationWarnings.html +148 -0
- data/doc/RubyProf/DotPrinter.html +2 -1
- data/doc/RubyProf/FlatPrinter.html +2 -1
- data/doc/RubyProf/FlatPrinterWithLineNumbers.html +7 -5
- data/doc/RubyProf/GraphHtmlPrinter.html +18 -12
- data/doc/RubyProf/GraphPrinter.html +2 -1
- data/doc/RubyProf/MethodInfo.html +19 -88
- data/doc/RubyProf/MultiPrinter.html +231 -17
- data/doc/RubyProf/Profile.html +184 -39
- data/doc/RubyProf/Profile/ExcludeCommonMethods.html +411 -0
- data/doc/RubyProf/Profile/LegacyMethodElimination.html +158 -0
- data/doc/RubyProf/ProfileTask.html +2 -1
- data/doc/RubyProf/Thread.html +4 -39
- data/doc/created.rid +21 -19
- data/doc/css/fonts.css +6 -6
- data/doc/examples/flat_txt.html +2 -1
- data/doc/examples/graph_html.html +2 -1
- data/doc/examples/graph_txt.html +2 -1
- data/doc/index.html +47 -7
- data/doc/js/darkfish.js +7 -7
- data/doc/js/search_index.js +1 -1
- data/doc/js/search_index.js.gz +0 -0
- data/doc/js/searcher.js +1 -0
- data/doc/js/searcher.js.gz +0 -0
- data/doc/table_of_contents.html +190 -80
- data/ext/ruby_prof/extconf.rb +4 -0
- data/ext/ruby_prof/rp_call_info.c +19 -1
- data/ext/ruby_prof/rp_call_info.h +8 -3
- data/ext/ruby_prof/rp_method.c +282 -57
- data/ext/ruby_prof/rp_method.h +28 -5
- data/ext/ruby_prof/rp_stack.c +69 -24
- data/ext/ruby_prof/rp_stack.h +21 -9
- data/ext/ruby_prof/rp_thread.c +4 -1
- data/ext/ruby_prof/ruby_prof.c +142 -39
- data/ext/ruby_prof/ruby_prof.h +3 -0
- data/lib/ruby-prof.rb +10 -0
- data/lib/ruby-prof/call_info.rb +0 -11
- data/lib/ruby-prof/method_info.rb +4 -12
- data/lib/ruby-prof/printers/abstract_printer.rb +19 -1
- data/lib/ruby-prof/printers/call_info_printer.rb +1 -1
- data/lib/ruby-prof/printers/call_stack_printer.rb +9 -4
- data/lib/ruby-prof/printers/call_tree_printer.rb +15 -2
- data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +23 -4
- data/lib/ruby-prof/printers/graph_html_printer.rb +10 -5
- data/lib/ruby-prof/printers/graph_printer.rb +2 -2
- data/lib/ruby-prof/printers/multi_printer.rb +44 -18
- data/lib/ruby-prof/profile.rb +13 -42
- data/lib/ruby-prof/profile/exclude_common_methods.rb +201 -0
- data/lib/ruby-prof/profile/legacy_method_elimination.rb +49 -0
- data/lib/ruby-prof/rack.rb +130 -51
- data/lib/ruby-prof/thread.rb +0 -6
- data/lib/ruby-prof/version.rb +1 -1
- data/ruby-prof.gemspec +4 -3
- data/test/aggregate_test.rb +1 -1
- data/test/exclude_methods_test.rb +146 -0
- data/test/line_number_test.rb +12 -3
- data/test/multi_printer_test.rb +23 -2
- data/test/no_method_class_test.rb +1 -1
- data/test/printers_test.rb +21 -1
- data/test/rack_test.rb +64 -0
- data/test/recursive_test.rb +15 -15
- data/test/test_helper.rb +11 -0
- metadata +20 -13
data/ext/ruby_prof/rp_method.h
CHANGED
@@ -16,18 +16,39 @@ typedef struct
|
|
16
16
|
st_index_t key; /* Cache calculated key */
|
17
17
|
} prof_method_key_t;
|
18
18
|
|
19
|
+
/* Source relation bit offsets. */
|
20
|
+
enum {
|
21
|
+
kModuleIncludee = 0, /* Included module */
|
22
|
+
kModuleSingleton, /* Singleton class of a module */
|
23
|
+
kObjectSingleton /* Singleton class of an object */
|
24
|
+
};
|
19
25
|
|
20
26
|
/* Forward declaration, see rp_call_info.h */
|
21
27
|
struct prof_call_infos_t;
|
22
28
|
|
23
29
|
/* Profiling information for each method. */
|
24
|
-
|
30
|
+
/* Excluded methods have no call_infos, source_klass, or source_file. */
|
31
|
+
typedef struct
|
25
32
|
{
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
33
|
+
/* Hot */
|
34
|
+
|
35
|
+
prof_method_key_t *key; /* Table key */
|
36
|
+
|
37
|
+
struct prof_call_infos_t *call_infos; /* Call infos */
|
38
|
+
int visits; /* Current visits on the stack */
|
39
|
+
|
40
|
+
unsigned int excluded : 1; /* Exclude from profile? */
|
41
|
+
unsigned int recursive : 1; /* Recursive (direct or mutual)? */
|
42
|
+
|
43
|
+
/* Cold */
|
44
|
+
|
30
45
|
VALUE object; /* Cached ruby object */
|
46
|
+
VALUE source_klass; /* Source class */
|
47
|
+
const char *source_file; /* Source file */
|
48
|
+
int line; /* Line number */
|
49
|
+
|
50
|
+
unsigned int resolved : 1; /* Source resolved? */
|
51
|
+
unsigned int relation : 3; /* Source relation bits */
|
31
52
|
} prof_method_t;
|
32
53
|
|
33
54
|
void rp_init_method_info(void);
|
@@ -40,6 +61,8 @@ size_t method_table_insert(st_table *table, const prof_method_key_t *key, prof_m
|
|
40
61
|
void method_table_free(st_table *table);
|
41
62
|
|
42
63
|
prof_method_t* prof_method_create(VALUE klass, ID mid, const char* source_file, int line);
|
64
|
+
prof_method_t* prof_method_create_excluded(VALUE klass, ID mid);
|
65
|
+
|
43
66
|
VALUE prof_method_wrap(prof_method_t *result);
|
44
67
|
void prof_method_mark(prof_method_t *method);
|
45
68
|
|
data/ext/ruby_prof/rp_stack.c
CHANGED
@@ -43,13 +43,15 @@ prof_stack_free(prof_stack_t *stack)
|
|
43
43
|
}
|
44
44
|
|
45
45
|
prof_frame_t *
|
46
|
-
prof_stack_push(prof_stack_t *stack, double measurement)
|
46
|
+
prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measurement, int paused)
|
47
47
|
{
|
48
|
-
prof_frame_t*
|
48
|
+
prof_frame_t *result;
|
49
|
+
prof_frame_t* parent_frame;
|
50
|
+
prof_method_t *method;
|
49
51
|
|
50
52
|
/* Is there space on the stack? If not, double
|
51
53
|
its size. */
|
52
|
-
if (stack->ptr == stack->end
|
54
|
+
if (stack->ptr == stack->end)
|
53
55
|
{
|
54
56
|
size_t len = stack->ptr - stack->start;
|
55
57
|
size_t new_capacity = (stack->end - stack->start) * 2;
|
@@ -59,17 +61,42 @@ prof_stack_push(prof_stack_t *stack, double measurement)
|
|
59
61
|
stack->end = stack->start + new_capacity;
|
60
62
|
}
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
-
|
64
|
+
parent_frame = prof_stack_peek(stack);
|
65
|
+
|
66
|
+
// Reserve the next available frame pointer.
|
67
|
+
result = stack->ptr++;
|
68
|
+
|
69
|
+
result->call_info = call_info;
|
70
|
+
result->call_info->depth = (int)(stack->ptr - stack->start); // shortening of 64 bit into 32;
|
71
|
+
result->passes = 0;
|
72
|
+
|
73
|
+
result->start_time = measurement;
|
74
|
+
result->pause_time = -1; // init as not paused.
|
65
75
|
result->switch_time = 0;
|
66
76
|
result->wait_time = 0;
|
77
|
+
result->child_time = 0;
|
67
78
|
result->dead_time = 0;
|
68
|
-
result->depth = (int)(stack->ptr - stack->start); // shortening of 64 bit into 32
|
69
|
-
result->start_time = measurement;
|
70
79
|
|
71
|
-
|
72
|
-
|
80
|
+
method = call_info->target;
|
81
|
+
|
82
|
+
/* If the method was visited previously, it's recursive. */
|
83
|
+
if (method->visits > 0)
|
84
|
+
{
|
85
|
+
method->recursive = 1;
|
86
|
+
call_info->recursive = 1;
|
87
|
+
}
|
88
|
+
/* Enter the method. */
|
89
|
+
method->visits++;
|
90
|
+
|
91
|
+
// Unpause the parent frame, if it exists.
|
92
|
+
// If currently paused then:
|
93
|
+
// 1) The child frame will begin paused.
|
94
|
+
// 2) The parent will inherit the child's dead time.
|
95
|
+
prof_frame_unpause(parent_frame, measurement);
|
96
|
+
|
97
|
+
if (paused) {
|
98
|
+
prof_frame_pause(result, measurement);
|
99
|
+
}
|
73
100
|
|
74
101
|
// Return the result
|
75
102
|
return result;
|
@@ -78,21 +105,33 @@ prof_stack_push(prof_stack_t *stack, double measurement)
|
|
78
105
|
prof_frame_t *
|
79
106
|
prof_stack_pop(prof_stack_t *stack, double measurement)
|
80
107
|
{
|
81
|
-
prof_frame_t *frame
|
82
|
-
prof_frame_t*
|
108
|
+
prof_frame_t *frame;
|
109
|
+
prof_frame_t *parent_frame;
|
83
110
|
prof_call_info_t *call_info;
|
111
|
+
prof_method_t *method;
|
84
112
|
|
85
113
|
double total_time;
|
86
114
|
double self_time;
|
87
115
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
116
|
+
frame = prof_stack_peek(stack);
|
117
|
+
|
118
|
+
/* Frame can be null, which means the stack is empty. This can happen if
|
119
|
+
RubProf.start is called from a method that exits. And it can happen if an
|
120
|
+
exception is raised in code that is being profiled and the stack unwinds
|
121
|
+
(RubyProf is not notified of that by the ruby runtime. */
|
122
|
+
if (!frame) {
|
93
123
|
return NULL;
|
94
|
-
|
95
|
-
|
124
|
+
}
|
125
|
+
|
126
|
+
/* Match passes until we reach the frame itself. */
|
127
|
+
if (prof_frame_is_pass(frame)) {
|
128
|
+
frame->passes--;
|
129
|
+
/* Additional frames can be consumed. See pop_frames(). */
|
130
|
+
return frame;
|
131
|
+
}
|
132
|
+
|
133
|
+
/* Consume this frame. */
|
134
|
+
stack->ptr--;
|
96
135
|
|
97
136
|
/* Calculate the total time this method took */
|
98
137
|
prof_frame_unpause(frame, measurement);
|
@@ -101,11 +140,16 @@ prof_stack_pop(prof_stack_t *stack, double measurement)
|
|
101
140
|
|
102
141
|
/* Update information about the current method */
|
103
142
|
call_info = frame->call_info;
|
143
|
+
method = call_info->target;
|
144
|
+
|
104
145
|
call_info->called++;
|
105
146
|
call_info->total_time += total_time;
|
106
147
|
call_info->self_time += self_time;
|
107
148
|
call_info->wait_time += frame->wait_time;
|
108
149
|
|
150
|
+
/* Leave the method. */
|
151
|
+
method->visits--;
|
152
|
+
|
109
153
|
parent_frame = prof_stack_peek(stack);
|
110
154
|
if (parent_frame)
|
111
155
|
{
|
@@ -119,10 +163,11 @@ prof_stack_pop(prof_stack_t *stack, double measurement)
|
|
119
163
|
}
|
120
164
|
|
121
165
|
prof_frame_t *
|
122
|
-
|
166
|
+
prof_stack_pass(prof_stack_t *stack)
|
123
167
|
{
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
168
|
+
prof_frame_t *frame = prof_stack_peek(stack);
|
169
|
+
if (frame) {
|
170
|
+
frame->passes++;
|
171
|
+
}
|
172
|
+
return frame;
|
128
173
|
}
|
data/ext/ruby_prof/rp_stack.h
CHANGED
@@ -13,39 +13,51 @@
|
|
13
13
|
/* Temporary object that maintains profiling information
|
14
14
|
for active methods. They are created and destroyed
|
15
15
|
as the program moves up and down its stack. */
|
16
|
-
typedef struct
|
16
|
+
typedef struct
|
17
17
|
{
|
18
18
|
/* Caching prof_method_t values significantly
|
19
19
|
increases performance. */
|
20
20
|
prof_call_info_t *call_info;
|
21
|
+
|
22
|
+
unsigned int line;
|
23
|
+
unsigned int passes; /* Count of "pass" frames, _after_ this one. */
|
24
|
+
|
21
25
|
double start_time;
|
22
26
|
double switch_time; /* Time at switch to different thread */
|
23
27
|
double wait_time;
|
24
28
|
double child_time;
|
25
29
|
double pause_time; // Time pause() was initiated
|
26
30
|
double dead_time; // Time to ignore (i.e. total amount of time between pause/resume blocks)
|
27
|
-
int depth;
|
28
|
-
unsigned int line;
|
29
31
|
} prof_frame_t;
|
30
32
|
|
33
|
+
#define prof_frame_is_real(f) ((f)->passes == 0)
|
34
|
+
#define prof_frame_is_pass(f) ((f)->passes > 0)
|
35
|
+
|
31
36
|
#define prof_frame_is_paused(f) (f->pause_time >= 0)
|
32
37
|
#define prof_frame_is_unpaused(f) (f->pause_time < 0)
|
38
|
+
|
33
39
|
void prof_frame_pause(prof_frame_t*, double current_measurement);
|
34
40
|
void prof_frame_unpause(prof_frame_t*, double current_measurement);
|
35
41
|
|
36
|
-
|
37
42
|
/* Current stack of active methods.*/
|
38
|
-
typedef struct
|
43
|
+
typedef struct
|
39
44
|
{
|
40
45
|
prof_frame_t *start;
|
41
46
|
prof_frame_t *end;
|
42
47
|
prof_frame_t *ptr;
|
43
48
|
} prof_stack_t;
|
44
49
|
|
45
|
-
prof_stack_t *
|
50
|
+
prof_stack_t *prof_stack_create();
|
46
51
|
void prof_stack_free(prof_stack_t *stack);
|
47
|
-
|
48
|
-
prof_frame_t *
|
49
|
-
prof_frame_t *
|
52
|
+
|
53
|
+
prof_frame_t *prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measurement, int paused);
|
54
|
+
prof_frame_t *prof_stack_pop(prof_stack_t *stack, double measurement);
|
55
|
+
prof_frame_t *prof_stack_pass(prof_stack_t *stack);
|
56
|
+
|
57
|
+
static inline prof_frame_t *
|
58
|
+
prof_stack_peek(prof_stack_t *stack) {
|
59
|
+
return stack->ptr != stack->start ? stack->ptr - 1 : NULL;
|
60
|
+
}
|
61
|
+
|
50
62
|
|
51
63
|
#endif //__RP_STACK__
|
data/ext/ruby_prof/rp_thread.c
CHANGED
@@ -217,7 +217,10 @@ collect_methods(st_data_t key, st_data_t value, st_data_t result)
|
|
217
217
|
We want to store the method info information into an array.*/
|
218
218
|
VALUE methods = (VALUE) result;
|
219
219
|
prof_method_t *method = (prof_method_t *) value;
|
220
|
-
|
220
|
+
|
221
|
+
if (!method->excluded) {
|
222
|
+
rb_ary_push(methods, prof_method_wrap(method));
|
223
|
+
}
|
221
224
|
|
222
225
|
return ST_CONTINUE;
|
223
226
|
}
|
data/ext/ruby_prof/ruby_prof.c
CHANGED
@@ -28,6 +28,7 @@
|
|
28
28
|
|
29
29
|
VALUE mProf;
|
30
30
|
VALUE cProfile;
|
31
|
+
VALUE cExcludeCommonMethods;
|
31
32
|
|
32
33
|
static prof_profile_t*
|
33
34
|
prof_get_profile(VALUE self)
|
@@ -70,6 +71,19 @@ get_event_name(rb_event_flag_t event)
|
|
70
71
|
}
|
71
72
|
}
|
72
73
|
|
74
|
+
static int
|
75
|
+
excludes_method(prof_method_key_t *key, prof_profile_t *profile)
|
76
|
+
{
|
77
|
+
return (profile->exclude_methods_tbl &&
|
78
|
+
method_table_lookup(profile->exclude_methods_tbl, key) != NULL);
|
79
|
+
}
|
80
|
+
|
81
|
+
static void
|
82
|
+
prof_exclude_common_methods(VALUE profile)
|
83
|
+
{
|
84
|
+
rb_funcall(cExcludeCommonMethods, rb_intern("apply!"), 1, profile);
|
85
|
+
}
|
86
|
+
|
73
87
|
static prof_method_t*
|
74
88
|
create_method(rb_event_flag_t event, VALUE klass, ID mid, const char* source_file, int line)
|
75
89
|
{
|
@@ -83,24 +97,36 @@ create_method(rb_event_flag_t event, VALUE klass, ID mid, const char* source_fil
|
|
83
97
|
return prof_method_create(klass, mid, source_file, line);
|
84
98
|
}
|
85
99
|
|
86
|
-
|
87
100
|
static prof_method_t*
|
88
|
-
get_method(rb_event_flag_t event, VALUE klass, ID mid, thread_data_t*
|
101
|
+
get_method(rb_event_flag_t event, VALUE klass, ID mid, thread_data_t *thread_data, prof_profile_t *profile)
|
89
102
|
{
|
90
103
|
prof_method_key_t key;
|
91
104
|
prof_method_t *method = NULL;
|
92
105
|
|
106
|
+
/* Probe the local table. */
|
93
107
|
method_key(&key, klass, mid);
|
94
108
|
method = method_table_lookup(thread_data->method_table, &key);
|
95
109
|
|
96
110
|
if (!method)
|
97
111
|
{
|
98
|
-
|
99
|
-
|
112
|
+
/* Didn't find it; are we excluding it specifically? */
|
113
|
+
if (excludes_method(&key, profile)) {
|
114
|
+
/* We found a exclusion sentinel so propagate it into the thread's local hash table. */
|
115
|
+
/* TODO(nelgau): Is there a way to avoid this allocation completely so that all these
|
116
|
+
tables share the same exclusion method struct? The first attempt failed due to my
|
117
|
+
ignorance of the whims of the GC. */
|
118
|
+
method = prof_method_create_excluded(klass, mid);
|
119
|
+
} else {
|
120
|
+
/* This method has no entry for this thread/fiber and isn't specifically excluded. */
|
121
|
+
const char* source_file = rb_sourcefile();
|
122
|
+
int line = rb_sourceline();
|
123
|
+
method = create_method(event, klass, mid, source_file, line);
|
124
|
+
}
|
100
125
|
|
101
|
-
|
126
|
+
/* Insert the newly created method, or the exlcusion sentinel. */
|
102
127
|
method_table_insert(thread_data->method_table, method->key, method);
|
103
128
|
}
|
129
|
+
|
104
130
|
return method;
|
105
131
|
}
|
106
132
|
|
@@ -238,10 +264,11 @@ prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE kla
|
|
238
264
|
/* Keep track of the current line number in this method. When
|
239
265
|
a new method is called, we know what line number it was
|
240
266
|
called from. */
|
241
|
-
|
242
267
|
if (frame)
|
243
268
|
{
|
244
|
-
|
269
|
+
if (prof_frame_is_real(frame)) {
|
270
|
+
frame->line = rb_sourceline();
|
271
|
+
}
|
245
272
|
break;
|
246
273
|
}
|
247
274
|
|
@@ -252,10 +279,16 @@ prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE kla
|
|
252
279
|
case RUBY_EVENT_CALL:
|
253
280
|
case RUBY_EVENT_C_CALL:
|
254
281
|
{
|
255
|
-
|
256
|
-
|
282
|
+
prof_frame_t *next_frame;
|
283
|
+
prof_call_info_t *call_info;
|
284
|
+
prof_method_t *method;
|
257
285
|
|
258
|
-
method = get_method(event, klass, mid, thread_data);
|
286
|
+
method = get_method(event, klass, mid, thread_data, profile);
|
287
|
+
|
288
|
+
if (method->excluded) {
|
289
|
+
prof_stack_pass(thread_data->stack);
|
290
|
+
break;
|
291
|
+
}
|
259
292
|
|
260
293
|
if (!frame)
|
261
294
|
{
|
@@ -274,26 +307,18 @@ prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE kla
|
|
274
307
|
call_info_table_insert(frame->call_info->call_infos, method->key, call_info);
|
275
308
|
prof_add_call_info(method->call_infos, call_info);
|
276
309
|
}
|
277
|
-
|
278
|
-
// Unpause the parent frame. If currently paused then:
|
279
|
-
// 1) The child frame will begin paused.
|
280
|
-
// 2) The parent will inherit the child's dead time.
|
281
|
-
prof_frame_unpause(frame, measurement);
|
282
310
|
}
|
283
311
|
|
284
312
|
/* Push a new frame onto the stack for a new c-call or ruby call (into a method) */
|
285
|
-
|
286
|
-
|
287
|
-
frame->call_info->depth = frame->depth;
|
288
|
-
frame->pause_time = profile->paused == Qtrue ? measurement : -1;
|
289
|
-
frame->line = rb_sourceline();
|
313
|
+
next_frame = prof_stack_push(thread_data->stack, call_info, measurement, RTEST(profile->paused));
|
314
|
+
next_frame->line = rb_sourceline();
|
290
315
|
break;
|
291
316
|
}
|
292
317
|
case RUBY_EVENT_RETURN:
|
293
318
|
case RUBY_EVENT_C_RETURN:
|
294
319
|
{
|
295
|
-
|
296
|
-
|
320
|
+
prof_stack_pop(thread_data->stack, measurement);
|
321
|
+
break;
|
297
322
|
}
|
298
323
|
}
|
299
324
|
}
|
@@ -307,10 +332,19 @@ prof_install_hook(VALUE self)
|
|
307
332
|
RUBY_EVENT_LINE, self);
|
308
333
|
}
|
309
334
|
|
335
|
+
#ifdef HAVE_RB_REMOVE_EVENT_HOOK_WITH_DATA
|
336
|
+
extern int
|
337
|
+
rb_remove_event_hook_with_data(rb_event_hook_func_t func, VALUE data);
|
338
|
+
#endif
|
339
|
+
|
310
340
|
void
|
311
|
-
prof_remove_hook()
|
341
|
+
prof_remove_hook(VALUE self)
|
312
342
|
{
|
343
|
+
#ifdef HAVE_RB_REMOVE_EVENT_HOOK_WITH_DATA
|
344
|
+
rb_remove_event_hook_with_data(prof_event_hook, self);
|
345
|
+
#else
|
313
346
|
rb_remove_event_hook(prof_event_hook);
|
347
|
+
#endif
|
314
348
|
}
|
315
349
|
|
316
350
|
static int
|
@@ -331,10 +365,19 @@ mark_threads(st_data_t key, st_data_t value, st_data_t result)
|
|
331
365
|
return ST_CONTINUE;
|
332
366
|
}
|
333
367
|
|
368
|
+
static int
|
369
|
+
mark_methods(st_data_t key, st_data_t value, st_data_t result)
|
370
|
+
{
|
371
|
+
prof_method_t *method = (prof_method_t *) value;
|
372
|
+
prof_method_mark(method);
|
373
|
+
return ST_CONTINUE;
|
374
|
+
}
|
375
|
+
|
334
376
|
static void
|
335
377
|
prof_mark(prof_profile_t *profile)
|
336
378
|
{
|
337
379
|
st_foreach(profile->threads_tbl, mark_threads, 0);
|
380
|
+
st_foreach(profile->exclude_methods_tbl, mark_methods, 0);
|
338
381
|
}
|
339
382
|
|
340
383
|
/* Freeing the profile creates a cascade of freeing.
|
@@ -358,6 +401,10 @@ prof_free(prof_profile_t *profile)
|
|
358
401
|
profile->include_threads_tbl = NULL;
|
359
402
|
}
|
360
403
|
|
404
|
+
/* This table owns the excluded sentinels for now. */
|
405
|
+
method_table_free(profile->exclude_methods_tbl);
|
406
|
+
profile->exclude_methods_tbl = NULL;
|
407
|
+
|
361
408
|
xfree(profile->measurer);
|
362
409
|
profile->measurer = NULL;
|
363
410
|
|
@@ -375,6 +422,8 @@ prof_allocate(VALUE klass)
|
|
375
422
|
profile->include_threads_tbl = NULL;
|
376
423
|
profile->running = Qfalse;
|
377
424
|
profile->merge_fibers = 0;
|
425
|
+
profile->exclude_methods_tbl = method_table_create();
|
426
|
+
profile->running = Qfalse;
|
378
427
|
return result;
|
379
428
|
}
|
380
429
|
|
@@ -401,6 +450,7 @@ prof_initialize(int argc, VALUE *argv, VALUE self)
|
|
401
450
|
VALUE exclude_threads = Qnil;
|
402
451
|
VALUE include_threads = Qnil;
|
403
452
|
VALUE merge_fibers = Qnil;
|
453
|
+
VALUE exclude_common = Qnil;
|
404
454
|
int i;
|
405
455
|
|
406
456
|
switch (rb_scan_args(argc, argv, "02", &mode_or_options, &exclude_threads)) {
|
@@ -414,6 +464,7 @@ prof_initialize(int argc, VALUE *argv, VALUE self)
|
|
414
464
|
Check_Type(mode_or_options, T_HASH);
|
415
465
|
mode = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("measure_mode")));
|
416
466
|
merge_fibers = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("merge_fibers")));
|
467
|
+
exclude_common = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("exclude_common")));
|
417
468
|
exclude_threads = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("exclude_threads")));
|
418
469
|
include_threads = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("include_threads")));
|
419
470
|
}
|
@@ -453,6 +504,10 @@ prof_initialize(int argc, VALUE *argv, VALUE self)
|
|
453
504
|
}
|
454
505
|
}
|
455
506
|
|
507
|
+
if (RTEST(exclude_common)) {
|
508
|
+
prof_exclude_common_methods(self);
|
509
|
+
}
|
510
|
+
|
456
511
|
return self;
|
457
512
|
}
|
458
513
|
|
@@ -581,11 +636,11 @@ prof_stop(VALUE self)
|
|
581
636
|
{
|
582
637
|
rb_raise(rb_eRuntimeError, "RubyProf.start was not yet called");
|
583
638
|
}
|
584
|
-
|
585
|
-
prof_remove_hook();
|
639
|
+
|
640
|
+
prof_remove_hook(self);
|
586
641
|
|
587
642
|
/* close trace file if open */
|
588
|
-
if (trace_file != NULL)
|
643
|
+
if (trace_file != NULL)
|
589
644
|
{
|
590
645
|
if (trace_file !=stderr && trace_file != stdout)
|
591
646
|
{
|
@@ -597,7 +652,7 @@ prof_stop(VALUE self)
|
|
597
652
|
}
|
598
653
|
trace_file = NULL;
|
599
654
|
}
|
600
|
-
|
655
|
+
|
601
656
|
prof_pop_threads(profile);
|
602
657
|
|
603
658
|
/* Unset the last_thread_data (very important!)
|
@@ -605,12 +660,23 @@ prof_stop(VALUE self)
|
|
605
660
|
profile->running = profile->paused = Qfalse;
|
606
661
|
profile->last_thread_data = NULL;
|
607
662
|
|
608
|
-
/* Post process result */
|
609
|
-
rb_funcall(self, rb_intern("post_process") , 0);
|
610
|
-
|
611
663
|
return self;
|
612
664
|
}
|
613
665
|
|
666
|
+
/* call-seq:
|
667
|
+
threads -> Array of RubyProf::Thread
|
668
|
+
|
669
|
+
Returns an array of RubyProf::Thread instances that were executed
|
670
|
+
while the the program was being run. */
|
671
|
+
static VALUE
|
672
|
+
prof_threads(VALUE self)
|
673
|
+
{
|
674
|
+
VALUE result = rb_ary_new();
|
675
|
+
prof_profile_t* profile = prof_get_profile(self);
|
676
|
+
st_foreach(profile->threads_tbl, collect_threads, result);
|
677
|
+
return result;
|
678
|
+
}
|
679
|
+
|
614
680
|
/* call-seq:
|
615
681
|
profile(&block) -> self
|
616
682
|
profile(options, &block) -> self
|
@@ -619,7 +685,7 @@ Profiles the specified block and returns a RubyProf::Profile
|
|
619
685
|
object. Arguments are passed to Profile initialize method.
|
620
686
|
*/
|
621
687
|
static VALUE
|
622
|
-
|
688
|
+
prof_profile_class(int argc, VALUE *argv, VALUE klass)
|
623
689
|
{
|
624
690
|
int result;
|
625
691
|
VALUE profile = rb_class_new_instance(argc, argv, cProfile);
|
@@ -635,17 +701,47 @@ prof_profile(int argc, VALUE *argv, VALUE klass)
|
|
635
701
|
}
|
636
702
|
|
637
703
|
/* call-seq:
|
638
|
-
|
704
|
+
profile {block} -> RubyProf::Result
|
639
705
|
|
640
|
-
|
641
|
-
while the the program was being run. */
|
706
|
+
Profiles the specified block and returns a RubyProf::Result object. */
|
642
707
|
static VALUE
|
643
|
-
|
708
|
+
prof_profile_object(VALUE self)
|
709
|
+
{
|
710
|
+
int result;
|
711
|
+
if (!rb_block_given_p())
|
712
|
+
{
|
713
|
+
rb_raise(rb_eArgError, "A block must be provided to the profile method.");
|
714
|
+
}
|
715
|
+
|
716
|
+
prof_start(self);
|
717
|
+
rb_protect(rb_yield, self, &result);
|
718
|
+
return prof_stop(self);
|
719
|
+
|
720
|
+
}
|
721
|
+
|
722
|
+
static VALUE
|
723
|
+
prof_exclude_method(VALUE self, VALUE klass, VALUE sym)
|
644
724
|
{
|
645
|
-
VALUE result = rb_ary_new();
|
646
725
|
prof_profile_t* profile = prof_get_profile(self);
|
647
|
-
|
648
|
-
|
726
|
+
ID mid = SYM2ID(sym);
|
727
|
+
|
728
|
+
prof_method_key_t key;
|
729
|
+
prof_method_t *method;
|
730
|
+
|
731
|
+
if (profile->running == Qtrue)
|
732
|
+
{
|
733
|
+
rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
|
734
|
+
}
|
735
|
+
|
736
|
+
method_key(&key, klass, mid);
|
737
|
+
method = method_table_lookup(profile->exclude_methods_tbl, &key);
|
738
|
+
|
739
|
+
if (!method) {
|
740
|
+
method = prof_method_create_excluded(klass, mid);
|
741
|
+
method_table_insert(profile->exclude_methods_tbl, method->key, method);
|
742
|
+
}
|
743
|
+
|
744
|
+
return self;
|
649
745
|
}
|
650
746
|
|
651
747
|
void Init_ruby_prof()
|
@@ -658,8 +754,8 @@ void Init_ruby_prof()
|
|
658
754
|
rp_init_thread();
|
659
755
|
|
660
756
|
cProfile = rb_define_class_under(mProf, "Profile", rb_cObject);
|
661
|
-
rb_define_singleton_method(cProfile, "profile", prof_profile, -1);
|
662
757
|
rb_define_alloc_func (cProfile, prof_allocate);
|
758
|
+
|
663
759
|
rb_define_method(cProfile, "initialize", prof_initialize, -1);
|
664
760
|
rb_define_method(cProfile, "start", prof_start, 0);
|
665
761
|
rb_define_method(cProfile, "stop", prof_stop, 0);
|
@@ -668,4 +764,11 @@ void Init_ruby_prof()
|
|
668
764
|
rb_define_method(cProfile, "running?", prof_running, 0);
|
669
765
|
rb_define_method(cProfile, "paused?", prof_paused, 0);
|
670
766
|
rb_define_method(cProfile, "threads", prof_threads, 0);
|
767
|
+
|
768
|
+
rb_define_singleton_method(cProfile, "profile", prof_profile_class, -1);
|
769
|
+
rb_define_method(cProfile, "profile", prof_profile_object, 0);
|
770
|
+
|
771
|
+
rb_define_method(cProfile, "exclude_method!", prof_exclude_method, 2);
|
772
|
+
|
773
|
+
cExcludeCommonMethods = rb_define_class_under(cProfile, "ExcludeCommonMethods", rb_cObject);
|
671
774
|
}
|