ruby-prof 1.4.3 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +59 -9
  3. data/{README.rdoc → README.md} +2 -2
  4. data/Rakefile +4 -4
  5. data/bin/ruby-prof +100 -87
  6. data/ext/ruby_prof/rp_allocation.c +140 -85
  7. data/ext/ruby_prof/rp_allocation.h +8 -6
  8. data/ext/ruby_prof/rp_call_tree.c +502 -369
  9. data/ext/ruby_prof/rp_call_tree.h +47 -43
  10. data/ext/ruby_prof/rp_call_trees.c +16 -8
  11. data/ext/ruby_prof/rp_measure_allocations.c +10 -13
  12. data/ext/ruby_prof/rp_measure_memory.c +8 -4
  13. data/ext/ruby_prof/rp_measure_process_time.c +7 -6
  14. data/ext/ruby_prof/rp_measurement.c +147 -20
  15. data/ext/ruby_prof/rp_measurement.h +4 -1
  16. data/ext/ruby_prof/rp_method.c +142 -83
  17. data/ext/ruby_prof/rp_method.h +63 -62
  18. data/ext/ruby_prof/rp_profile.c +933 -900
  19. data/ext/ruby_prof/rp_profile.h +1 -0
  20. data/ext/ruby_prof/rp_thread.c +433 -362
  21. data/ext/ruby_prof/rp_thread.h +39 -39
  22. data/ext/ruby_prof/ruby_prof.c +0 -2
  23. data/ext/ruby_prof/ruby_prof.h +8 -0
  24. data/ext/ruby_prof/vc/ruby_prof.vcxproj +11 -8
  25. data/lib/ruby-prof/assets/call_stack_printer.html.erb +2 -1
  26. data/lib/ruby-prof/compatibility.rb +14 -0
  27. data/lib/ruby-prof/method_info.rb +8 -1
  28. data/lib/ruby-prof/printers/abstract_printer.rb +2 -1
  29. data/lib/ruby-prof/printers/call_tree_printer.rb +4 -10
  30. data/lib/ruby-prof/printers/graph_html_printer.rb +1 -1
  31. data/lib/ruby-prof/printers/multi_printer.rb +17 -17
  32. data/lib/ruby-prof/profile.rb +70 -37
  33. data/lib/ruby-prof/rack.rb +31 -21
  34. data/lib/ruby-prof/version.rb +1 -1
  35. data/lib/ruby-prof.rb +1 -1
  36. data/ruby-prof.gemspec +2 -3
  37. data/test/abstract_printer_test.rb +1 -0
  38. data/test/alias_test.rb +97 -106
  39. data/test/call_tree_builder.rb +126 -0
  40. data/test/call_tree_test.rb +94 -0
  41. data/test/call_tree_visitor_test.rb +1 -6
  42. data/test/call_trees_test.rb +6 -6
  43. data/test/{basic_test.rb → compatibility_test.rb} +8 -2
  44. data/test/duplicate_names_test.rb +5 -5
  45. data/test/dynamic_method_test.rb +24 -15
  46. data/test/enumerable_test.rb +1 -1
  47. data/test/exceptions_test.rb +2 -2
  48. data/test/exclude_methods_test.rb +3 -8
  49. data/test/exclude_threads_test.rb +4 -9
  50. data/test/fiber_test.rb +74 -8
  51. data/test/gc_test.rb +11 -9
  52. data/test/inverse_call_tree_test.rb +33 -34
  53. data/test/line_number_test.rb +37 -61
  54. data/test/marshal_test.rb +16 -3
  55. data/test/measure_allocations.rb +1 -5
  56. data/test/measure_allocations_test.rb +642 -357
  57. data/test/{measure_memory_trace_test.rb → measure_memory_test.rb} +180 -616
  58. data/test/measure_process_time_test.rb +1566 -741
  59. data/test/measure_wall_time_test.rb +179 -193
  60. data/test/measurement_test.rb +82 -0
  61. data/test/merge_test.rb +146 -0
  62. data/test/method_info_test.rb +95 -0
  63. data/test/multi_printer_test.rb +0 -5
  64. data/test/no_method_class_test.rb +1 -1
  65. data/test/pause_resume_test.rb +12 -16
  66. data/test/printer_call_stack_test.rb +2 -2
  67. data/test/printer_call_tree_test.rb +4 -4
  68. data/test/printer_flat_test.rb +1 -1
  69. data/test/printer_graph_html_test.rb +2 -2
  70. data/test/printer_graph_test.rb +2 -2
  71. data/test/printers_test.rb +14 -20
  72. data/test/printing_recursive_graph_test.rb +2 -2
  73. data/test/profile_test.rb +85 -0
  74. data/test/recursive_test.rb +374 -155
  75. data/test/scheduler.rb +363 -0
  76. data/test/singleton_test.rb +1 -1
  77. data/test/stack_printer_test.rb +5 -8
  78. data/test/start_stop_test.rb +11 -14
  79. data/test/test_helper.rb +11 -8
  80. data/test/thread_test.rb +106 -15
  81. data/test/unique_call_path_test.rb +28 -12
  82. data/test/yarv_test.rb +11 -7
  83. metadata +17 -29
  84. data/ext/ruby_prof/rp_aggregate_call_tree.c +0 -59
  85. data/ext/ruby_prof/rp_aggregate_call_tree.h +0 -13
  86. data/test/measure_allocations_trace_test.rb +0 -375
  87. data/test/temp.rb +0 -20
@@ -1,362 +1,433 @@
1
- /* Copyright (C) 2005-2013 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
- Please see the LICENSE file for copyright and distribution information */
3
-
4
- /* Document-class: RubyProf::Thread
5
-
6
- The Thread class contains profile results for a single fiber (note a Ruby thread can run multiple fibers).
7
- You cannot create an instance of RubyProf::Thread, instead you access it from a RubyProf::Profile object.
8
-
9
- profile = RubyProf::Profile.profile do
10
- ...
11
- end
12
-
13
- profile.threads.each do |thread|
14
- thread.root_methods.sort.each do |method|
15
- puts method.total_time
16
- end
17
- end */
18
-
19
- #include "rp_thread.h"
20
- #include "rp_profile.h"
21
-
22
- VALUE cRpThread;
23
-
24
- // ====== thread_data_t ======
25
- thread_data_t* thread_data_create(void)
26
- {
27
- thread_data_t* result = ALLOC(thread_data_t);
28
- result->stack = prof_stack_create();
29
- result->method_table = method_table_create();
30
- result->call_tree = NULL;
31
- result->object = Qnil;
32
- result->methods = Qnil;
33
- result->fiber_id = Qnil;
34
- result->thread_id = Qnil;
35
- result->trace = true;
36
- result->fiber = Qnil;
37
- return result;
38
- }
39
-
40
- static int mark_methods(st_data_t key, st_data_t value, st_data_t result)
41
- {
42
- prof_method_t* method = (prof_method_t*)value;
43
- prof_method_mark(method);
44
- return ST_CONTINUE;
45
- }
46
-
47
- size_t prof_thread_size(const void* data)
48
- {
49
- return sizeof(thread_data_t);
50
- }
51
-
52
- void prof_thread_mark(void* data)
53
- {
54
- if (!data)
55
- return;
56
-
57
- thread_data_t* thread = (thread_data_t*)data;
58
-
59
- if (thread->object != Qnil)
60
- rb_gc_mark(thread->object);
61
-
62
- rb_gc_mark(thread->fiber);
63
-
64
- if (thread->methods != Qnil)
65
- rb_gc_mark(thread->methods);
66
-
67
- if (thread->fiber_id != Qnil)
68
- rb_gc_mark(thread->fiber_id);
69
-
70
- if (thread->thread_id != Qnil)
71
- rb_gc_mark(thread->thread_id);
72
-
73
- if (thread->call_tree)
74
- prof_call_tree_mark(thread->call_tree);
75
-
76
- rb_st_foreach(thread->method_table, mark_methods, 0);
77
- }
78
-
79
- void prof_thread_ruby_gc_free(void* data)
80
- {
81
- if (data)
82
- {
83
- thread_data_t* thread_data = (thread_data_t*)data;
84
- thread_data->object = Qnil;
85
- }
86
- }
87
-
88
- static void prof_thread_free(thread_data_t* thread_data)
89
- {
90
- /* Has this method object been accessed by Ruby? If
91
- yes then set its data to nil to avoid a segmentation fault on the next mark and sweep. */
92
- if (thread_data->object != Qnil)
93
- {
94
- RTYPEDDATA(thread_data->object)->data = NULL;
95
- thread_data->object = Qnil;
96
- }
97
-
98
- method_table_free(thread_data->method_table);
99
-
100
- if (thread_data->call_tree)
101
- prof_call_tree_free(thread_data->call_tree);
102
-
103
- prof_stack_free(thread_data->stack);
104
-
105
- xfree(thread_data);
106
- }
107
-
108
- static const rb_data_type_t thread_type =
109
- {
110
- .wrap_struct_name = "ThreadInfo",
111
- .function =
112
- {
113
- .dmark = prof_thread_mark,
114
- .dfree = prof_thread_ruby_gc_free,
115
- .dsize = prof_thread_size,
116
- },
117
- .data = NULL,
118
- .flags = RUBY_TYPED_FREE_IMMEDIATELY
119
- };
120
-
121
- VALUE prof_thread_wrap(thread_data_t* thread)
122
- {
123
- if (thread->object == Qnil)
124
- {
125
- thread->object = TypedData_Wrap_Struct(cRpThread, &thread_type, thread);
126
- }
127
- return thread->object;
128
- }
129
-
130
- static VALUE prof_thread_allocate(VALUE klass)
131
- {
132
- thread_data_t* thread_data = thread_data_create();
133
- thread_data->object = prof_thread_wrap(thread_data);
134
- return thread_data->object;
135
- }
136
-
137
- thread_data_t* prof_get_thread(VALUE self)
138
- {
139
- /* Can't use Data_Get_Struct because that triggers the event hook
140
- ending up in endless recursion. */
141
- thread_data_t* result = RTYPEDDATA_DATA(self);
142
- if (!result)
143
- rb_raise(rb_eRuntimeError, "This RubyProf::Thread instance has already been freed, likely because its profile has been freed.");
144
-
145
- return result;
146
- }
147
-
148
- // ====== Thread Table ======
149
- // The thread table is hash keyed on ruby fiber_id that stores instances of thread_data_t.
150
-
151
- st_table* threads_table_create()
152
- {
153
- return rb_st_init_numtable();
154
- }
155
-
156
- static int thread_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
157
- {
158
- prof_thread_free((thread_data_t*)value);
159
- return ST_CONTINUE;
160
- }
161
-
162
- void threads_table_free(st_table* table)
163
- {
164
- rb_st_foreach(table, thread_table_free_iterator, 0);
165
- rb_st_free_table(table);
166
- }
167
-
168
- thread_data_t* threads_table_lookup(void* prof, VALUE fiber)
169
- {
170
- prof_profile_t* profile = prof;
171
- thread_data_t* result = NULL;
172
- st_data_t val;
173
-
174
- if (rb_st_lookup(profile->threads_tbl, fiber, &val))
175
- {
176
- result = (thread_data_t*)val;
177
- }
178
-
179
- return result;
180
- }
181
-
182
- thread_data_t* threads_table_insert(void* prof, VALUE fiber)
183
- {
184
- prof_profile_t* profile = prof;
185
- thread_data_t* result = thread_data_create();
186
- VALUE thread = rb_thread_current();
187
-
188
- result->fiber = fiber;
189
- result->fiber_id = rb_obj_id(fiber);
190
- result->thread_id = rb_obj_id(thread);
191
- rb_st_insert(profile->threads_tbl, (st_data_t)fiber, (st_data_t)result);
192
-
193
- // Are we tracing this thread?
194
- if (profile->include_threads_tbl && !rb_st_lookup(profile->include_threads_tbl, thread, 0))
195
- {
196
- result->trace = false;
197
- }
198
- else if (profile->exclude_threads_tbl && rb_st_lookup(profile->exclude_threads_tbl, thread, 0))
199
- {
200
- result->trace = false;
201
- }
202
- else
203
- {
204
- result->trace = true;
205
- }
206
-
207
- return result;
208
- }
209
-
210
- // ====== Profiling Methods ======
211
- void switch_thread(void* prof, thread_data_t* thread_data, double measurement)
212
- {
213
- prof_profile_t* profile = prof;
214
-
215
- /* Get current frame for this thread */
216
- prof_frame_t* frame = prof_frame_current(thread_data->stack);
217
- if (frame)
218
- {
219
- frame->wait_time += measurement - frame->switch_time;
220
- frame->switch_time = 0;
221
- }
222
-
223
- /* Save on the last thread the time of the context switch
224
- and reset this thread's last context switch to 0.*/
225
- if (profile->last_thread_data)
226
- {
227
- prof_frame_t* last_frame = prof_frame_current(profile->last_thread_data->stack);
228
- if (last_frame)
229
- last_frame->switch_time = measurement;
230
- }
231
-
232
- profile->last_thread_data = thread_data;
233
- }
234
-
235
- int pause_thread(st_data_t key, st_data_t value, st_data_t data)
236
- {
237
- thread_data_t* thread_data = (thread_data_t*)value;
238
- prof_profile_t* profile = (prof_profile_t*)data;
239
-
240
- prof_frame_t* frame = prof_frame_current(thread_data->stack);
241
- prof_frame_pause(frame, profile->measurement_at_pause_resume);
242
-
243
- return ST_CONTINUE;
244
- }
245
-
246
- int unpause_thread(st_data_t key, st_data_t value, st_data_t data)
247
- {
248
- thread_data_t* thread_data = (thread_data_t*)value;
249
- prof_profile_t* profile = (prof_profile_t*)data;
250
-
251
- prof_frame_t* frame = prof_frame_current(thread_data->stack);
252
- prof_frame_unpause(frame, profile->measurement_at_pause_resume);
253
-
254
- return ST_CONTINUE;
255
- }
256
-
257
- // ====== Helper Methods ======
258
- static int collect_methods(st_data_t key, st_data_t value, st_data_t result)
259
- {
260
- /* Called for each method stored in a thread's method table.
261
- We want to store the method info information into an array.*/
262
- VALUE methods = (VALUE)result;
263
- prof_method_t* method = (prof_method_t*)value;
264
- rb_ary_push(methods, prof_method_wrap(method));
265
-
266
- return ST_CONTINUE;
267
- }
268
-
269
- // ====== RubyProf::Thread ======
270
- /* call-seq:
271
- id -> number
272
-
273
- Returns the thread id of this thread. */
274
- static VALUE prof_thread_id(VALUE self)
275
- {
276
- thread_data_t* thread = prof_get_thread(self);
277
- return thread->thread_id;
278
- }
279
-
280
- /* call-seq:
281
- fiber_id -> number
282
-
283
- Returns the fiber id of this thread. */
284
- static VALUE prof_fiber_id(VALUE self)
285
- {
286
- thread_data_t* thread = prof_get_thread(self);
287
- return thread->fiber_id;
288
- }
289
-
290
- /* call-seq:
291
- call_tree -> CallTree
292
-
293
- Returns the root of the call tree. */
294
- static VALUE prof_call_tree(VALUE self)
295
- {
296
- thread_data_t* thread = prof_get_thread(self);
297
- return prof_call_tree_wrap(thread->call_tree);
298
- }
299
-
300
- /* call-seq:
301
- methods -> [RubyProf::MethodInfo]
302
-
303
- Returns an array of methods that were called from this
304
- thread during program execution. */
305
- static VALUE prof_thread_methods(VALUE self)
306
- {
307
- thread_data_t* thread = prof_get_thread(self);
308
- if (thread->methods == Qnil)
309
- {
310
- thread->methods = rb_ary_new();
311
- rb_st_foreach(thread->method_table, collect_methods, thread->methods);
312
- }
313
- return thread->methods;
314
- }
315
-
316
- /* :nodoc: */
317
- static VALUE prof_thread_dump(VALUE self)
318
- {
319
- thread_data_t* thread_data = RTYPEDDATA_DATA(self);
320
-
321
- VALUE result = rb_hash_new();
322
- rb_hash_aset(result, ID2SYM(rb_intern("fiber_id")), thread_data->fiber_id);
323
- rb_hash_aset(result, ID2SYM(rb_intern("methods")), prof_thread_methods(self));
324
- rb_hash_aset(result, ID2SYM(rb_intern("call_tree")), prof_call_tree(self));
325
-
326
- return result;
327
- }
328
-
329
- /* :nodoc: */
330
- static VALUE prof_thread_load(VALUE self, VALUE data)
331
- {
332
- thread_data_t* thread_data = RTYPEDDATA_DATA(self);
333
-
334
- VALUE call_tree = rb_hash_aref(data, ID2SYM(rb_intern("call_tree")));
335
- thread_data->call_tree = prof_get_call_tree(call_tree);
336
-
337
- thread_data->fiber_id = rb_hash_aref(data, ID2SYM(rb_intern("fiber_id")));
338
-
339
- VALUE methods = rb_hash_aref(data, ID2SYM(rb_intern("methods")));
340
- for (int i = 0; i < rb_array_len(methods); i++)
341
- {
342
- VALUE method = rb_ary_entry(methods, i);
343
- prof_method_t* method_data = RTYPEDDATA_DATA(method);
344
- method_table_insert(thread_data->method_table, method_data->key, method_data);
345
- }
346
-
347
- return data;
348
- }
349
-
350
- void rp_init_thread(void)
351
- {
352
- cRpThread = rb_define_class_under(mProf, "Thread", rb_cObject);
353
- rb_undef_method(CLASS_OF(cRpThread), "new");
354
- rb_define_alloc_func(cRpThread, prof_thread_allocate);
355
-
356
- rb_define_method(cRpThread, "id", prof_thread_id, 0);
357
- rb_define_method(cRpThread, "call_tree", prof_call_tree, 0);
358
- rb_define_method(cRpThread, "fiber_id", prof_fiber_id, 0);
359
- rb_define_method(cRpThread, "methods", prof_thread_methods, 0);
360
- rb_define_method(cRpThread, "_dump_data", prof_thread_dump, 0);
361
- rb_define_method(cRpThread, "_load_data", prof_thread_load, 1);
362
- }
1
+ /* Copyright (C) 2005-2013 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ /* Document-class: RubyProf::Thread
5
+
6
+ The Thread class contains profile results for a single fiber (note a Ruby thread can run multiple fibers).
7
+ You cannot create an instance of RubyProf::Thread, instead you access it from a RubyProf::Profile object.
8
+
9
+ profile = RubyProf::Profile.profile do
10
+ ...
11
+ end
12
+
13
+ profile.threads.each do |thread|
14
+ thread.root_methods.sort.each do |method|
15
+ puts method.total_time
16
+ end
17
+ end */
18
+
19
+ #include "rp_thread.h"
20
+ #include "rp_profile.h"
21
+
22
+ VALUE cRpThread;
23
+
24
+ // ====== thread_data_t ======
25
+ thread_data_t* thread_data_create(void)
26
+ {
27
+ thread_data_t* result = ALLOC(thread_data_t);
28
+ result->owner = OWNER_C;
29
+ result->stack = prof_stack_create();
30
+ result->method_table = method_table_create();
31
+ result->call_tree = NULL;
32
+ result->object = Qnil;
33
+ result->methods = Qnil;
34
+ result->fiber_id = Qnil;
35
+ result->thread_id = Qnil;
36
+ result->trace = true;
37
+ result->fiber = Qnil;
38
+ return result;
39
+ }
40
+
41
+ static int mark_methods(st_data_t key, st_data_t value, st_data_t result)
42
+ {
43
+ prof_method_t* method = (prof_method_t*)value;
44
+ prof_method_mark(method);
45
+ return ST_CONTINUE;
46
+ }
47
+
48
+ size_t prof_thread_size(const void* data)
49
+ {
50
+ return sizeof(thread_data_t);
51
+ }
52
+
53
+ void prof_thread_mark(void* data)
54
+ {
55
+ if (!data)
56
+ return;
57
+
58
+ thread_data_t* thread = (thread_data_t*)data;
59
+
60
+ if (thread->object != Qnil)
61
+ rb_gc_mark_movable(thread->object);
62
+
63
+ rb_gc_mark(thread->fiber);
64
+
65
+ if (thread->methods != Qnil)
66
+ rb_gc_mark_movable(thread->methods);
67
+
68
+ if (thread->fiber_id != Qnil)
69
+ rb_gc_mark_movable(thread->fiber_id);
70
+
71
+ if (thread->thread_id != Qnil)
72
+ rb_gc_mark_movable(thread->thread_id);
73
+
74
+ if (thread->call_tree)
75
+ prof_call_tree_mark(thread->call_tree);
76
+
77
+ rb_st_foreach(thread->method_table, mark_methods, 0);
78
+ }
79
+
80
+ void prof_thread_compact(void* data)
81
+ {
82
+ thread_data_t* thread = (thread_data_t*)data;
83
+ thread->object = rb_gc_location(thread->object);
84
+ thread->methods = rb_gc_location(thread->methods);
85
+ thread->fiber_id = rb_gc_location(thread->fiber_id);
86
+ thread->thread_id = rb_gc_location(thread->thread_id);
87
+ }
88
+
89
+ static void prof_thread_free(thread_data_t* thread_data)
90
+ {
91
+ /* Has this method object been accessed by Ruby? If
92
+ yes then set its data to nil to avoid a segmentation fault on the next mark and sweep. */
93
+ if (thread_data->object != Qnil)
94
+ {
95
+ RTYPEDDATA(thread_data->object)->data = NULL;
96
+ thread_data->object = Qnil;
97
+ }
98
+
99
+ method_table_free(thread_data->method_table);
100
+
101
+ if (thread_data->call_tree)
102
+ prof_call_tree_free(thread_data->call_tree);
103
+
104
+ prof_stack_free(thread_data->stack);
105
+
106
+ xfree(thread_data);
107
+ }
108
+
109
+ void prof_thread_ruby_gc_free(void* data)
110
+ {
111
+ thread_data_t* thread_data = (thread_data_t*)data;
112
+
113
+ if (!thread_data)
114
+ {
115
+ // Object has already been freed by C code
116
+ return;
117
+ }
118
+ else if (thread_data->owner == OWNER_RUBY)
119
+ {
120
+ // Ruby owns this object, we need to free the underlying C struct
121
+ prof_thread_free(thread_data);
122
+ }
123
+ else
124
+ {
125
+ // The Ruby object is being freed, but not the underlying C structure. So unlink the two.
126
+ thread_data->object = Qnil;
127
+ }
128
+ }
129
+
130
+ static const rb_data_type_t thread_type =
131
+ {
132
+ .wrap_struct_name = "ThreadInfo",
133
+ .function =
134
+ {
135
+ .dmark = prof_thread_mark,
136
+ .dfree = prof_thread_ruby_gc_free,
137
+ .dsize = prof_thread_size,
138
+ .dcompact = prof_thread_compact
139
+ },
140
+ .data = NULL,
141
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
142
+ };
143
+
144
+ VALUE prof_thread_wrap(thread_data_t* thread)
145
+ {
146
+ if (thread->object == Qnil)
147
+ {
148
+ thread->object = TypedData_Wrap_Struct(cRpThread, &thread_type, thread);
149
+ }
150
+ return thread->object;
151
+ }
152
+
153
+ static VALUE prof_thread_allocate(VALUE klass)
154
+ {
155
+ thread_data_t* thread_data = thread_data_create();
156
+ thread_data->owner = OWNER_RUBY;
157
+ thread_data->object = prof_thread_wrap(thread_data);
158
+ return thread_data->object;
159
+ }
160
+
161
+ thread_data_t* prof_get_thread(VALUE self)
162
+ {
163
+ /* Can't use Data_Get_Struct because that triggers the event hook
164
+ ending up in endless recursion. */
165
+ thread_data_t* result = RTYPEDDATA_DATA(self);
166
+ if (!result)
167
+ rb_raise(rb_eRuntimeError, "This RubyProf::Thread instance has already been freed, likely because its profile has been freed.");
168
+
169
+ return result;
170
+ }
171
+
172
+ // ====== Thread Table ======
173
+ // The thread table is hash keyed on ruby fiber_id that stores instances of thread_data_t.
174
+
175
+ st_table* threads_table_create()
176
+ {
177
+ return rb_st_init_numtable();
178
+ }
179
+
180
+ static int thread_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
181
+ {
182
+ prof_thread_free((thread_data_t*)value);
183
+ return ST_CONTINUE;
184
+ }
185
+
186
+ void threads_table_free(st_table* table)
187
+ {
188
+ rb_st_foreach(table, thread_table_free_iterator, 0);
189
+ rb_st_free_table(table);
190
+ }
191
+
192
+ thread_data_t* threads_table_lookup(void* prof, VALUE fiber)
193
+ {
194
+ prof_profile_t* profile = prof;
195
+ thread_data_t* result = NULL;
196
+ st_data_t val;
197
+
198
+ VALUE fiber_id = rb_obj_id(fiber);
199
+ if (rb_st_lookup(profile->threads_tbl, fiber_id, &val))
200
+ {
201
+ result = (thread_data_t*)val;
202
+ }
203
+
204
+ return result;
205
+ }
206
+
207
+ thread_data_t* threads_table_insert(void* prof, VALUE fiber)
208
+ {
209
+ prof_profile_t* profile = prof;
210
+ thread_data_t* result = thread_data_create();
211
+ VALUE thread = rb_thread_current();
212
+
213
+ result->fiber = fiber;
214
+ result->fiber_id = rb_obj_id(fiber);
215
+ result->thread_id = rb_obj_id(thread);
216
+ rb_st_insert(profile->threads_tbl, (st_data_t)result->fiber_id, (st_data_t)result);
217
+
218
+ // Are we tracing this thread?
219
+ if (profile->include_threads_tbl && !rb_st_lookup(profile->include_threads_tbl, thread, 0))
220
+ {
221
+ result->trace = false;
222
+ }
223
+ else if (profile->exclude_threads_tbl && rb_st_lookup(profile->exclude_threads_tbl, thread, 0))
224
+ {
225
+ result->trace = false;
226
+ }
227
+ else
228
+ {
229
+ result->trace = true;
230
+ }
231
+
232
+ return result;
233
+ }
234
+
235
+ // ====== Profiling Methods ======
236
+ void switch_thread(void* prof, thread_data_t* thread_data, double measurement)
237
+ {
238
+ prof_profile_t* profile = prof;
239
+
240
+ /* Get current frame for this thread */
241
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
242
+ if (frame)
243
+ {
244
+ frame->wait_time += measurement - frame->switch_time;
245
+ frame->switch_time = 0;
246
+ }
247
+
248
+ /* Save on the last thread the time of the context switch
249
+ and reset this thread's last context switch to 0.*/
250
+ if (profile->last_thread_data)
251
+ {
252
+ prof_frame_t* last_frame = prof_frame_current(profile->last_thread_data->stack);
253
+ if (last_frame)
254
+ last_frame->switch_time = measurement;
255
+ }
256
+
257
+ profile->last_thread_data = thread_data;
258
+ }
259
+
260
+ int pause_thread(st_data_t key, st_data_t value, st_data_t data)
261
+ {
262
+ thread_data_t* thread_data = (thread_data_t*)value;
263
+ prof_profile_t* profile = (prof_profile_t*)data;
264
+
265
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
266
+ prof_frame_pause(frame, profile->measurement_at_pause_resume);
267
+
268
+ return ST_CONTINUE;
269
+ }
270
+
271
+ int unpause_thread(st_data_t key, st_data_t value, st_data_t data)
272
+ {
273
+ thread_data_t* thread_data = (thread_data_t*)value;
274
+ prof_profile_t* profile = (prof_profile_t*)data;
275
+
276
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
277
+ prof_frame_unpause(frame, profile->measurement_at_pause_resume);
278
+
279
+ return ST_CONTINUE;
280
+ }
281
+
282
+ // ====== Helper Methods ======
283
+ static int collect_methods(st_data_t key, st_data_t value, st_data_t result)
284
+ {
285
+ /* Called for each method stored in a thread's method table.
286
+ We want to store the method info information into an array.*/
287
+ VALUE methods = (VALUE)result;
288
+ prof_method_t* method = (prof_method_t*)value;
289
+ rb_ary_push(methods, prof_method_wrap(method));
290
+
291
+ return ST_CONTINUE;
292
+ }
293
+
294
+ // ====== RubyProf::Thread ======
295
+ /* call-seq:
296
+ new(call_tree, thread, fiber) -> thread
297
+
298
+ Creates a new RubyProf thread instance. +call_tree+ is the root call_tree instance,
299
+ +thread+ is a reference to a Ruby thread and +fiber+ is a reference to a Ruby fiber.*/
300
+ static VALUE prof_thread_initialize(VALUE self, VALUE call_tree, VALUE thread, VALUE fiber)
301
+ {
302
+ thread_data_t* thread_ptr = prof_get_thread(self);
303
+
304
+ // This call tree must now be managed by C
305
+ thread_ptr->call_tree = prof_get_call_tree(call_tree);
306
+ thread_ptr->call_tree->owner = OWNER_C;
307
+
308
+ thread_ptr->fiber = fiber;
309
+ thread_ptr->fiber_id = rb_obj_id(fiber);
310
+ thread_ptr->thread_id = rb_obj_id(thread);
311
+
312
+ // Add methods from call trees into thread methods table
313
+ VALUE methods = prof_call_tree_methods(thread_ptr->call_tree);
314
+ for (int i = 0; i < rb_array_len(methods); i++)
315
+ {
316
+ VALUE method = rb_ary_entry(methods, i);
317
+ prof_method_t* method_ptr = prof_get_method(method);
318
+ method_table_insert(thread_ptr->method_table, method_ptr->key, method_ptr);
319
+ }
320
+
321
+ return self;
322
+ }
323
+
324
+ /* call-seq:
325
+ id -> number
326
+
327
+ Returns the thread id of this thread. */
328
+ static VALUE prof_thread_id(VALUE self)
329
+ {
330
+ thread_data_t* thread = prof_get_thread(self);
331
+ return thread->thread_id;
332
+ }
333
+
334
+ /* call-seq:
335
+ fiber_id -> number
336
+
337
+ Returns the fiber id of this thread. */
338
+ static VALUE prof_fiber_id(VALUE self)
339
+ {
340
+ thread_data_t* thread = prof_get_thread(self);
341
+ return thread->fiber_id;
342
+ }
343
+
344
+ /* call-seq:
345
+ call_tree -> CallTree
346
+
347
+ Returns the root call tree. */
348
+ static VALUE prof_call_tree(VALUE self)
349
+ {
350
+ thread_data_t* thread = prof_get_thread(self);
351
+ return prof_call_tree_wrap(thread->call_tree);
352
+ }
353
+
354
+ /* call-seq:
355
+ methods -> [RubyProf::MethodInfo]
356
+
357
+ Returns an array of methods that were called from this
358
+ thread during program execution. */
359
+ static VALUE prof_thread_methods(VALUE self)
360
+ {
361
+ thread_data_t* thread = prof_get_thread(self);
362
+ if (thread->methods == Qnil)
363
+ {
364
+ thread->methods = rb_ary_new();
365
+ rb_st_foreach(thread->method_table, collect_methods, thread->methods);
366
+ }
367
+ return thread->methods;
368
+ }
369
+
370
+ static VALUE prof_thread_merge(VALUE self, VALUE other)
371
+ {
372
+ thread_data_t* self_ptr = prof_get_thread(self);
373
+ thread_data_t* other_ptr = prof_get_thread(other);
374
+ prof_method_table_merge(self_ptr->method_table, other_ptr->method_table);
375
+ prof_call_tree_merge_internal(self_ptr->call_tree, other_ptr->call_tree, self_ptr->method_table);
376
+
377
+ // Reset method cache since it just changed
378
+ self_ptr->methods = Qnil;
379
+
380
+ return other;
381
+ }
382
+
383
+ /* :nodoc: */
384
+ static VALUE prof_thread_dump(VALUE self)
385
+ {
386
+ thread_data_t* thread_data = RTYPEDDATA_DATA(self);
387
+
388
+ VALUE result = rb_hash_new();
389
+ rb_hash_aset(result, ID2SYM(rb_intern("owner")), INT2FIX(thread_data->owner));
390
+ rb_hash_aset(result, ID2SYM(rb_intern("fiber_id")), thread_data->fiber_id);
391
+ rb_hash_aset(result, ID2SYM(rb_intern("methods")), prof_thread_methods(self));
392
+ rb_hash_aset(result, ID2SYM(rb_intern("call_tree")), prof_call_tree(self));
393
+
394
+ return result;
395
+ }
396
+
397
+ /* :nodoc: */
398
+ static VALUE prof_thread_load(VALUE self, VALUE data)
399
+ {
400
+ thread_data_t* thread_data = RTYPEDDATA_DATA(self);
401
+
402
+ thread_data->owner = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("owner"))));
403
+
404
+ VALUE call_tree = rb_hash_aref(data, ID2SYM(rb_intern("call_tree")));
405
+ thread_data->call_tree = prof_get_call_tree(call_tree);
406
+
407
+ thread_data->fiber_id = rb_hash_aref(data, ID2SYM(rb_intern("fiber_id")));
408
+
409
+ VALUE methods = rb_hash_aref(data, ID2SYM(rb_intern("methods")));
410
+ for (int i = 0; i < rb_array_len(methods); i++)
411
+ {
412
+ VALUE method = rb_ary_entry(methods, i);
413
+ prof_method_t* method_data = RTYPEDDATA_DATA(method);
414
+ method_table_insert(thread_data->method_table, method_data->key, method_data);
415
+ }
416
+
417
+ return data;
418
+ }
419
+
420
+ void rp_init_thread(void)
421
+ {
422
+ cRpThread = rb_define_class_under(mProf, "Thread", rb_cObject);
423
+ rb_define_alloc_func(cRpThread, prof_thread_allocate);
424
+ rb_define_method(cRpThread, "initialize", prof_thread_initialize, 3);
425
+
426
+ rb_define_method(cRpThread, "id", prof_thread_id, 0);
427
+ rb_define_method(cRpThread, "call_tree", prof_call_tree, 0);
428
+ rb_define_method(cRpThread, "fiber_id", prof_fiber_id, 0);
429
+ rb_define_method(cRpThread, "methods", prof_thread_methods, 0);
430
+ rb_define_method(cRpThread, "merge!", prof_thread_merge, 1);
431
+ rb_define_method(cRpThread, "_dump_data", prof_thread_dump, 0);
432
+ rb_define_method(cRpThread, "_load_data", prof_thread_load, 1);
433
+ }