ruby-prof 0.13.1 → 1.4.2

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.
Files changed (209) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +579 -371
  3. data/LICENSE +24 -23
  4. data/README.rdoc +5 -433
  5. data/Rakefile +98 -110
  6. data/bin/ruby-prof +328 -329
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/ext/ruby_prof/extconf.rb +16 -59
  9. data/ext/ruby_prof/rp_aggregate_call_tree.c +59 -0
  10. data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
  11. data/ext/ruby_prof/rp_allocation.c +287 -0
  12. data/ext/ruby_prof/rp_allocation.h +31 -0
  13. data/ext/ruby_prof/rp_call_tree.c +369 -0
  14. data/ext/ruby_prof/rp_call_tree.h +43 -0
  15. data/ext/ruby_prof/rp_call_trees.c +288 -0
  16. data/ext/ruby_prof/rp_call_trees.h +28 -0
  17. data/ext/ruby_prof/rp_measure_allocations.c +50 -65
  18. data/ext/ruby_prof/rp_measure_memory.c +42 -73
  19. data/ext/ruby_prof/rp_measure_process_time.c +65 -71
  20. data/ext/ruby_prof/rp_measure_wall_time.c +64 -42
  21. data/ext/ruby_prof/rp_measurement.c +237 -0
  22. data/ext/ruby_prof/rp_measurement.h +50 -0
  23. data/ext/ruby_prof/rp_method.c +491 -420
  24. data/ext/ruby_prof/rp_method.h +62 -57
  25. data/ext/ruby_prof/rp_profile.c +908 -0
  26. data/ext/ruby_prof/rp_profile.h +35 -0
  27. data/ext/ruby_prof/rp_stack.c +212 -128
  28. data/ext/ruby_prof/rp_stack.h +53 -51
  29. data/ext/ruby_prof/rp_thread.c +362 -268
  30. data/ext/ruby_prof/rp_thread.h +39 -27
  31. data/ext/ruby_prof/ruby_prof.c +52 -695
  32. data/ext/ruby_prof/ruby_prof.h +26 -55
  33. data/ext/ruby_prof/vc/ruby_prof.sln +28 -21
  34. data/ext/ruby_prof/vc/{ruby_prof_20.vcxproj → ruby_prof.vcxproj} +56 -8
  35. data/lib/ruby-prof.rb +52 -67
  36. data/lib/ruby-prof/assets/call_stack_printer.html.erb +710 -0
  37. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  38. data/lib/ruby-prof/assets/graph_printer.html.erb +355 -0
  39. data/lib/ruby-prof/call_tree.rb +57 -0
  40. data/lib/ruby-prof/call_tree_visitor.rb +36 -0
  41. data/lib/ruby-prof/compatibility.rb +99 -169
  42. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  43. data/lib/ruby-prof/measurement.rb +17 -0
  44. data/lib/ruby-prof/method_info.rb +78 -131
  45. data/lib/ruby-prof/printers/abstract_printer.rb +137 -85
  46. data/lib/ruby-prof/printers/call_info_printer.rb +53 -41
  47. data/lib/ruby-prof/printers/call_stack_printer.rb +180 -773
  48. data/lib/ruby-prof/printers/call_tree_printer.rb +151 -92
  49. data/lib/ruby-prof/printers/dot_printer.rb +132 -132
  50. data/lib/ruby-prof/printers/flat_printer.rb +53 -69
  51. data/lib/ruby-prof/printers/graph_html_printer.rb +63 -255
  52. data/lib/ruby-prof/printers/graph_printer.rb +113 -116
  53. data/lib/ruby-prof/printers/multi_printer.rb +127 -56
  54. data/lib/ruby-prof/profile.rb +37 -77
  55. data/lib/ruby-prof/rack.rb +62 -15
  56. data/lib/ruby-prof/task.rb +147 -147
  57. data/lib/ruby-prof/thread.rb +10 -12
  58. data/lib/ruby-prof/version.rb +3 -0
  59. data/lib/unprof.rb +10 -10
  60. data/ruby-prof.gemspec +65 -61
  61. data/test/abstract_printer_test.rb +26 -0
  62. data/test/alias_test.rb +126 -0
  63. data/test/basic_test.rb +43 -128
  64. data/test/call_tree_visitor_test.rb +32 -0
  65. data/test/call_trees_test.rb +66 -0
  66. data/test/duplicate_names_test.rb +32 -32
  67. data/test/dynamic_method_test.rb +53 -74
  68. data/test/enumerable_test.rb +21 -16
  69. data/test/exceptions_test.rb +24 -16
  70. data/test/exclude_methods_test.rb +151 -0
  71. data/test/exclude_threads_test.rb +53 -54
  72. data/test/fiber_test.rb +129 -65
  73. data/test/gc_test.rb +90 -0
  74. data/test/inverse_call_tree_test.rb +175 -0
  75. data/test/line_number_test.rb +158 -71
  76. data/test/marshal_test.rb +113 -0
  77. data/test/measure_allocations.rb +30 -0
  78. data/test/measure_allocations_test.rb +375 -25
  79. data/test/measure_allocations_trace_test.rb +375 -0
  80. data/test/measure_memory_trace_test.rb +1101 -0
  81. data/test/measure_process_time_test.rb +785 -62
  82. data/test/measure_times.rb +56 -0
  83. data/test/measure_wall_time_test.rb +434 -254
  84. data/test/multi_printer_test.rb +71 -82
  85. data/test/no_method_class_test.rb +15 -15
  86. data/test/pause_resume_test.rb +175 -166
  87. data/test/prime.rb +54 -54
  88. data/test/prime_script.rb +6 -0
  89. data/test/printer_call_stack_test.rb +27 -0
  90. data/test/printer_call_tree_test.rb +30 -0
  91. data/test/printer_flat_test.rb +99 -0
  92. data/test/printer_graph_html_test.rb +59 -0
  93. data/test/printer_graph_test.rb +40 -0
  94. data/test/printers_test.rb +141 -257
  95. data/test/printing_recursive_graph_test.rb +81 -0
  96. data/test/profile_test.rb +16 -0
  97. data/test/rack_test.rb +93 -0
  98. data/test/recursive_test.rb +206 -215
  99. data/test/singleton_test.rb +38 -38
  100. data/test/stack_printer_test.rb +64 -78
  101. data/test/start_stop_test.rb +109 -112
  102. data/test/test_helper.rb +13 -115
  103. data/test/thread_test.rb +144 -178
  104. data/test/unique_call_path_test.rb +120 -224
  105. data/test/yarv_test.rb +56 -0
  106. metadata +77 -133
  107. data/doc/LICENSE.html +0 -155
  108. data/doc/README_rdoc.html +0 -648
  109. data/doc/Rack.html +0 -167
  110. data/doc/Rack/RubyProf.html +0 -319
  111. data/doc/RubyProf.html +0 -1000
  112. data/doc/RubyProf/AbstractPrinter.html +0 -580
  113. data/doc/RubyProf/AggregateCallInfo.html +0 -570
  114. data/doc/RubyProf/CallInfo.html +0 -512
  115. data/doc/RubyProf/CallInfoPrinter.html +0 -190
  116. data/doc/RubyProf/CallInfoVisitor.html +0 -332
  117. data/doc/RubyProf/CallStackPrinter.html +0 -1600
  118. data/doc/RubyProf/CallTreePrinter.html +0 -413
  119. data/doc/RubyProf/Cmd.html +0 -669
  120. data/doc/RubyProf/DotPrinter.html +0 -312
  121. data/doc/RubyProf/FlatPrinter.html +0 -229
  122. data/doc/RubyProf/FlatPrinterWithLineNumbers.html +0 -267
  123. data/doc/RubyProf/GraphHtmlPrinter.html +0 -630
  124. data/doc/RubyProf/GraphPrinter.html +0 -209
  125. data/doc/RubyProf/MethodInfo.html +0 -713
  126. data/doc/RubyProf/MultiPrinter.html +0 -407
  127. data/doc/RubyProf/Profile.html +0 -821
  128. data/doc/RubyProf/ProfileTask.html +0 -532
  129. data/doc/RubyProf/Test.html +0 -578
  130. data/doc/RubyProf/Thread.html +0 -262
  131. data/doc/created.rid +0 -32
  132. data/doc/examples/flat_txt.html +0 -191
  133. data/doc/examples/graph_txt.html +0 -305
  134. data/doc/images/add.png +0 -0
  135. data/doc/images/brick.png +0 -0
  136. data/doc/images/brick_link.png +0 -0
  137. data/doc/images/bug.png +0 -0
  138. data/doc/images/bullet_black.png +0 -0
  139. data/doc/images/bullet_toggle_minus.png +0 -0
  140. data/doc/images/bullet_toggle_plus.png +0 -0
  141. data/doc/images/date.png +0 -0
  142. data/doc/images/delete.png +0 -0
  143. data/doc/images/find.png +0 -0
  144. data/doc/images/loadingAnimation.gif +0 -0
  145. data/doc/images/macFFBgHack.png +0 -0
  146. data/doc/images/package.png +0 -0
  147. data/doc/images/page_green.png +0 -0
  148. data/doc/images/page_white_text.png +0 -0
  149. data/doc/images/page_white_width.png +0 -0
  150. data/doc/images/plugin.png +0 -0
  151. data/doc/images/ruby.png +0 -0
  152. data/doc/images/tag_blue.png +0 -0
  153. data/doc/images/tag_green.png +0 -0
  154. data/doc/images/transparent.png +0 -0
  155. data/doc/images/wrench.png +0 -0
  156. data/doc/images/wrench_orange.png +0 -0
  157. data/doc/images/zoom.png +0 -0
  158. data/doc/index.html +0 -647
  159. data/doc/js/darkfish.js +0 -155
  160. data/doc/js/jquery.js +0 -18
  161. data/doc/js/navigation.js +0 -142
  162. data/doc/js/search.js +0 -94
  163. data/doc/js/search_index.js +0 -1
  164. data/doc/js/searcher.js +0 -228
  165. data/doc/rdoc.css +0 -543
  166. data/doc/table_of_contents.html +0 -462
  167. data/examples/empty.png +0 -0
  168. data/examples/flat.txt +0 -55
  169. data/examples/graph.dot +0 -106
  170. data/examples/graph.html +0 -823
  171. data/examples/graph.png +0 -0
  172. data/examples/graph.txt +0 -170
  173. data/examples/minus.png +0 -0
  174. data/examples/multi.flat.txt +0 -23
  175. data/examples/multi.graph.html +0 -906
  176. data/examples/multi.grind.dat +0 -194
  177. data/examples/multi.stack.html +0 -573
  178. data/examples/plus.png +0 -0
  179. data/examples/stack.html +0 -573
  180. data/ext/ruby_prof/rp_call_info.c +0 -407
  181. data/ext/ruby_prof/rp_call_info.h +0 -48
  182. data/ext/ruby_prof/rp_measure.c +0 -48
  183. data/ext/ruby_prof/rp_measure.h +0 -45
  184. data/ext/ruby_prof/rp_measure_cpu_time.c +0 -112
  185. data/ext/ruby_prof/rp_measure_gc_runs.c +0 -65
  186. data/ext/ruby_prof/rp_measure_gc_time.c +0 -57
  187. data/ext/ruby_prof/vc/ruby_prof_18.vcxproj +0 -108
  188. data/ext/ruby_prof/vc/ruby_prof_19.vcxproj +0 -110
  189. data/ext/ruby_prof/version.h +0 -7
  190. data/lib/ruby-prof/aggregate_call_info.rb +0 -72
  191. data/lib/ruby-prof/call_info.rb +0 -89
  192. data/lib/ruby-prof/call_info_visitor.rb +0 -44
  193. data/lib/ruby-prof/images/empty.png +0 -0
  194. data/lib/ruby-prof/images/minus.png +0 -0
  195. data/lib/ruby-prof/images/plus.png +0 -0
  196. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +0 -57
  197. data/lib/ruby-prof/test.rb +0 -150
  198. data/test/aggregate_test.rb +0 -136
  199. data/test/call_info_test.rb +0 -78
  200. data/test/call_info_visitor_test.rb +0 -31
  201. data/test/exec_test.rb +0 -14
  202. data/test/measure_cpu_time_test.rb +0 -220
  203. data/test/measure_gc_runs_test.rb +0 -32
  204. data/test/measure_gc_time_test.rb +0 -36
  205. data/test/measure_memory_test.rb +0 -31
  206. data/test/method_elimination_test.rb +0 -84
  207. data/test/module_test.rb +0 -45
  208. data/test/stack_test.rb +0 -138
  209. data/test/test_suite.rb +0 -37
@@ -1,268 +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
- #include "ruby_prof.h"
5
-
6
- VALUE cRpThread;
7
-
8
- /* ====== thread_data_t ====== */
9
- thread_data_t*
10
- thread_data_create()
11
- {
12
- thread_data_t* result = ALLOC(thread_data_t);
13
- result->stack = prof_stack_create();
14
- result->method_table = method_table_create();
15
- result->object = Qnil;
16
- result->methods = Qnil;
17
- return result;
18
- }
19
-
20
- /* The underlying c structures are freed when the parent profile is freed.
21
- However, on shutdown the Ruby GC frees objects in any will-nilly order.
22
- That means the ruby thread object wrapping the c thread struct may
23
- be freed before the parent profile. Thus we add in a free function
24
- for the garbage collector so that if it does get called will nil
25
- out our Ruby object reference.*/
26
- static void
27
- thread_data_ruby_gc_free(thread_data_t* thread_data)
28
- {
29
- /* Has this thread object been accessed by Ruby? If
30
- yes clean it up so to avoid a segmentation fault. */
31
- if (thread_data->object != Qnil)
32
- {
33
- RDATA(thread_data->object)->data = NULL;
34
- RDATA(thread_data->object)->dfree = NULL;
35
- RDATA(thread_data->object)->dmark = NULL;
36
- }
37
- thread_data->object = Qnil;
38
- }
39
-
40
- static void
41
- thread_data_free(thread_data_t* thread_data)
42
- {
43
- thread_data_ruby_gc_free(thread_data);
44
- method_table_free(thread_data->method_table);
45
- prof_stack_free(thread_data->stack);
46
-
47
- thread_data->thread_id = Qnil;
48
-
49
- xfree(thread_data);
50
- }
51
-
52
- static int
53
- mark_methods(st_data_t key, st_data_t value, st_data_t result)
54
- {
55
- prof_method_t *method = (prof_method_t *) value;
56
- prof_method_mark(method);
57
- return ST_CONTINUE;
58
- }
59
-
60
- void
61
- prof_thread_mark(thread_data_t *thread)
62
- {
63
- if (thread->object != Qnil)
64
- rb_gc_mark(thread->object);
65
-
66
- if (thread->methods != Qnil)
67
- rb_gc_mark(thread->methods);
68
-
69
- if (thread->thread_id != Qnil)
70
- rb_gc_mark(thread->thread_id);
71
-
72
- if (thread->fiber_id != Qnil)
73
- rb_gc_mark(thread->fiber_id);
74
-
75
- st_foreach(thread->method_table, mark_methods, 0);
76
- }
77
-
78
- VALUE
79
- prof_thread_wrap(thread_data_t *thread)
80
- {
81
- if (thread->object == Qnil)
82
- {
83
- thread->object = Data_Wrap_Struct(cRpThread, prof_thread_mark, thread_data_ruby_gc_free, thread);
84
- }
85
- return thread->object;
86
- }
87
-
88
- static thread_data_t*
89
- prof_get_thread(VALUE self)
90
- {
91
- /* Can't use Data_Get_Struct because that triggers the event hook
92
- ending up in endless recursion. */
93
- thread_data_t* result = DATA_PTR(self);
94
- if (!result)
95
- rb_raise(rb_eRuntimeError, "This RubyProf::Thread instance has already been freed, likely because its profile has been freed.");
96
-
97
- return result;
98
- }
99
-
100
- /* ====== Thread Table ====== */
101
- /* The thread table is hash keyed on ruby thread_id that stores instances
102
- of thread_data_t. */
103
-
104
- st_table *
105
- threads_table_create()
106
- {
107
- return st_init_numtable();
108
- }
109
-
110
- static int
111
- thread_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
112
- {
113
- thread_data_free((thread_data_t*)value);
114
- return ST_CONTINUE;
115
- }
116
-
117
- void
118
- threads_table_free(st_table *table)
119
- {
120
- st_foreach(table, thread_table_free_iterator, 0);
121
- st_free_table(table);
122
- }
123
-
124
- size_t
125
- threads_table_insert(prof_profile_t* profile, VALUE fiber, thread_data_t *thread_data)
126
- {
127
- /* Its too slow to key on the real thread id so just typecast thread instead. */
128
- return st_insert(profile->threads_tbl, (st_data_t) fiber, (st_data_t) thread_data);
129
- }
130
-
131
- thread_data_t *
132
- threads_table_lookup(prof_profile_t* profile, VALUE thread_id, VALUE fiber_id)
133
- {
134
- thread_data_t* result;
135
- st_data_t val;
136
-
137
- /* Its too slow to key on the real thread id so just typecast thread instead. */
138
- if (st_lookup(profile->threads_tbl, (st_data_t) fiber_id, &val))
139
- {
140
- result = (thread_data_t *) val;
141
- }
142
- else
143
- {
144
- result = thread_data_create();
145
- result->thread_id = thread_id;
146
- result->fiber_id = fiber_id;
147
-
148
- /* Insert the table */
149
- threads_table_insert(profile, fiber_id, result);
150
- }
151
- return result;
152
- }
153
-
154
- thread_data_t *
155
- switch_thread(void* prof, VALUE thread_id, VALUE fiber_id)
156
- {
157
- prof_profile_t* profile = (prof_profile_t*)prof;
158
- double measurement = profile->measurer->measure();
159
-
160
- /* Get new thread information. */
161
- thread_data_t *thread_data = threads_table_lookup(profile, thread_id, fiber_id);
162
-
163
- /* Get current frame for this thread */
164
- prof_frame_t *frame = prof_stack_peek(thread_data->stack);
165
-
166
- /* Update the time this thread waited for another thread */
167
- if (frame)
168
- {
169
- frame->wait_time += measurement - frame->switch_time;
170
- frame->switch_time = measurement;
171
- }
172
-
173
- /* Save on the last thread the time of the context switch
174
- and reset this thread's last context switch to 0.*/
175
- if (profile->last_thread_data)
176
- {
177
- prof_frame_t *last_frame = prof_stack_peek(profile->last_thread_data->stack);
178
- if (last_frame)
179
- last_frame->switch_time = measurement;
180
- }
181
-
182
- profile->last_thread_data = thread_data;
183
- return thread_data;
184
- }
185
-
186
- int pause_thread(st_data_t key, st_data_t value, st_data_t data)
187
- {
188
- thread_data_t* thread_data = (thread_data_t *) value;
189
- prof_profile_t* profile = (prof_profile_t*)data;
190
-
191
- prof_frame_t* frame = prof_stack_peek(thread_data->stack);
192
- prof_frame_pause(frame, profile->measurement_at_pause_resume);
193
-
194
- return ST_CONTINUE;
195
- }
196
-
197
- int unpause_thread(st_data_t key, st_data_t value, st_data_t data)
198
- {
199
- thread_data_t* thread_data = (thread_data_t *) value;
200
- prof_profile_t* profile = (prof_profile_t*)data;
201
-
202
- prof_frame_t* frame = prof_stack_peek(thread_data->stack);
203
- prof_frame_unpause(frame, profile->measurement_at_pause_resume);
204
-
205
- return ST_CONTINUE;
206
- }
207
-
208
- static int
209
- collect_methods(st_data_t key, st_data_t value, st_data_t result)
210
- {
211
- /* Called for each method stored in a thread's method table.
212
- We want to store the method info information into an array.*/
213
- VALUE methods = (VALUE) result;
214
- prof_method_t *method = (prof_method_t *) value;
215
- rb_ary_push(methods, prof_method_wrap(method));
216
-
217
- return ST_CONTINUE;
218
- }
219
-
220
-
221
- /* call-seq:
222
- id -> number
223
-
224
- Returns the id of this thread. */
225
- static VALUE
226
- prof_thread_id(VALUE self)
227
- {
228
- thread_data_t* thread = prof_get_thread(self);
229
- return thread->thread_id;
230
- }
231
-
232
- /* call-seq:
233
- fiber_id -> number
234
-
235
- Returns the fiber id of this thread. */
236
- static VALUE
237
- prof_fiber_id(VALUE self)
238
- {
239
- thread_data_t* thread = prof_get_thread(self);
240
- return thread->fiber_id;
241
- }
242
-
243
- /* call-seq:
244
- methods -> Array of MethodInfo
245
-
246
- Returns an array of methods that were called from this
247
- thread during program execution. */
248
- static VALUE
249
- prof_thread_methods(VALUE self)
250
- {
251
- thread_data_t* thread = prof_get_thread(self);
252
- if (thread->methods == Qnil)
253
- {
254
- thread->methods = rb_ary_new();
255
- st_foreach(thread->method_table, collect_methods, thread->methods);
256
- }
257
- return thread->methods;
258
- }
259
-
260
- void rp_init_thread()
261
- {
262
- cRpThread = rb_define_class_under(mProf, "Thread", rb_cObject);
263
- rb_undef_method(CLASS_OF(cRpThread), "new");
264
-
265
- rb_define_method(cRpThread, "id", prof_thread_id, 0);
266
- rb_define_method(cRpThread, "fiber_id", prof_fiber_id, 0);
267
- rb_define_method(cRpThread, "methods", prof_thread_methods, 0);
268
- }
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
+ }