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,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
+ }