ruby-prof 1.1.0 → 1.2.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 +12 -1
- data/bin/ruby-prof +100 -152
- data/ext/ruby_prof/rp_aggregate_call_tree.c +41 -0
- data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
- data/ext/ruby_prof/rp_allocation.c +31 -51
- data/ext/ruby_prof/rp_allocation.h +2 -2
- data/ext/ruby_prof/rp_call_tree.c +353 -0
- data/ext/ruby_prof/rp_call_tree.h +43 -0
- data/ext/ruby_prof/rp_call_trees.c +266 -0
- data/ext/ruby_prof/rp_call_trees.h +29 -0
- data/ext/ruby_prof/rp_measure_allocations.c +11 -13
- data/ext/ruby_prof/rp_measure_process_time.c +11 -13
- data/ext/ruby_prof/rp_measure_wall_time.c +17 -15
- data/ext/ruby_prof/rp_measurement.c +27 -36
- data/ext/ruby_prof/rp_measurement.h +6 -6
- data/ext/ruby_prof/rp_method.c +88 -248
- data/ext/ruby_prof/rp_method.h +12 -19
- data/ext/ruby_prof/rp_profile.c +277 -270
- data/ext/ruby_prof/rp_profile.h +0 -1
- data/ext/ruby_prof/rp_stack.c +113 -105
- data/ext/ruby_prof/rp_stack.h +15 -18
- data/ext/ruby_prof/rp_thread.c +115 -107
- data/ext/ruby_prof/rp_thread.h +9 -8
- data/ext/ruby_prof/ruby_prof.c +27 -23
- data/ext/ruby_prof/ruby_prof.h +9 -0
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +11 -7
- data/lib/ruby-prof.rb +2 -3
- data/lib/ruby-prof/assets/call_stack_printer.html.erb +4 -7
- data/lib/ruby-prof/assets/graph_printer.html.erb +5 -6
- data/lib/ruby-prof/{call_info.rb → call_tree.rb} +6 -6
- data/lib/ruby-prof/call_tree_visitor.rb +36 -0
- data/lib/ruby-prof/measurement.rb +5 -2
- data/lib/ruby-prof/method_info.rb +3 -15
- data/lib/ruby-prof/printers/call_info_printer.rb +12 -10
- data/lib/ruby-prof/printers/call_stack_printer.rb +19 -22
- data/lib/ruby-prof/printers/call_tree_printer.rb +1 -1
- data/lib/ruby-prof/printers/dot_printer.rb +3 -3
- data/lib/ruby-prof/printers/graph_printer.rb +3 -4
- data/lib/ruby-prof/printers/multi_printer.rb +2 -2
- data/lib/ruby-prof/rack.rb +3 -0
- data/lib/ruby-prof/thread.rb +3 -18
- data/lib/ruby-prof/version.rb +1 -1
- data/ruby-prof.gemspec +7 -0
- data/test/alias_test.rb +42 -45
- data/test/basic_test.rb +0 -86
- data/test/{call_info_visitor_test.rb → call_tree_visitor_test.rb} +6 -5
- data/test/call_trees_test.rb +66 -0
- data/test/exclude_methods_test.rb +17 -12
- data/test/fiber_test.rb +203 -6
- data/test/gc_test.rb +32 -23
- data/test/inverse_call_tree_test.rb +175 -0
- data/test/line_number_test.rb +64 -67
- data/test/marshal_test.rb +7 -11
- data/test/measure_allocations_test.rb +224 -234
- data/test/measure_allocations_trace_test.rb +224 -234
- data/test/measure_memory_trace_test.rb +814 -469
- data/test/measure_process_time_test.rb +0 -64
- data/test/measure_times.rb +2 -0
- data/test/measure_wall_time_test.rb +34 -58
- data/test/pause_resume_test.rb +19 -10
- data/test/prime.rb +1 -3
- data/test/prime_script.rb +6 -0
- data/test/printers_test.rb +1 -1
- data/test/recursive_test.rb +50 -54
- data/test/start_stop_test.rb +19 -19
- data/test/test_helper.rb +3 -15
- data/test/thread_test.rb +11 -11
- data/test/unique_call_path_test.rb +25 -95
- metadata +19 -9
- data/ext/ruby_prof/rp_call_info.c +0 -271
- data/ext/ruby_prof/rp_call_info.h +0 -35
- data/lib/ruby-prof/call_info_visitor.rb +0 -38
- data/test/parser_timings.rb +0 -24
data/ext/ruby_prof/rp_profile.h
CHANGED
data/ext/ruby_prof/rp_stack.c
CHANGED
@@ -3,31 +3,12 @@
|
|
3
3
|
|
4
4
|
#include "rp_stack.h"
|
5
5
|
|
6
|
-
#define INITIAL_STACK_SIZE
|
6
|
+
#define INITIAL_STACK_SIZE 16
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
// Creates a stack of prof_frame_t to keep track of timings for active methods.
|
9
|
+
prof_stack_t* prof_stack_create()
|
10
10
|
{
|
11
|
-
|
12
|
-
frame->pause_time = current_measurement;
|
13
|
-
}
|
14
|
-
|
15
|
-
void
|
16
|
-
prof_frame_unpause(prof_frame_t *frame, double current_measurement)
|
17
|
-
{
|
18
|
-
if (frame && prof_frame_is_paused(frame))
|
19
|
-
{
|
20
|
-
frame->dead_time += (current_measurement - frame->pause_time);
|
21
|
-
frame->pause_time = -1;
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
/* Creates a stack of prof_frame_t to keep track
|
26
|
-
of timings for active methods. */
|
27
|
-
prof_stack_t *
|
28
|
-
prof_stack_create()
|
29
|
-
{
|
30
|
-
prof_stack_t *stack = ALLOC(prof_stack_t);
|
11
|
+
prof_stack_t* stack = ALLOC(prof_stack_t);
|
31
12
|
stack->start = ZALLOC_N(prof_frame_t, INITIAL_STACK_SIZE);
|
32
13
|
stack->ptr = stack->start;
|
33
14
|
stack->end = stack->start + INITIAL_STACK_SIZE;
|
@@ -35,22 +16,24 @@ prof_stack_create()
|
|
35
16
|
return stack;
|
36
17
|
}
|
37
18
|
|
38
|
-
void
|
39
|
-
prof_stack_free(prof_stack_t *stack)
|
19
|
+
void prof_stack_free(prof_stack_t* stack)
|
40
20
|
{
|
41
21
|
xfree(stack->start);
|
42
22
|
xfree(stack);
|
43
23
|
}
|
44
24
|
|
45
|
-
prof_frame_t *
|
46
|
-
prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measurement, int paused)
|
25
|
+
prof_frame_t* prof_stack_last(prof_stack_t* stack)
|
47
26
|
{
|
48
|
-
|
49
|
-
|
27
|
+
if (stack->ptr == stack->start)
|
28
|
+
return NULL;
|
29
|
+
else
|
30
|
+
return stack->ptr - 1;
|
31
|
+
}
|
50
32
|
|
51
|
-
|
52
|
-
|
53
|
-
|
33
|
+
void prof_stack_verify_size(prof_stack_t* stack)
|
34
|
+
{
|
35
|
+
// Is there space on the stack? If not, double its size.
|
36
|
+
if (stack->ptr == stack->end)
|
54
37
|
{
|
55
38
|
size_t len = stack->ptr - stack->start;
|
56
39
|
size_t new_capacity = (stack->end - stack->start) * 2;
|
@@ -60,14 +43,53 @@ prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measure
|
|
60
43
|
stack->ptr = stack->start + len;
|
61
44
|
stack->end = stack->start + new_capacity;
|
62
45
|
}
|
46
|
+
}
|
47
|
+
|
48
|
+
prof_frame_t* prof_stack_push(prof_stack_t* stack)
|
49
|
+
{
|
50
|
+
prof_stack_verify_size(stack);
|
63
51
|
|
64
|
-
|
52
|
+
prof_frame_t* result = stack->ptr;
|
65
53
|
stack->ptr++;
|
54
|
+
return result;
|
55
|
+
}
|
56
|
+
|
57
|
+
prof_frame_t* prof_stack_pop(prof_stack_t* stack)
|
58
|
+
{
|
59
|
+
prof_frame_t* result = prof_stack_last(stack);
|
60
|
+
if (result)
|
61
|
+
stack->ptr--;
|
62
|
+
|
63
|
+
return result;
|
64
|
+
}
|
65
|
+
|
66
|
+
// ---------------- Frame Methods ----------------------------
|
67
|
+
void prof_frame_pause(prof_frame_t* frame, double current_measurement)
|
68
|
+
{
|
69
|
+
if (frame && prof_frame_is_unpaused(frame))
|
70
|
+
frame->pause_time = current_measurement;
|
71
|
+
}
|
72
|
+
|
73
|
+
void prof_frame_unpause(prof_frame_t* frame, double current_measurement)
|
74
|
+
{
|
75
|
+
if (prof_frame_is_paused(frame))
|
76
|
+
{
|
77
|
+
frame->dead_time += (current_measurement - frame->pause_time);
|
78
|
+
frame->pause_time = -1;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
prof_frame_t* prof_frame_current(prof_stack_t* stack)
|
83
|
+
{
|
84
|
+
return prof_stack_last(stack);
|
85
|
+
}
|
66
86
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
result
|
87
|
+
prof_frame_t* prof_frame_push(prof_stack_t* stack, prof_call_tree_t* call_tree, double measurement, bool paused)
|
88
|
+
{
|
89
|
+
prof_frame_t* parent_frame = prof_stack_last(stack);
|
90
|
+
prof_frame_t* result = prof_stack_push(stack);
|
91
|
+
|
92
|
+
result->call_tree = call_tree;
|
71
93
|
|
72
94
|
result->start_time = measurement;
|
73
95
|
result->pause_time = -1; // init as not paused.
|
@@ -78,21 +100,22 @@ prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measure
|
|
78
100
|
result->source_file = Qnil;
|
79
101
|
result->source_line = 0;
|
80
102
|
|
81
|
-
|
82
|
-
|
103
|
+
call_tree->measurement->called++;
|
104
|
+
call_tree->visits++;
|
83
105
|
|
84
|
-
if (
|
106
|
+
if (call_tree->method->visits > 0)
|
85
107
|
{
|
86
|
-
|
108
|
+
call_tree->method->recursive = true;
|
87
109
|
}
|
88
|
-
|
89
|
-
|
110
|
+
call_tree->method->measurement->called++;
|
111
|
+
call_tree->method->visits++;
|
90
112
|
|
91
113
|
// Unpause the parent frame, if it exists.
|
92
114
|
// If currently paused then:
|
93
115
|
// 1) The child frame will begin paused.
|
94
116
|
// 2) The parent will inherit the child's dead time.
|
95
|
-
|
117
|
+
if (parent_frame)
|
118
|
+
prof_frame_unpause(parent_frame, measurement);
|
96
119
|
|
97
120
|
if (paused)
|
98
121
|
{
|
@@ -103,92 +126,77 @@ prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measure
|
|
103
126
|
return result;
|
104
127
|
}
|
105
128
|
|
106
|
-
prof_frame_t *
|
107
|
-
prof_stack_pop(prof_stack_t *stack, double measurement)
|
129
|
+
prof_frame_t* prof_frame_unshift(prof_stack_t* stack, prof_call_tree_t* parent_call_tree, prof_call_tree_t* call_tree, double measurement)
|
108
130
|
{
|
109
|
-
|
110
|
-
|
111
|
-
prof_call_info_t *call_info;
|
131
|
+
if (prof_stack_last(stack))
|
132
|
+
rb_raise(rb_eRuntimeError, "Stach unshift can only be called with an empty stack");
|
112
133
|
|
113
|
-
|
114
|
-
|
134
|
+
parent_call_tree->measurement->total_time = call_tree->measurement->total_time;
|
135
|
+
parent_call_tree->measurement->self_time = 0;
|
136
|
+
parent_call_tree->measurement->wait_time = call_tree->measurement->wait_time;
|
115
137
|
|
116
|
-
|
117
|
-
|
138
|
+
parent_call_tree->method->measurement->total_time += call_tree->measurement->total_time;
|
139
|
+
parent_call_tree->method->measurement->wait_time += call_tree->measurement->wait_time;
|
118
140
|
|
119
|
-
|
120
|
-
|
121
|
-
/* Match passes until we reach the frame itself. */
|
122
|
-
if (prof_frame_is_pass(frame))
|
123
|
-
{
|
124
|
-
frame->passes--;
|
125
|
-
/* Additional frames can be consumed. See pop_frames(). */
|
126
|
-
return frame;
|
127
|
-
}
|
128
|
-
|
129
|
-
/* Consume this frame. */
|
130
|
-
stack->ptr--;
|
131
|
-
|
132
|
-
parent_frame = stack->ptr;
|
141
|
+
return prof_frame_push(stack, parent_call_tree, measurement, false);
|
142
|
+
}
|
133
143
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
total_time = measurement - frame->start_time - frame->dead_time;
|
138
|
-
self_time = total_time - frame->child_time - frame->wait_time;
|
144
|
+
prof_frame_t* prof_frame_pop(prof_stack_t* stack, double measurement)
|
145
|
+
{
|
146
|
+
prof_frame_t* frame = prof_stack_pop(stack);
|
139
147
|
|
140
|
-
|
141
|
-
|
148
|
+
if (!frame)
|
149
|
+
return NULL;
|
142
150
|
|
143
|
-
|
144
|
-
|
145
|
-
call_info->method->measurement->wait_time += frame->wait_time;
|
146
|
-
if (call_info->method->visits == 1)
|
147
|
-
call_info->method->measurement->total_time += total_time;
|
151
|
+
/* Calculate the total time this method took */
|
152
|
+
prof_frame_unpause(frame, measurement);
|
148
153
|
|
149
|
-
|
154
|
+
double total_time = measurement - frame->start_time - frame->dead_time;
|
155
|
+
double self_time = total_time - frame->child_time - frame->wait_time;
|
150
156
|
|
151
|
-
|
152
|
-
|
153
|
-
call_info->measurement->wait_time += frame->wait_time;
|
154
|
-
if (call_info->visits == 1)
|
155
|
-
call_info->measurement->total_time += total_time;
|
157
|
+
/* Update information about the current method */
|
158
|
+
prof_call_tree_t* call_tree = frame->call_tree;
|
156
159
|
|
157
|
-
|
160
|
+
// Update method measurement
|
161
|
+
call_tree->method->measurement->self_time += self_time;
|
162
|
+
call_tree->method->measurement->wait_time += frame->wait_time;
|
163
|
+
if (call_tree->method->visits == 1)
|
164
|
+
call_tree->method->measurement->total_time += total_time;
|
158
165
|
|
159
|
-
|
160
|
-
{
|
161
|
-
parent_frame->child_time += total_time;
|
162
|
-
parent_frame->dead_time += frame->dead_time;
|
163
|
-
}
|
166
|
+
call_tree->method->visits--;
|
164
167
|
|
165
|
-
|
166
|
-
|
168
|
+
// Update method measurement
|
169
|
+
call_tree->measurement->self_time += self_time;
|
170
|
+
call_tree->measurement->wait_time += frame->wait_time;
|
171
|
+
if (call_tree->visits == 1)
|
172
|
+
call_tree->measurement->total_time += total_time;
|
167
173
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
if (frame)
|
174
|
+
call_tree->visits--;
|
175
|
+
|
176
|
+
prof_frame_t* parent_frame = prof_stack_last(stack);
|
177
|
+
if (parent_frame)
|
173
178
|
{
|
174
|
-
|
179
|
+
parent_frame->child_time += total_time;
|
180
|
+
parent_frame->dead_time += frame->dead_time;
|
175
181
|
}
|
182
|
+
|
183
|
+
frame->source_file = Qnil;
|
184
|
+
|
176
185
|
return frame;
|
177
186
|
}
|
178
187
|
|
179
|
-
prof_method_t*
|
180
|
-
prof_find_method(prof_stack_t* stack, VALUE source_file, int source_line)
|
188
|
+
prof_method_t* prof_find_method(prof_stack_t* stack, VALUE source_file, int source_line)
|
181
189
|
{
|
182
190
|
prof_frame_t* frame = stack->ptr;
|
183
191
|
while (frame >= stack->start)
|
184
192
|
{
|
185
|
-
if (!frame->
|
193
|
+
if (!frame->call_tree)
|
186
194
|
return NULL;
|
187
195
|
|
188
|
-
if (rb_str_equal(source_file, frame->
|
189
|
-
source_line >= frame->
|
196
|
+
if (rb_str_equal(source_file, frame->call_tree->method->source_file) &&
|
197
|
+
source_line >= frame->call_tree->method->source_line)
|
190
198
|
{
|
191
|
-
return frame->
|
199
|
+
return frame->call_tree->method;
|
192
200
|
}
|
193
201
|
frame--;
|
194
202
|
}
|
data/ext/ruby_prof/rp_stack.h
CHANGED
@@ -5,20 +5,19 @@
|
|
5
5
|
#define __RP_STACK__
|
6
6
|
|
7
7
|
#include "ruby_prof.h"
|
8
|
-
#include "
|
8
|
+
#include "rp_call_tree.h"
|
9
9
|
|
10
|
-
/* Temporary object that maintains profiling information
|
11
|
-
|
12
|
-
|
10
|
+
/* Temporary object that maintains profiling information
|
11
|
+
for active methods. They are created and destroyed
|
12
|
+
as the program moves up and down its stack. */
|
13
13
|
typedef struct
|
14
14
|
{
|
15
15
|
/* Caching prof_method_t values significantly
|
16
16
|
increases performance. */
|
17
|
-
|
17
|
+
prof_call_tree_t* call_tree;
|
18
18
|
|
19
19
|
VALUE source_file;
|
20
20
|
unsigned int source_line;
|
21
|
-
unsigned int passes; /* Count of "pass" frames, _after_ this one. */
|
22
21
|
|
23
22
|
double start_time;
|
24
23
|
double switch_time; /* Time at switch to different thread */
|
@@ -28,9 +27,6 @@ typedef struct
|
|
28
27
|
double dead_time; // Time to ignore (i.e. total amount of time between pause/resume blocks)
|
29
28
|
} prof_frame_t;
|
30
29
|
|
31
|
-
#define prof_frame_is_real(f) ((f)->passes == 0)
|
32
|
-
#define prof_frame_is_pass(f) ((f)->passes > 0)
|
33
|
-
|
34
30
|
#define prof_frame_is_paused(f) (f->pause_time >= 0)
|
35
31
|
#define prof_frame_is_unpaused(f) (f->pause_time < 0)
|
36
32
|
|
@@ -40,17 +36,18 @@ void prof_frame_unpause(prof_frame_t*, double current_measurement);
|
|
40
36
|
/* Current stack of active methods.*/
|
41
37
|
typedef struct
|
42
38
|
{
|
43
|
-
prof_frame_t
|
44
|
-
prof_frame_t
|
45
|
-
prof_frame_t
|
39
|
+
prof_frame_t* start;
|
40
|
+
prof_frame_t* end;
|
41
|
+
prof_frame_t* ptr;
|
46
42
|
} prof_stack_t;
|
47
43
|
|
48
|
-
prof_stack_t
|
49
|
-
void prof_stack_free(prof_stack_t
|
44
|
+
prof_stack_t* prof_stack_create(void);
|
45
|
+
void prof_stack_free(prof_stack_t* stack);
|
50
46
|
|
51
|
-
prof_frame_t
|
52
|
-
prof_frame_t
|
53
|
-
prof_frame_t
|
54
|
-
|
47
|
+
prof_frame_t* prof_frame_current(prof_stack_t* stack);
|
48
|
+
prof_frame_t* prof_frame_push(prof_stack_t* stack, prof_call_tree_t* call_tree, double measurement, bool paused);
|
49
|
+
prof_frame_t* prof_frame_unshift(prof_stack_t* stack, prof_call_tree_t* parent_call_tree, prof_call_tree_t* call_tree, double measurement);
|
50
|
+
prof_frame_t* prof_frame_pop(prof_stack_t* stack, double measurement);
|
51
|
+
prof_method_t* prof_find_method(prof_stack_t* stack, VALUE source_file, int source_line);
|
55
52
|
|
56
53
|
#endif //__RP_STACK__
|
data/ext/ruby_prof/rp_thread.c
CHANGED
@@ -14,21 +14,20 @@ You cannot create an instance of RubyProf::Thread, instead you access it from a
|
|
14
14
|
thread.root_methods.sort.each do |method|
|
15
15
|
puts method.total_time
|
16
16
|
end
|
17
|
-
end
|
18
|
-
*/
|
17
|
+
end */
|
19
18
|
|
20
19
|
#include "rp_thread.h"
|
21
20
|
#include "rp_profile.h"
|
22
21
|
|
23
22
|
VALUE cRpThread;
|
24
23
|
|
25
|
-
|
26
|
-
thread_data_t*
|
27
|
-
thread_data_create(void)
|
24
|
+
// ====== thread_data_t ======
|
25
|
+
thread_data_t* thread_data_create(void)
|
28
26
|
{
|
29
27
|
thread_data_t* result = ALLOC(thread_data_t);
|
30
28
|
result->stack = prof_stack_create();
|
31
29
|
result->method_table = method_table_create();
|
30
|
+
result->call_tree = NULL;
|
32
31
|
result->object = Qnil;
|
33
32
|
result->methods = Qnil;
|
34
33
|
result->fiber_id = Qnil;
|
@@ -38,81 +37,87 @@ thread_data_create(void)
|
|
38
37
|
return result;
|
39
38
|
}
|
40
39
|
|
41
|
-
static
|
42
|
-
prof_thread_free(thread_data_t* thread_data)
|
40
|
+
static int mark_methods(st_data_t key, st_data_t value, st_data_t result)
|
43
41
|
{
|
44
|
-
|
45
|
-
yes then set its data to nil to avoid a segmentation fault on the next mark and sweep. */
|
46
|
-
if (thread_data->object != Qnil)
|
47
|
-
{
|
48
|
-
RDATA(thread_data->object)->dmark = NULL;
|
49
|
-
RDATA(thread_data->object)->dfree = NULL;
|
50
|
-
RDATA(thread_data->object)->data = NULL;
|
51
|
-
thread_data->object = Qnil;
|
52
|
-
}
|
53
|
-
|
54
|
-
method_table_free(thread_data->method_table);
|
55
|
-
prof_stack_free(thread_data->stack);
|
56
|
-
|
57
|
-
|
58
|
-
xfree(thread_data);
|
59
|
-
}
|
60
|
-
|
61
|
-
static int
|
62
|
-
mark_methods(st_data_t key, st_data_t value, st_data_t result)
|
63
|
-
{
|
64
|
-
prof_method_t *method = (prof_method_t *) value;
|
42
|
+
prof_method_t* method = (prof_method_t*)value;
|
65
43
|
prof_method_mark(method);
|
66
44
|
return ST_CONTINUE;
|
67
45
|
}
|
68
46
|
|
69
|
-
size_t
|
70
|
-
prof_thread_size(const void *data)
|
47
|
+
size_t prof_thread_size(const void* data)
|
71
48
|
{
|
72
|
-
return sizeof(
|
49
|
+
return sizeof(prof_call_tree_t);
|
73
50
|
}
|
74
51
|
|
75
|
-
void
|
76
|
-
prof_thread_mark(void *data)
|
52
|
+
void prof_thread_mark(void* data)
|
77
53
|
{
|
78
|
-
thread_data_t
|
79
|
-
|
54
|
+
thread_data_t* thread = (thread_data_t*)data;
|
55
|
+
|
80
56
|
if (thread->object != Qnil)
|
81
57
|
rb_gc_mark(thread->object);
|
82
|
-
|
58
|
+
|
59
|
+
rb_gc_mark(thread->fiber);
|
60
|
+
|
83
61
|
if (thread->methods != Qnil)
|
84
62
|
rb_gc_mark(thread->methods);
|
85
|
-
|
63
|
+
|
86
64
|
if (thread->fiber_id != Qnil)
|
87
65
|
rb_gc_mark(thread->fiber_id);
|
88
|
-
|
66
|
+
|
89
67
|
if (thread->thread_id != Qnil)
|
90
68
|
rb_gc_mark(thread->thread_id);
|
91
|
-
|
92
|
-
|
69
|
+
|
70
|
+
if (thread->call_tree)
|
71
|
+
prof_call_tree_mark(thread->call_tree);
|
72
|
+
|
73
|
+
rb_st_foreach(thread->method_table, mark_methods, 0);
|
93
74
|
}
|
94
75
|
|
76
|
+
void prof_thread_ruby_gc_free(void* data)
|
77
|
+
{
|
78
|
+
thread_data_t* thread_data = (thread_data_t*)data;
|
79
|
+
thread_data->object = Qnil;
|
80
|
+
}
|
81
|
+
|
82
|
+
static void prof_thread_free(thread_data_t* thread_data)
|
83
|
+
{
|
84
|
+
/* Has this method object been accessed by Ruby? If
|
85
|
+
yes then set its data to nil to avoid a segmentation fault on the next mark and sweep. */
|
86
|
+
if (thread_data->object != Qnil)
|
87
|
+
{
|
88
|
+
RDATA(thread_data->object)->dmark = NULL;
|
89
|
+
RDATA(thread_data->object)->dfree = NULL;
|
90
|
+
RDATA(thread_data->object)->data = NULL;
|
91
|
+
thread_data->object = Qnil;
|
92
|
+
}
|
93
|
+
|
94
|
+
method_table_free(thread_data->method_table);
|
95
|
+
|
96
|
+
if (thread_data->call_tree)
|
97
|
+
prof_call_tree_free(thread_data->call_tree);
|
98
|
+
|
99
|
+
prof_stack_free(thread_data->stack);
|
100
|
+
|
101
|
+
xfree(thread_data);
|
102
|
+
}
|
95
103
|
|
96
|
-
VALUE
|
97
|
-
prof_thread_wrap(thread_data_t *thread)
|
104
|
+
VALUE prof_thread_wrap(thread_data_t* thread)
|
98
105
|
{
|
99
106
|
if (thread->object == Qnil)
|
100
107
|
{
|
101
|
-
thread->object = Data_Wrap_Struct(cRpThread, prof_thread_mark,
|
108
|
+
thread->object = Data_Wrap_Struct(cRpThread, prof_thread_mark, prof_thread_ruby_gc_free, thread);
|
102
109
|
}
|
103
110
|
return thread->object;
|
104
111
|
}
|
105
112
|
|
106
|
-
static VALUE
|
107
|
-
prof_thread_allocate(VALUE klass)
|
113
|
+
static VALUE prof_thread_allocate(VALUE klass)
|
108
114
|
{
|
109
115
|
thread_data_t* thread_data = thread_data_create();
|
110
116
|
thread_data->object = prof_thread_wrap(thread_data);
|
111
117
|
return thread_data->object;
|
112
118
|
}
|
113
119
|
|
114
|
-
static thread_data_t*
|
115
|
-
prof_get_thread(VALUE self)
|
120
|
+
static thread_data_t* prof_get_thread(VALUE self)
|
116
121
|
{
|
117
122
|
/* Can't use Data_Get_Struct because that triggers the event hook
|
118
123
|
ending up in endless recursion. */
|
@@ -123,38 +128,33 @@ prof_get_thread(VALUE self)
|
|
123
128
|
return result;
|
124
129
|
}
|
125
130
|
|
126
|
-
|
127
|
-
|
128
|
-
of thread_data_t. */
|
131
|
+
// ====== Thread Table ======
|
132
|
+
// The thread table is hash keyed on ruby fiber_id that stores instances of thread_data_t.
|
129
133
|
|
130
|
-
st_table
|
131
|
-
threads_table_create()
|
134
|
+
st_table* threads_table_create()
|
132
135
|
{
|
133
|
-
return
|
136
|
+
return rb_st_init_numtable();
|
134
137
|
}
|
135
138
|
|
136
|
-
static int
|
137
|
-
thread_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
|
139
|
+
static int thread_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
|
138
140
|
{
|
139
141
|
prof_thread_free((thread_data_t*)value);
|
140
142
|
return ST_CONTINUE;
|
141
143
|
}
|
142
144
|
|
143
|
-
void
|
144
|
-
threads_table_free(st_table *table)
|
145
|
+
void threads_table_free(st_table* table)
|
145
146
|
{
|
146
|
-
|
147
|
-
|
147
|
+
rb_st_foreach(table, thread_table_free_iterator, 0);
|
148
|
+
rb_st_free_table(table);
|
148
149
|
}
|
149
150
|
|
150
|
-
thread_data_t *
|
151
|
-
threads_table_lookup(void *prof, VALUE fiber)
|
151
|
+
thread_data_t* threads_table_lookup(void* prof, VALUE fiber)
|
152
152
|
{
|
153
|
-
prof_profile_t
|
153
|
+
prof_profile_t* profile = prof;
|
154
154
|
thread_data_t* result = NULL;
|
155
155
|
st_data_t val;
|
156
156
|
|
157
|
-
if (
|
157
|
+
if (rb_st_lookup(profile->threads_tbl, fiber, &val))
|
158
158
|
{
|
159
159
|
result = (thread_data_t*)val;
|
160
160
|
}
|
@@ -162,24 +162,23 @@ threads_table_lookup(void *prof, VALUE fiber)
|
|
162
162
|
return result;
|
163
163
|
}
|
164
164
|
|
165
|
-
thread_data_t*
|
166
|
-
threads_table_insert(void *prof, VALUE fiber)
|
165
|
+
thread_data_t* threads_table_insert(void* prof, VALUE fiber)
|
167
166
|
{
|
168
|
-
prof_profile_t
|
169
|
-
thread_data_t
|
167
|
+
prof_profile_t* profile = prof;
|
168
|
+
thread_data_t* result = thread_data_create();
|
170
169
|
VALUE thread = rb_thread_current();
|
171
170
|
|
172
171
|
result->fiber = fiber;
|
173
172
|
result->fiber_id = rb_obj_id(fiber);
|
174
173
|
result->thread_id = rb_obj_id(thread);
|
175
|
-
|
174
|
+
rb_st_insert(profile->threads_tbl, (st_data_t)fiber, (st_data_t)result);
|
176
175
|
|
177
176
|
// Are we tracing this thread?
|
178
|
-
if (profile->include_threads_tbl && !
|
177
|
+
if (profile->include_threads_tbl && !rb_st_lookup(profile->include_threads_tbl, thread, 0))
|
179
178
|
{
|
180
|
-
result->trace= false;
|
179
|
+
result->trace = false;
|
181
180
|
}
|
182
|
-
else if (profile->exclude_threads_tbl &&
|
181
|
+
else if (profile->exclude_threads_tbl && rb_st_lookup(profile->exclude_threads_tbl, thread, 0))
|
183
182
|
{
|
184
183
|
result->trace = false;
|
185
184
|
}
|
@@ -187,25 +186,28 @@ threads_table_insert(void *prof, VALUE fiber)
|
|
187
186
|
{
|
188
187
|
result->trace = true;
|
189
188
|
}
|
190
|
-
|
189
|
+
|
191
190
|
return result;
|
192
191
|
}
|
193
192
|
|
194
|
-
|
195
|
-
switch_thread(void
|
193
|
+
// ====== Profiling Methods ======
|
194
|
+
void switch_thread(void* prof, thread_data_t* thread_data, double measurement)
|
196
195
|
{
|
197
|
-
prof_profile_t
|
196
|
+
prof_profile_t* profile = prof;
|
198
197
|
|
199
198
|
/* Get current frame for this thread */
|
200
|
-
prof_frame_t* frame = thread_data->stack
|
201
|
-
|
202
|
-
|
203
|
-
|
199
|
+
prof_frame_t* frame = prof_frame_current(thread_data->stack);
|
200
|
+
if (frame)
|
201
|
+
{
|
202
|
+
frame->wait_time += measurement - frame->switch_time;
|
203
|
+
frame->switch_time = 0;
|
204
|
+
}
|
205
|
+
|
204
206
|
/* Save on the last thread the time of the context switch
|
205
207
|
and reset this thread's last context switch to 0.*/
|
206
208
|
if (profile->last_thread_data)
|
207
209
|
{
|
208
|
-
prof_frame_t* last_frame = profile->last_thread_data->stack
|
210
|
+
prof_frame_t* last_frame = prof_frame_current(profile->last_thread_data->stack);
|
209
211
|
if (last_frame)
|
210
212
|
last_frame->switch_time = measurement;
|
211
213
|
}
|
@@ -215,10 +217,10 @@ switch_thread(void *prof, thread_data_t *thread_data, double measurement)
|
|
215
217
|
|
216
218
|
int pause_thread(st_data_t key, st_data_t value, st_data_t data)
|
217
219
|
{
|
218
|
-
thread_data_t* thread_data = (thread_data_t
|
220
|
+
thread_data_t* thread_data = (thread_data_t*)value;
|
219
221
|
prof_profile_t* profile = (prof_profile_t*)data;
|
220
222
|
|
221
|
-
prof_frame_t* frame = thread_data->stack
|
223
|
+
prof_frame_t* frame = prof_frame_current(thread_data->stack);
|
222
224
|
prof_frame_pause(frame, profile->measurement_at_pause_resume);
|
223
225
|
|
224
226
|
return ST_CONTINUE;
|
@@ -226,37 +228,33 @@ int pause_thread(st_data_t key, st_data_t value, st_data_t data)
|
|
226
228
|
|
227
229
|
int unpause_thread(st_data_t key, st_data_t value, st_data_t data)
|
228
230
|
{
|
229
|
-
thread_data_t* thread_data = (thread_data_t
|
231
|
+
thread_data_t* thread_data = (thread_data_t*)value;
|
230
232
|
prof_profile_t* profile = (prof_profile_t*)data;
|
231
233
|
|
232
|
-
prof_frame_t* frame = thread_data->stack
|
234
|
+
prof_frame_t* frame = prof_frame_current(thread_data->stack);
|
233
235
|
prof_frame_unpause(frame, profile->measurement_at_pause_resume);
|
234
236
|
|
235
237
|
return ST_CONTINUE;
|
236
238
|
}
|
237
239
|
|
238
|
-
|
239
|
-
collect_methods(st_data_t key, st_data_t value, st_data_t result)
|
240
|
+
// ====== Helper Methods ======
|
241
|
+
static int collect_methods(st_data_t key, st_data_t value, st_data_t result)
|
240
242
|
{
|
241
243
|
/* Called for each method stored in a thread's method table.
|
242
244
|
We want to store the method info information into an array.*/
|
243
|
-
VALUE methods = (VALUE)
|
244
|
-
prof_method_t
|
245
|
-
|
246
|
-
if (!method->excluded)
|
247
|
-
{
|
248
|
-
rb_ary_push(methods, prof_method_wrap(method));
|
249
|
-
}
|
245
|
+
VALUE methods = (VALUE)result;
|
246
|
+
prof_method_t* method = (prof_method_t*)value;
|
247
|
+
rb_ary_push(methods, prof_method_wrap(method));
|
250
248
|
|
251
249
|
return ST_CONTINUE;
|
252
250
|
}
|
253
251
|
|
252
|
+
// ====== RubyProf::Thread ======
|
254
253
|
/* call-seq:
|
255
254
|
id -> number
|
256
255
|
|
257
256
|
Returns the thread id of this thread. */
|
258
|
-
static VALUE
|
259
|
-
prof_thread_id(VALUE self)
|
257
|
+
static VALUE prof_thread_id(VALUE self)
|
260
258
|
{
|
261
259
|
thread_data_t* thread = prof_get_thread(self);
|
262
260
|
return thread->thread_id;
|
@@ -266,57 +264,66 @@ prof_thread_id(VALUE self)
|
|
266
264
|
fiber_id -> number
|
267
265
|
|
268
266
|
Returns the fiber id of this thread. */
|
269
|
-
static VALUE
|
270
|
-
prof_fiber_id(VALUE self)
|
267
|
+
static VALUE prof_fiber_id(VALUE self)
|
271
268
|
{
|
272
269
|
thread_data_t* thread = prof_get_thread(self);
|
273
270
|
return thread->fiber_id;
|
274
271
|
}
|
275
272
|
|
273
|
+
/* call-seq:
|
274
|
+
call_tree -> CallTree
|
275
|
+
|
276
|
+
Returns the root of the call tree. */
|
277
|
+
static VALUE prof_call_tree(VALUE self)
|
278
|
+
{
|
279
|
+
thread_data_t* thread = prof_get_thread(self);
|
280
|
+
return prof_call_tree_wrap(thread->call_tree);
|
281
|
+
}
|
282
|
+
|
276
283
|
/* call-seq:
|
277
284
|
methods -> [RubyProf::MethodInfo]
|
278
285
|
|
279
286
|
Returns an array of methods that were called from this
|
280
287
|
thread during program execution. */
|
281
|
-
static VALUE
|
282
|
-
prof_thread_methods(VALUE self)
|
288
|
+
static VALUE prof_thread_methods(VALUE self)
|
283
289
|
{
|
284
290
|
thread_data_t* thread = prof_get_thread(self);
|
285
291
|
if (thread->methods == Qnil)
|
286
292
|
{
|
287
293
|
thread->methods = rb_ary_new();
|
288
|
-
|
294
|
+
rb_st_foreach(thread->method_table, collect_methods, thread->methods);
|
289
295
|
}
|
290
296
|
return thread->methods;
|
291
297
|
}
|
292
298
|
|
293
299
|
/* :nodoc: */
|
294
|
-
static VALUE
|
295
|
-
prof_thread_dump(VALUE self)
|
300
|
+
static VALUE prof_thread_dump(VALUE self)
|
296
301
|
{
|
297
302
|
thread_data_t* thread_data = DATA_PTR(self);
|
298
303
|
|
299
304
|
VALUE result = rb_hash_new();
|
300
305
|
rb_hash_aset(result, ID2SYM(rb_intern("fiber_id")), thread_data->fiber_id);
|
301
306
|
rb_hash_aset(result, ID2SYM(rb_intern("methods")), prof_thread_methods(self));
|
307
|
+
rb_hash_aset(result, ID2SYM(rb_intern("call_tree")), prof_call_tree(self));
|
302
308
|
|
303
309
|
return result;
|
304
310
|
}
|
305
311
|
|
306
312
|
/* :nodoc: */
|
307
|
-
static VALUE
|
308
|
-
prof_thread_load(VALUE self, VALUE data)
|
313
|
+
static VALUE prof_thread_load(VALUE self, VALUE data)
|
309
314
|
{
|
310
315
|
thread_data_t* thread_data = DATA_PTR(self);
|
311
|
-
|
316
|
+
|
317
|
+
VALUE call_tree = rb_hash_aref(data, ID2SYM(rb_intern("call_tree")));
|
318
|
+
thread_data->call_tree = prof_get_call_tree(call_tree);
|
312
319
|
|
313
320
|
thread_data->fiber_id = rb_hash_aref(data, ID2SYM(rb_intern("fiber_id")));
|
314
|
-
VALUE methods = rb_hash_aref(data, ID2SYM(rb_intern("methods")));
|
315
321
|
|
322
|
+
VALUE methods = rb_hash_aref(data, ID2SYM(rb_intern("methods")));
|
316
323
|
for (int i = 0; i < rb_array_len(methods); i++)
|
317
324
|
{
|
318
325
|
VALUE method = rb_ary_entry(methods, i);
|
319
|
-
prof_method_t
|
326
|
+
prof_method_t* method_data = DATA_PTR(method);
|
320
327
|
method_table_insert(thread_data->method_table, method_data->key, method_data);
|
321
328
|
}
|
322
329
|
|
@@ -330,6 +337,7 @@ void rp_init_thread(void)
|
|
330
337
|
rb_define_alloc_func(cRpThread, prof_thread_allocate);
|
331
338
|
|
332
339
|
rb_define_method(cRpThread, "id", prof_thread_id, 0);
|
340
|
+
rb_define_method(cRpThread, "call_tree", prof_call_tree, 0);
|
333
341
|
rb_define_method(cRpThread, "fiber_id", prof_fiber_id, 0);
|
334
342
|
rb_define_method(cRpThread, "methods", prof_thread_methods, 0);
|
335
343
|
rb_define_method(cRpThread, "_dump_data", prof_thread_dump, 0);
|