ruby-prof 0.13.1 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
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,27 +1,39 @@
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
- #ifndef __RP_THREAD__
5
- #define __RP_THREAD__
6
-
7
- /* Profiling information for a thread. */
8
- typedef struct
9
- {
10
- VALUE object; /* Cache to wrapped object */
11
- VALUE methods; /* Array of RubyProf::MethodInfo */
12
- VALUE thread_id; /* Thread id */
13
- VALUE fiber_id; /* Fiber id */
14
- st_table* method_table; /* Methods called in the thread */
15
- prof_stack_t* stack; /* Stack of frames */
16
- } thread_data_t;
17
-
18
- void rp_init_thread();
19
- st_table * threads_table_create();
20
- thread_data_t* switch_thread(void* prof, VALUE thread_id, VALUE fiber_id);
21
- void threads_table_free(st_table *table);
22
- VALUE prof_thread_wrap(thread_data_t *thread);
23
- void prof_thread_mark(thread_data_t *thread);
24
- int pause_thread(st_data_t key, st_data_t value, st_data_t data);
25
- int unpause_thread(st_data_t key, st_data_t value, st_data_t data);
26
-
27
- #endif //__RP_THREAD__
1
+ /* Copyright (C) 2005-2019 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
+ #ifndef __RP_THREAD__
5
+ #define __RP_THREAD__
6
+
7
+ #include "ruby_prof.h"
8
+ #include "rp_stack.h"
9
+
10
+ /* Profiling information for a thread. */
11
+ typedef struct thread_data_t
12
+ {
13
+ // Runtime
14
+ VALUE object; /* Cache to wrapped object */
15
+ VALUE fiber; /* Fiber */
16
+ prof_stack_t* stack; /* Stack of frames */
17
+ bool trace; /* Are we tracking this thread */
18
+ prof_call_tree_t* call_tree; /* The root of the call tree*/
19
+ VALUE thread_id; /* Thread id */
20
+ VALUE fiber_id; /* Fiber id */
21
+ VALUE methods; /* Array of RubyProf::MethodInfo */
22
+ st_table* method_table; /* Methods called in the thread */
23
+ } thread_data_t;
24
+
25
+ void rp_init_thread(void);
26
+ st_table* threads_table_create(void);
27
+ thread_data_t* threads_table_lookup(void* profile, VALUE fiber);
28
+ thread_data_t* threads_table_insert(void* profile, VALUE fiber);
29
+ void threads_table_free(st_table* table);
30
+
31
+ thread_data_t* prof_get_thread(VALUE self);
32
+ VALUE prof_thread_wrap(thread_data_t* thread);
33
+ void prof_thread_mark(void* data);
34
+
35
+ void switch_thread(void* profile, thread_data_t* thread_data, double measurement);
36
+ int pause_thread(st_data_t key, st_data_t value, st_data_t data);
37
+ int unpause_thread(st_data_t key, st_data_t value, st_data_t data);
38
+
39
+ #endif //__RP_THREAD__
@@ -1,695 +1,52 @@
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
- /* ruby-prof tracks the time spent executing every method in ruby programming.
5
- The main players are:
6
-
7
- profile_t - This represents 1 profile.
8
- thread_data_t - Stores data about a single thread.
9
- prof_stack_t - The method call stack in a particular thread
10
- prof_method_t - Profiling information about each method
11
- prof_call_info_t - Keeps track a method's callers and callees.
12
-
13
- The final result is an instance of a profile object which has a hash table of
14
- thread_data_t, keyed on the thread id. Each thread in turn has a hash table
15
- of prof_method_t, keyed on the method id. A hash table is used for quick
16
- look up when doing a profile. However, it is exposed to Ruby as an array.
17
-
18
- Each prof_method_t has two hash tables, parent and children, of prof_call_info_t.
19
- These objects keep track of a method's callers (who called the method) and its
20
- callees (who the method called). These are keyed the method id, but once again,
21
- are exposed to Ruby as arrays. Each prof_call_into_t maintains a pointer to the
22
- caller or callee method, thereby making it easy to navigate through the call
23
- hierarchy in ruby - which is very helpful for creating call graphs.
24
- */
25
-
26
- #include "ruby_prof.h"
27
- #include <assert.h>
28
-
29
- VALUE mProf;
30
- VALUE cProfile;
31
-
32
- #ifndef RUBY_VM
33
- /* Global variable to hold current profile - needed
34
- prior to Ruby 1.9 */
35
- static prof_profile_t* pCurrentProfile;
36
- #endif
37
-
38
- static prof_profile_t*
39
- prof_get_profile(VALUE self)
40
- {
41
- /* Can't use Data_Get_Struct because that triggers the event hook
42
- ending up in endless recursion. */
43
- return (prof_profile_t*)RDATA(self)->data;
44
- }
45
-
46
- /* support tracing ruby events from ruby-prof. useful for getting at
47
- what actually happens inside the ruby interpreter (and ruby-prof).
48
- set environment variable RUBY_PROF_TRACE to filename you want to
49
- find the trace in.
50
- */
51
- static FILE* trace_file = NULL;
52
-
53
- /* Copied from eval.c (1.8.x) / thread.c (1.9.2) */
54
- static const char *
55
- get_event_name(rb_event_flag_t event)
56
- {
57
- switch (event) {
58
- case RUBY_EVENT_LINE:
59
- return "line";
60
- case RUBY_EVENT_CLASS:
61
- return "class";
62
- case RUBY_EVENT_END:
63
- return "end";
64
- case RUBY_EVENT_CALL:
65
- return "call";
66
- case RUBY_EVENT_RETURN:
67
- return "return";
68
- case RUBY_EVENT_C_CALL:
69
- return "c-call";
70
- case RUBY_EVENT_C_RETURN:
71
- return "c-return";
72
- case RUBY_EVENT_RAISE:
73
- return "raise";
74
-
75
- #ifdef RUBY_VM
76
- case RUBY_EVENT_SWITCH:
77
- return "thread-interrupt";
78
- #endif
79
-
80
- default:
81
- return "unknown";
82
- }
83
- }
84
-
85
- static prof_method_t*
86
- create_method(rb_event_flag_t event, VALUE klass, ID mid, const char* source_file, int line)
87
- {
88
- /* Line numbers are not accurate for c method calls */
89
- if (event == RUBY_EVENT_C_CALL)
90
- {
91
- line = 0;
92
- source_file = NULL;
93
- }
94
-
95
- return prof_method_create(klass, mid, source_file, line);
96
- }
97
-
98
-
99
- static prof_method_t*
100
- #ifdef RUBY_VM
101
- get_method(rb_event_flag_t event, VALUE klass, ID mid, thread_data_t* thread_data)
102
- # else
103
- get_method(rb_event_flag_t event, NODE *node, VALUE klass, ID mid, thread_data_t* thread_data)
104
- #endif
105
- {
106
- prof_method_key_t key;
107
- prof_method_t *method = NULL;
108
-
109
- method_key(&key, klass, mid);
110
- method = method_table_lookup(thread_data->method_table, &key);
111
-
112
- if (!method)
113
- {
114
- const char* source_file = rb_sourcefile();
115
- int line = rb_sourceline();
116
-
117
- method = create_method(event, klass, mid, source_file, line);
118
- method_table_insert(thread_data->method_table, method->key, method);
119
- }
120
- return method;
121
- }
122
-
123
- static int
124
- pop_frames(st_data_t key, st_data_t value, st_data_t data)
125
- {
126
- VALUE fiber_id = (VALUE)key;
127
- thread_data_t* thread_data = (thread_data_t *) value;
128
- prof_profile_t* profile = (prof_profile_t*) data;
129
- double measurement = profile->measurer->measure();
130
-
131
- if (!profile->last_thread_data || profile->last_thread_data->fiber_id != fiber_id)
132
- thread_data = switch_thread(profile, Qnil, fiber_id);
133
- else
134
- thread_data = profile->last_thread_data;
135
-
136
- while (prof_stack_pop(thread_data->stack, measurement))
137
- {
138
- }
139
-
140
- return ST_CONTINUE;
141
- }
142
-
143
- static void
144
- prof_pop_threads(prof_profile_t* profile)
145
- {
146
- st_foreach(profile->threads_tbl, pop_frames, (st_data_t) profile);
147
- }
148
-
149
- /* =========== Profiling ================= */
150
- #ifdef RUBY_VM
151
- static void
152
- prof_trace(prof_profile_t* profile, rb_event_flag_t event, ID mid, VALUE klass, double measurement)
153
- #else
154
- static void
155
- prof_trace(prof_profile_t* profile, rb_event_flag_t event, NODE *node, ID mid, VALUE klass, double measurement)
156
- #endif
157
- {
158
- static VALUE last_fiber_id = Qnil;
159
-
160
- VALUE thread = rb_thread_current();
161
- VALUE thread_id = rb_obj_id(thread);
162
- #if defined(HAVE_RB_FIBER_CURRENT)
163
- VALUE fiber = rb_fiber_current();
164
- VALUE fiber_id = rb_obj_id(fiber);
165
- #else
166
- VALUE fiber_id = thread_id;
167
- #endif
168
- const char* class_name = NULL;
169
- const char* method_name = rb_id2name(mid);
170
- const char* source_file = rb_sourcefile();
171
- unsigned int source_line = rb_sourceline();
172
-
173
- const char* event_name = get_event_name(event);
174
-
175
- if (klass != 0)
176
- klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
177
-
178
- class_name = rb_class2name(klass);
179
-
180
- if (last_fiber_id != fiber_id)
181
- {
182
- fprintf(trace_file, "\n");
183
- }
184
-
185
- fprintf(trace_file, "%2lu:%2lu:%2ums %-8s %s:%2d %s#%s\n",
186
- (unsigned long) thread_id, (unsigned long) fiber_id, (unsigned int) measurement*1000,
187
- event_name, source_file, source_line, class_name, method_name);
188
- fflush(trace_file);
189
- last_fiber_id = fiber_id;
190
- }
191
-
192
- #ifdef RUBY_VM
193
- static void
194
- prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
195
- #else
196
- static void
197
- prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE klass)
198
- #endif
199
- {
200
- #ifndef RUBY_VM
201
- prof_profile_t* profile = pCurrentProfile;
202
- #else
203
- prof_profile_t* profile = prof_get_profile(data);
204
- #endif
205
-
206
- VALUE thread = Qnil;
207
- VALUE thread_id = Qnil;
208
- VALUE fiber = Qnil;
209
- VALUE fiber_id = Qnil;
210
- thread_data_t* thread_data = NULL;
211
- prof_frame_t *frame = NULL;
212
- double measurement;
213
-
214
- #ifdef RUBY_VM
215
- if (event != RUBY_EVENT_C_CALL && event != RUBY_EVENT_C_RETURN) {
216
- // guess these are already set for C calls in 1.9, then?
217
- rb_frame_method_id_and_class(&mid, &klass);
218
- }
219
- #endif
220
-
221
- /* Get current measurement */
222
- measurement = profile->measurer->measure();
223
-
224
- if (trace_file != NULL)
225
- {
226
- #ifdef RUBY_VM
227
- prof_trace(profile, event, mid, klass, measurement);
228
- #else
229
- prof_trace(profile, event, node, mid, klass, measurement);
230
- #endif
231
- }
232
-
233
- /* Special case - skip any methods from the mProf
234
- module or cProfile class since they clutter
235
- the results but aren't important to them results. */
236
- if (self == mProf || klass == cProfile)
237
- return;
238
-
239
- /* Get the current thread information. */
240
- thread = rb_thread_current();
241
- thread_id = rb_obj_id(thread);
242
- #if defined(HAVE_RB_FIBER_CURRENT)
243
- fiber = rb_fiber_current();
244
- fiber_id = rb_obj_id(fiber);
245
- #else
246
- fiber = thread;
247
- fiber_id = thread_id;
248
- #endif
249
-
250
- if (st_lookup(profile->exclude_threads_tbl, (st_data_t) thread_id, 0))
251
- {
252
- return;
253
- }
254
-
255
- /* Was there a context switch? */
256
- if (!profile->last_thread_data || profile->last_thread_data->fiber_id != fiber_id)
257
- thread_data = switch_thread(profile, thread_id, fiber_id);
258
- else
259
- thread_data = profile->last_thread_data;
260
-
261
- /* Get the current frame for the current thread. */
262
- frame = prof_stack_peek(thread_data->stack);
263
-
264
- switch (event) {
265
- case RUBY_EVENT_LINE:
266
- {
267
- /* Keep track of the current line number in this method. When
268
- a new method is called, we know what line number it was
269
- called from. */
270
-
271
- if (frame)
272
- {
273
- frame->line = rb_sourceline();
274
- break;
275
- }
276
-
277
- /* If we get here there was no frame, which means this is
278
- the first method seen for this thread, so fall through
279
- to below to create it. */
280
- }
281
- case RUBY_EVENT_CALL:
282
- case RUBY_EVENT_C_CALL:
283
- {
284
- prof_call_info_t *call_info = NULL;
285
- prof_method_t *method = NULL;
286
-
287
- #ifdef RUBY_VM
288
- method = get_method(event, klass, mid, thread_data);
289
- #else
290
- method = get_method(event, node, klass, mid, thread_data);
291
- #endif
292
-
293
- if (!frame)
294
- {
295
- call_info = prof_call_info_create(method, NULL);
296
- prof_add_call_info(method->call_infos, call_info);
297
- }
298
- else
299
- {
300
- call_info = call_info_table_lookup(frame->call_info->call_infos, method->key);
301
-
302
- if (!call_info)
303
- {
304
- /* This call info does not yet exist. So create it, then add
305
- it to previous callinfo's children and to the current method .*/
306
- call_info = prof_call_info_create(method, frame->call_info);
307
- call_info_table_insert(frame->call_info->call_infos, method->key, call_info);
308
- prof_add_call_info(method->call_infos, call_info);
309
- }
310
-
311
- // Unpause the parent frame. If currently paused then:
312
- // 1) The child frame will begin paused.
313
- // 2) The parent will inherit the child's dead time.
314
- prof_frame_unpause(frame, measurement);
315
- }
316
-
317
- /* Push a new frame onto the stack for a new c-call or ruby call (into a method) */
318
- frame = prof_stack_push(thread_data->stack, measurement);
319
- frame->call_info = call_info;
320
- frame->call_info->depth = frame->depth;
321
- frame->pause_time = profile->paused == Qtrue ? measurement : -1;
322
- frame->line = rb_sourceline();
323
- break;
324
- }
325
- case RUBY_EVENT_RETURN:
326
- case RUBY_EVENT_C_RETURN:
327
- {
328
- prof_stack_pop(thread_data->stack, measurement);
329
- break;
330
- }
331
- }
332
- }
333
-
334
- void
335
- prof_install_hook(VALUE self)
336
- {
337
- #ifdef RUBY_VM
338
- rb_add_event_hook(prof_event_hook,
339
- RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
340
- RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
341
- | RUBY_EVENT_LINE, self); // RUBY_EVENT_SWITCH
342
- #else
343
- rb_add_event_hook(prof_event_hook,
344
- RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
345
- RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
346
- | RUBY_EVENT_LINE);
347
-
348
- pCurrentProfile = prof_get_profile(self);
349
- #endif
350
-
351
- #if defined(TOGGLE_GC_STATS)
352
- rb_gc_enable_stats();
353
- #endif
354
- }
355
-
356
- void
357
- prof_remove_hook()
358
- {
359
- #if defined(TOGGLE_GC_STATS)
360
- rb_gc_disable_stats();
361
- #endif
362
-
363
- #ifndef RUBY_VM
364
- pCurrentProfile = NULL;
365
- #endif
366
-
367
- /* Now unregister from event */
368
- rb_remove_event_hook(prof_event_hook);
369
- }
370
-
371
- static int
372
- collect_threads(st_data_t key, st_data_t value, st_data_t result)
373
- {
374
- thread_data_t* thread_data = (thread_data_t*) value;
375
- VALUE threads_array = (VALUE) result;
376
- rb_ary_push(threads_array, prof_thread_wrap(thread_data));
377
- return ST_CONTINUE;
378
- }
379
-
380
- /* ======== Profile Class ====== */
381
- static int
382
- mark_threads(st_data_t key, st_data_t value, st_data_t result)
383
- {
384
- thread_data_t *thread = (thread_data_t *) value;
385
- prof_thread_mark(thread);
386
- return ST_CONTINUE;
387
- }
388
-
389
- static void
390
- prof_mark(prof_profile_t *profile)
391
- {
392
- st_foreach(profile->threads_tbl, mark_threads, 0);
393
- }
394
-
395
- /* Freeing the profile creates a cascade of freeing.
396
- It fress the thread table, which frees its methods,
397
- which frees its call infos. */
398
- static void
399
- prof_free(prof_profile_t *profile)
400
- {
401
- profile->last_thread_data = NULL;
402
-
403
- threads_table_free(profile->threads_tbl);
404
- profile->threads_tbl = NULL;
405
-
406
- st_free_table(profile->exclude_threads_tbl);
407
- profile->exclude_threads_tbl = NULL;
408
-
409
- xfree(profile->measurer);
410
- profile->measurer = NULL;
411
-
412
- xfree(profile);
413
- }
414
-
415
- static VALUE
416
- prof_allocate(VALUE klass)
417
- {
418
- VALUE result;
419
- prof_profile_t* profile;
420
- result = Data_Make_Struct(klass, prof_profile_t, prof_mark, prof_free, profile);
421
- profile->threads_tbl = threads_table_create();
422
- profile->exclude_threads_tbl = threads_table_create();
423
- profile->running = Qfalse;
424
- return result;
425
- }
426
-
427
- /* call-seq:
428
- RubyProf::Profile.new(mode, exclude_threads) -> instance
429
-
430
- Returns a new profiler.
431
-
432
- == Parameters
433
- mode:: Measure mode (optional). Specifies the profile measure mode. If not specified, defaults
434
- to RubyProf::WALL_TIME.
435
- exclude_threads:: Threads to exclude from the profiling results (optional). */
436
- static VALUE
437
- prof_initialize(int argc, VALUE *argv, VALUE self)
438
- {
439
- prof_profile_t* profile = prof_get_profile(self);
440
- VALUE mode;
441
- prof_measure_mode_t measurer = MEASURE_WALL_TIME;
442
- VALUE exclude_threads;
443
- int i;
444
-
445
- switch (rb_scan_args(argc, argv, "02", &mode, &exclude_threads))
446
- {
447
- case 0:
448
- {
449
- measurer = MEASURE_WALL_TIME;
450
- exclude_threads = rb_ary_new();
451
- break;
452
- }
453
- case 1:
454
- {
455
- measurer = (prof_measure_mode_t)NUM2INT(mode);
456
- exclude_threads = rb_ary_new();
457
- break;
458
- }
459
- case 2:
460
- {
461
- Check_Type(exclude_threads, T_ARRAY);
462
- measurer = (prof_measure_mode_t)NUM2INT(mode);
463
- break;
464
- }
465
- }
466
-
467
- profile->measurer = prof_get_measurer(measurer);
468
-
469
- for (i = 0; i < RARRAY_LEN(exclude_threads); i++)
470
- {
471
- VALUE thread = rb_ary_entry(exclude_threads, i);
472
- VALUE thread_id = rb_obj_id(thread);
473
- st_insert(profile->exclude_threads_tbl, thread_id, Qtrue);
474
- }
475
-
476
- return self;
477
- }
478
-
479
- /* call-seq:
480
- paused? -> boolean
481
-
482
- Returns whether a profile is currently paused.*/
483
- static VALUE
484
- prof_paused(VALUE self)
485
- {
486
- prof_profile_t* profile = prof_get_profile(self);
487
- return profile->paused;
488
- }
489
-
490
- /* call-seq:
491
- running? -> boolean
492
-
493
- Returns whether a profile is currently running.*/
494
- static VALUE
495
- prof_running(VALUE self)
496
- {
497
- prof_profile_t* profile = prof_get_profile(self);
498
- return profile->running;
499
- }
500
-
501
- /* call-seq:
502
- start -> RubyProf
503
-
504
- Starts recording profile data.*/
505
- static VALUE
506
- prof_start(VALUE self)
507
- {
508
- char* trace_file_name;
509
-
510
- prof_profile_t* profile = prof_get_profile(self);
511
-
512
- if (profile->running == Qtrue)
513
- {
514
- rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
515
- }
516
-
517
- #ifndef RUBY_VM
518
- if (pCurrentProfile != NULL)
519
- {
520
- rb_raise(rb_eRuntimeError, "Only one profile can run at a time on Ruby 1.8.*");
521
- }
522
- #endif
523
-
524
- profile->running = Qtrue;
525
- profile->paused = Qfalse;
526
- profile->last_thread_data = NULL;
527
-
528
-
529
- /* open trace file if environment wants it */
530
- trace_file_name = getenv("RUBY_PROF_TRACE");
531
- if (trace_file_name != NULL)
532
- {
533
- if (strcmp(trace_file_name, "stdout") == 0)
534
- {
535
- trace_file = stdout;
536
- }
537
- else if (strcmp(trace_file_name, "stderr") == 0)
538
- {
539
- trace_file = stderr;
540
- }
541
- else
542
- {
543
- trace_file = fopen(trace_file_name, "w");
544
- }
545
- }
546
-
547
- prof_install_hook(self);
548
- return self;
549
- }
550
-
551
- /* call-seq:
552
- pause -> RubyProf
553
-
554
- Pauses collecting profile data. */
555
- static VALUE
556
- prof_pause(VALUE self)
557
- {
558
- prof_profile_t* profile = prof_get_profile(self);
559
- if (profile->running == Qfalse)
560
- {
561
- rb_raise(rb_eRuntimeError, "RubyProf is not running.");
562
- }
563
-
564
- if (profile->paused == Qfalse)
565
- {
566
- profile->paused = Qtrue;
567
- profile->measurement_at_pause_resume = profile->measurer->measure();
568
- st_foreach(profile->threads_tbl, pause_thread, (st_data_t) profile);
569
- }
570
-
571
- return self;
572
- }
573
-
574
- /* call-seq:
575
- resume {block} -> RubyProf
576
-
577
- Resumes recording profile data.*/
578
- static VALUE
579
- prof_resume(VALUE self)
580
- {
581
- prof_profile_t* profile = prof_get_profile(self);
582
- if (profile->running == Qfalse)
583
- {
584
- rb_raise(rb_eRuntimeError, "RubyProf is not running.");
585
- }
586
-
587
- if (profile->paused == Qtrue)
588
- {
589
- profile->paused = Qfalse;
590
- profile->measurement_at_pause_resume = profile->measurer->measure();
591
- st_foreach(profile->threads_tbl, unpause_thread, (st_data_t) profile);
592
- }
593
-
594
- return rb_block_given_p() ? rb_ensure(rb_yield, self, prof_pause, self) : self;
595
- }
596
-
597
- /* call-seq:
598
- stop -> self
599
-
600
- Stops collecting profile data.*/
601
- static VALUE
602
- prof_stop(VALUE self)
603
- {
604
- prof_profile_t* profile = prof_get_profile(self);
605
-
606
- if (profile->running == Qfalse)
607
- {
608
- rb_raise(rb_eRuntimeError, "RubyProf.start was not yet called");
609
- }
610
-
611
- prof_remove_hook();
612
-
613
- /* close trace file if open */
614
- if (trace_file != NULL)
615
- {
616
- if (trace_file !=stderr && trace_file != stdout)
617
- {
618
- #ifdef _MSC_VER
619
- _fcloseall();
620
- #else
621
- fclose(trace_file);
622
- #endif
623
- }
624
- trace_file = NULL;
625
- }
626
-
627
- prof_pop_threads(profile);
628
-
629
- /* Unset the last_thread_data (very important!)
630
- and the threads table */
631
- profile->running = profile->paused = Qfalse;
632
- profile->last_thread_data = NULL;
633
-
634
- /* Post process result */
635
- rb_funcall(self, rb_intern("post_process") , 0);
636
-
637
- return self;
638
- }
639
-
640
- /* call-seq:
641
- profile {block} -> RubyProf::Result
642
-
643
- Profiles the specified block and returns a RubyProf::Result object. */
644
- static VALUE
645
- prof_profile(int argc, VALUE *argv, VALUE klass)
646
- {
647
- int result;
648
- VALUE profile = rb_class_new_instance(argc, argv, cProfile);
649
-
650
- if (!rb_block_given_p())
651
- {
652
- rb_raise(rb_eArgError, "A block must be provided to the profile method.");
653
- }
654
-
655
- prof_start(profile);
656
- rb_protect(rb_yield, profile, &result);
657
- return prof_stop(profile);
658
- }
659
-
660
- /* call-seq:
661
- threads -> Array of RubyProf::Thread
662
-
663
- Returns an array of RubyProf::Thread instances that were executed
664
- while the the program was being run. */
665
- static VALUE
666
- prof_threads(VALUE self)
667
- {
668
- VALUE result = rb_ary_new();
669
- prof_profile_t* profile = prof_get_profile(self);
670
- st_foreach(profile->threads_tbl, collect_threads, result);
671
- return result;
672
- }
673
-
674
- void Init_ruby_prof()
675
- {
676
- mProf = rb_define_module("RubyProf");
677
- rb_define_const(mProf, "VERSION", rb_str_new2(RUBY_PROF_VERSION));
678
-
679
- rp_init_measure();
680
- rp_init_method_info();
681
- rp_init_call_info();
682
- rp_init_thread();
683
-
684
- cProfile = rb_define_class_under(mProf, "Profile", rb_cObject);
685
- rb_define_singleton_method(cProfile, "profile", prof_profile, -1);
686
- rb_define_alloc_func (cProfile, prof_allocate);
687
- rb_define_method(cProfile, "initialize", prof_initialize, -1);
688
- rb_define_method(cProfile, "start", prof_start, 0);
689
- rb_define_method(cProfile, "stop", prof_stop, 0);
690
- rb_define_method(cProfile, "resume", prof_resume, 0);
691
- rb_define_method(cProfile, "pause", prof_pause, 0);
692
- rb_define_method(cProfile, "running?", prof_running, 0);
693
- rb_define_method(cProfile, "paused?", prof_paused, 0);
694
- rb_define_method(cProfile, "threads", prof_threads, 0);
695
- }
1
+ /* Copyright (C) 2005-2019 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
+ /* ruby-prof tracks the time spent executing every method in ruby programming.
5
+ The main players are:
6
+
7
+ profile_t - This represents 1 profile.
8
+ thread_data_t - Stores data about a single thread.
9
+ prof_stack_t - The method call stack in a particular thread
10
+ prof_method_t - Profiling information about each method
11
+ prof_call_tree_t - Keeps track a method's callers and callees.
12
+
13
+ The final result is an instance of a profile object which has a hash table of
14
+ thread_data_t, keyed on the thread id. Each thread in turn has a hash table
15
+ of prof_method_t, keyed on the method id. A hash table is used for quick
16
+ look up when doing a profile. However, it is exposed to Ruby as an array.
17
+
18
+ Each prof_method_t has two hash tables, parent and children, of prof_call_tree_t.
19
+ These objects keep track of a method's callers (who called the method) and its
20
+ callees (who the method called). These are keyed the method id, but once again,
21
+ are exposed to Ruby as arrays. Each prof_call_into_t maintains a pointer to the
22
+ caller or callee method, thereby making it easy to navigate through the call
23
+ hierarchy in ruby - which is very helpful for creating call graphs.
24
+ */
25
+
26
+ #include "ruby_prof.h"
27
+
28
+ #include "rp_allocation.h"
29
+ #include "rp_measurement.h"
30
+ #include "rp_method.h"
31
+ #include "rp_call_tree.h"
32
+ #include "rp_aggregate_call_tree.h"
33
+ #include "rp_call_trees.h"
34
+ #include "rp_profile.h"
35
+ #include "rp_stack.h"
36
+ #include "rp_thread.h"
37
+
38
+ VALUE mProf;
39
+
40
+ void Init_ruby_prof()
41
+ {
42
+ mProf = rb_define_module("RubyProf");
43
+
44
+ rp_init_allocation();
45
+ rp_init_call_tree();
46
+ rp_init_aggregate_call_tree();
47
+ rp_init_call_trees();
48
+ rp_init_measure();
49
+ rp_init_method_info();
50
+ rp_init_profile();
51
+ rp_init_thread();
52
+ }