ruby-prof 0.16.2 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|