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
@@ -0,0 +1,237 @@
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
+ #include "rp_measurement.h"
5
+
6
+ VALUE mMeasure;
7
+ VALUE cRpMeasurement;
8
+
9
+ prof_measurer_t* prof_measurer_allocations(bool track_allocations);
10
+ prof_measurer_t* prof_measurer_memory(bool track_allocations);
11
+ prof_measurer_t* prof_measurer_process_time(bool track_allocations);
12
+ prof_measurer_t* prof_measurer_wall_time(bool track_allocations);
13
+
14
+ void rp_init_measure_allocations(void);
15
+ void rp_init_measure_memory(void);
16
+ void rp_init_measure_process_time(void);
17
+ void rp_init_measure_wall_time(void);
18
+
19
+ prof_measurer_t* prof_get_measurer(prof_measure_mode_t measure, bool track_allocations)
20
+ {
21
+ switch (measure)
22
+ {
23
+ case MEASURE_WALL_TIME:
24
+ return prof_measurer_wall_time(track_allocations);
25
+ case MEASURE_PROCESS_TIME:
26
+ return prof_measurer_process_time(track_allocations);
27
+ case MEASURE_ALLOCATIONS:
28
+ return prof_measurer_allocations(track_allocations);
29
+ case MEASURE_MEMORY:
30
+ return prof_measurer_memory(track_allocations);
31
+ default:
32
+ rb_raise(rb_eArgError, "Unknown measure mode: %d", measure);
33
+ }
34
+ };
35
+
36
+ double prof_measure(prof_measurer_t* measurer, rb_trace_arg_t* trace_arg)
37
+ {
38
+ double measurement = measurer->measure(trace_arg);
39
+ return measurement * measurer->multiplier;
40
+ }
41
+
42
+ /* ======= prof_measurement_t ========*/
43
+ prof_measurement_t* prof_measurement_create(void)
44
+ {
45
+ prof_measurement_t* result = ALLOC(prof_measurement_t);
46
+ result->total_time = 0;
47
+ result->self_time = 0;
48
+ result->wait_time = 0;
49
+ result->called = 0;
50
+ result->object = Qnil;
51
+ return result;
52
+ }
53
+
54
+ void prof_measurement_mark(void* data)
55
+ {
56
+ if (!data) return;
57
+
58
+ prof_measurement_t* measurement_data = (prof_measurement_t*)data;
59
+
60
+ if (measurement_data->object != Qnil)
61
+ rb_gc_mark(measurement_data->object);
62
+ }
63
+
64
+ static void prof_measurement_ruby_gc_free(void* data)
65
+ {
66
+ if (data)
67
+ {
68
+ // Measurements are freed by their owning object (call info or method)
69
+ prof_measurement_t* measurement = (prof_measurement_t*)data;
70
+ measurement->object = Qnil;
71
+ }
72
+ }
73
+
74
+ void prof_measurement_free(prof_measurement_t* measurement)
75
+ {
76
+ /* Has this measurement object been accessed by Ruby? If
77
+ yes clean it up so to avoid a segmentation fault. */
78
+ if (measurement->object != Qnil)
79
+ {
80
+ RTYPEDDATA(measurement->object)->data = NULL;
81
+ measurement->object = Qnil;
82
+ }
83
+
84
+ xfree(measurement);
85
+ }
86
+
87
+ size_t prof_measurement_size(const void* data)
88
+ {
89
+ return sizeof(prof_measurement_t);
90
+ }
91
+
92
+ static const rb_data_type_t measurement_type =
93
+ {
94
+ .wrap_struct_name = "Measurement",
95
+ .function =
96
+ {
97
+ .dmark = prof_measurement_mark,
98
+ .dfree = prof_measurement_ruby_gc_free,
99
+ .dsize = prof_measurement_size,
100
+ },
101
+ .data = NULL,
102
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
103
+ };
104
+
105
+ VALUE prof_measurement_wrap(prof_measurement_t* measurement)
106
+ {
107
+ if (measurement->object == Qnil)
108
+ {
109
+ measurement->object = TypedData_Wrap_Struct(cRpMeasurement, &measurement_type, measurement);
110
+ }
111
+ return measurement->object;
112
+ }
113
+
114
+ static VALUE prof_measurement_allocate(VALUE klass)
115
+ {
116
+ prof_measurement_t* measurement = prof_measurement_create();
117
+ measurement->object = prof_measurement_wrap(measurement);
118
+ return measurement->object;
119
+ }
120
+
121
+ prof_measurement_t* prof_get_measurement(VALUE self)
122
+ {
123
+ /* Can't use Data_Get_Struct because that triggers the event hook
124
+ ending up in endless recursion. */
125
+ prof_measurement_t* result = RTYPEDDATA_DATA(self);
126
+
127
+ if (!result)
128
+ rb_raise(rb_eRuntimeError, "This RubyProf::Measurement instance has already been freed, likely because its profile has been freed.");
129
+
130
+ return result;
131
+ }
132
+
133
+ /* call-seq:
134
+ total_time -> float
135
+
136
+ Returns the total amount of time spent in this method and its children. */
137
+ static VALUE prof_measurement_total_time(VALUE self)
138
+ {
139
+ prof_measurement_t* result = prof_get_measurement(self);
140
+ return rb_float_new(result->total_time);
141
+ }
142
+
143
+ /* call-seq:
144
+ self_time -> float
145
+
146
+ Returns the total amount of time spent in this method. */
147
+ static VALUE
148
+ prof_measurement_self_time(VALUE self)
149
+ {
150
+ prof_measurement_t* result = prof_get_measurement(self);
151
+
152
+ return rb_float_new(result->self_time);
153
+ }
154
+
155
+ /* call-seq:
156
+ wait_time -> float
157
+
158
+ Returns the total amount of time this method waited for other threads. */
159
+ static VALUE prof_measurement_wait_time(VALUE self)
160
+ {
161
+ prof_measurement_t* result = prof_get_measurement(self);
162
+
163
+ return rb_float_new(result->wait_time);
164
+ }
165
+
166
+ /* call-seq:
167
+ called -> int
168
+
169
+ Returns the total amount of times this method was called. */
170
+ static VALUE prof_measurement_called(VALUE self)
171
+ {
172
+ prof_measurement_t* result = prof_get_measurement(self);
173
+ return INT2NUM(result->called);
174
+ }
175
+
176
+ /* call-seq:
177
+ called=n -> n
178
+
179
+ Sets the call count to n. */
180
+ static VALUE prof_measurement_set_called(VALUE self, VALUE called)
181
+ {
182
+ prof_measurement_t* result = prof_get_measurement(self);
183
+ result->called = NUM2INT(called);
184
+ return called;
185
+ }
186
+
187
+ /* :nodoc: */
188
+ static VALUE
189
+ prof_measurement_dump(VALUE self)
190
+ {
191
+ prof_measurement_t* measurement_data = prof_get_measurement(self);
192
+ VALUE result = rb_hash_new();
193
+
194
+ rb_hash_aset(result, ID2SYM(rb_intern("total_time")), rb_float_new(measurement_data->total_time));
195
+ rb_hash_aset(result, ID2SYM(rb_intern("self_time")), rb_float_new(measurement_data->self_time));
196
+ rb_hash_aset(result, ID2SYM(rb_intern("wait_time")), rb_float_new(measurement_data->wait_time));
197
+ rb_hash_aset(result, ID2SYM(rb_intern("called")), INT2FIX(measurement_data->called));
198
+
199
+ return result;
200
+ }
201
+
202
+ /* :nodoc: */
203
+ static VALUE
204
+ prof_measurement_load(VALUE self, VALUE data)
205
+ {
206
+ prof_measurement_t* measurement = prof_get_measurement(self);
207
+ measurement->object = self;
208
+
209
+ measurement->total_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("total_time"))));
210
+ measurement->self_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("self_time"))));
211
+ measurement->wait_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("wait_time"))));
212
+ measurement->called = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("called"))));
213
+
214
+ return data;
215
+ }
216
+
217
+ void rp_init_measure()
218
+ {
219
+ mMeasure = rb_define_module_under(mProf, "Measure");
220
+ rp_init_measure_wall_time();
221
+ rp_init_measure_process_time();
222
+ rp_init_measure_allocations();
223
+ rp_init_measure_memory();
224
+
225
+ cRpMeasurement = rb_define_class_under(mProf, "Measurement", rb_cObject);
226
+ rb_undef_method(CLASS_OF(cRpMeasurement), "new");
227
+ rb_define_alloc_func(cRpMeasurement, prof_measurement_allocate);
228
+
229
+ rb_define_method(cRpMeasurement, "called", prof_measurement_called, 0);
230
+ rb_define_method(cRpMeasurement, "called=", prof_measurement_set_called, 1);
231
+ rb_define_method(cRpMeasurement, "total_time", prof_measurement_total_time, 0);
232
+ rb_define_method(cRpMeasurement, "self_time", prof_measurement_self_time, 0);
233
+ rb_define_method(cRpMeasurement, "wait_time", prof_measurement_wait_time, 0);
234
+
235
+ rb_define_method(cRpMeasurement, "_dump_data", prof_measurement_dump, 0);
236
+ rb_define_method(cRpMeasurement, "_load_data", prof_measurement_load, 1);
237
+ }
@@ -0,0 +1,50 @@
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_measurementMENT_H__
5
+ #define __rp_measurementMENT_H__
6
+
7
+ #include "ruby_prof.h"
8
+
9
+ extern VALUE mMeasure;
10
+
11
+ typedef double (*get_measurement)(rb_trace_arg_t* trace_arg);
12
+
13
+ typedef enum
14
+ {
15
+ MEASURE_WALL_TIME,
16
+ MEASURE_PROCESS_TIME,
17
+ MEASURE_ALLOCATIONS,
18
+ MEASURE_MEMORY
19
+ } prof_measure_mode_t;
20
+
21
+ typedef struct prof_measurer_t
22
+ {
23
+ get_measurement measure;
24
+ prof_measure_mode_t mode;
25
+ double multiplier;
26
+ bool track_allocations;
27
+ } prof_measurer_t;
28
+
29
+ /* Callers and callee information for a method. */
30
+ typedef struct prof_measurement_t
31
+ {
32
+ double total_time;
33
+ double self_time;
34
+ double wait_time;
35
+ int called;
36
+ VALUE object;
37
+ } prof_measurement_t;
38
+
39
+ prof_measurer_t* prof_get_measurer(prof_measure_mode_t measure, bool track_allocations);
40
+ double prof_measure(prof_measurer_t* measurer, rb_trace_arg_t* trace_arg);
41
+
42
+ prof_measurement_t* prof_measurement_create(void);
43
+ void prof_measurement_free(prof_measurement_t* measurement);
44
+ VALUE prof_measurement_wrap(prof_measurement_t* measurement);
45
+ prof_measurement_t* prof_get_measurement(VALUE self);
46
+ void prof_measurement_mark(void* data);
47
+
48
+ void rp_init_measure(void);
49
+
50
+ #endif //__rp_measurementMENT_H__
@@ -1,420 +1,491 @@
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 cMethodInfo;
7
-
8
- /* ================ Helper Functions =================*/
9
- static VALUE
10
- figure_singleton_name(VALUE klass)
11
- {
12
- VALUE result = Qnil;
13
-
14
- /* We have come across a singleton object. First
15
- figure out what it is attached to.*/
16
- VALUE attached = rb_iv_get(klass, "__attached__");
17
-
18
- /* Is this a singleton class acting as a metaclass? */
19
- if (BUILTIN_TYPE(attached) == T_CLASS)
20
- {
21
- result = rb_str_new2("<Class::");
22
- rb_str_append(result, rb_inspect(attached));
23
- rb_str_cat2(result, ">");
24
- }
25
-
26
- /* Is this for singleton methods on a module? */
27
- else if (BUILTIN_TYPE(attached) == T_MODULE)
28
- {
29
- result = rb_str_new2("<Module::");
30
- rb_str_append(result, rb_inspect(attached));
31
- rb_str_cat2(result, ">");
32
- }
33
-
34
- /* Is this for singleton methods on an object? */
35
- else if (BUILTIN_TYPE(attached) == T_OBJECT)
36
- {
37
- /* Make sure to get the super class so that we don't
38
- mistakenly grab a T_ICLASS which would lead to
39
- unknown method errors. */
40
- #ifdef HAVE_RB_CLASS_SUPERCLASS
41
- // 1.9.3
42
- VALUE super = rb_class_superclass(klass);
43
- #else
44
- # ifdef RCLASS_SUPER
45
- VALUE super = rb_class_real(RCLASS_SUPER(klass));
46
- # else
47
- VALUE super = rb_class_real(RCLASS(klass)->super);
48
- # endif
49
- #endif
50
- result = rb_str_new2("<Object::");
51
- rb_str_append(result, rb_inspect(super));
52
- rb_str_cat2(result, ">");
53
- }
54
-
55
- /* Ok, this could be other things like an array made put onto
56
- a singleton object (yeah, it happens, see the singleton
57
- objects test case). */
58
- else
59
- {
60
- result = rb_inspect(klass);
61
- }
62
-
63
- return result;
64
- }
65
-
66
- static VALUE
67
- klass_name(VALUE klass)
68
- {
69
- VALUE result = Qnil;
70
-
71
- if (klass == 0 || klass == Qnil)
72
- {
73
- result = rb_str_new2("Global");
74
- }
75
- else if (BUILTIN_TYPE(klass) == T_MODULE)
76
- {
77
- result = rb_inspect(klass);
78
- }
79
- else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
80
- {
81
- result = figure_singleton_name(klass);
82
- }
83
- else if (BUILTIN_TYPE(klass) == T_CLASS)
84
- {
85
- result = rb_inspect(klass);
86
- }
87
- else
88
- {
89
- /* Should never happen. */
90
- result = rb_str_new2("Unknown");
91
- }
92
-
93
- return result;
94
- }
95
-
96
- static VALUE
97
- method_name(ID mid)
98
- {
99
- VALUE result;
100
-
101
- if (mid == 0)
102
- result = rb_str_new2("[No method]");
103
- #ifdef ID_ALLOCATOR
104
- else if (mid == ID_ALLOCATOR)
105
- result = rb_str_new2("allocate");
106
- #endif
107
- else
108
- result = rb_String(ID2SYM(mid));
109
-
110
- return result;
111
- }
112
-
113
- static VALUE
114
- full_name(VALUE klass, ID mid)
115
- {
116
- VALUE result = klass_name(klass);
117
- rb_str_cat2(result, "#");
118
- rb_str_append(result, method_name(mid));
119
-
120
- return result;
121
- }
122
-
123
- void
124
- method_key(prof_method_key_t* key, VALUE klass, ID mid)
125
- {
126
- /* Is this an include for a module? If so get the actual
127
- module class since we want to combine all profiling
128
- results for that module. */
129
- if (klass != 0)
130
- klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
131
-
132
- key->klass = klass;
133
- key->mid = mid;
134
- key->key = (klass << 4) + (mid << 2);
135
- }
136
-
137
- /* ================ prof_method_t =================*/
138
- prof_method_t*
139
- prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
140
- {
141
- prof_method_t *result = ALLOC(prof_method_t);
142
- result->object = Qnil;
143
- result->call_infos = prof_call_infos_create();
144
-
145
- result->key = ALLOC(prof_method_key_t);
146
- method_key(result->key, klass, mid);
147
-
148
- //result->call_info_table = call_info_table_create();
149
-
150
- if (source_file != NULL)
151
- {
152
- size_t len = strlen(source_file) + 1;
153
- char *buffer = ALLOC_N(char, len);
154
-
155
- MEMCPY(buffer, source_file, char, len);
156
- result->source_file = buffer;
157
- }
158
- else
159
- {
160
- result->source_file = source_file;
161
- }
162
- result->line = line;
163
-
164
- return result;
165
- }
166
-
167
- /* The underlying c structures are freed when the parent profile is freed.
168
- However, on shutdown the Ruby GC frees objects in any will-nilly order.
169
- That means the ruby thread object wrapping the c thread struct may
170
- be freed before the parent profile. Thus we add in a free function
171
- for the garbage collector so that if it does get called will nil
172
- out our Ruby object reference.*/
173
- static void
174
- prof_method_ruby_gc_free(prof_method_t* method)
175
- {
176
- /* Has this thread object been accessed by Ruby? If
177
- yes clean it up so to avoid a segmentation fault. */
178
- if (method->object != Qnil)
179
- {
180
- RDATA(method->object)->data = NULL;
181
- RDATA(method->object)->dfree = NULL;
182
- RDATA(method->object)->dmark = NULL;
183
- }
184
- method->object = Qnil;
185
- }
186
-
187
- static void
188
- prof_method_free(prof_method_t* method)
189
- {
190
- prof_method_ruby_gc_free(method);
191
- prof_call_infos_free(method->call_infos);
192
- xfree(method->call_infos);
193
-
194
- xfree(method->key);
195
- method->key = NULL;
196
-
197
- xfree(method);
198
- }
199
-
200
- void
201
- prof_method_mark(prof_method_t *method)
202
- {
203
- if (method->object)
204
- rb_gc_mark(method->object);
205
-
206
- prof_call_infos_mark(method->call_infos);
207
- }
208
-
209
- VALUE
210
- prof_method_wrap(prof_method_t *result)
211
- {
212
- if (result->object == Qnil)
213
- {
214
- result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_ruby_gc_free, result);
215
- }
216
- return result->object;
217
- }
218
-
219
- static prof_method_t *
220
- get_prof_method(VALUE self)
221
- {
222
- /* Can't use Data_Get_Struct because that triggers the event hook
223
- ending up in endless recursion. */
224
- prof_method_t* result = DATA_PTR(self);
225
-
226
- if (!result)
227
- rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
228
-
229
- return result;
230
- }
231
-
232
- /* ================ Method Table =================*/
233
- int
234
- method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2)
235
- {
236
- return (key1->klass != key2->klass) || (key1->mid != key2->mid);
237
- }
238
-
239
- st_index_t
240
- method_table_hash(prof_method_key_t *key)
241
- {
242
- return key->key;
243
- }
244
-
245
- struct st_hash_type type_method_hash = {
246
- method_table_cmp,
247
- method_table_hash
248
- };
249
-
250
- st_table *
251
- method_table_create()
252
- {
253
- return st_init_table(&type_method_hash);
254
- }
255
-
256
- static int
257
- method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
258
- {
259
- prof_method_free((prof_method_t*)value);
260
- return ST_CONTINUE;
261
- }
262
-
263
- void
264
- method_table_free(st_table *table)
265
- {
266
- st_foreach(table, method_table_free_iterator, 0);
267
- st_free_table(table);
268
- }
269
-
270
-
271
- size_t
272
- method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
273
- {
274
- return st_insert(table, (st_data_t) key, (st_data_t) val);
275
- }
276
-
277
- prof_method_t *
278
- method_table_lookup(st_table *table, const prof_method_key_t* key)
279
- {
280
- st_data_t val;
281
- if (st_lookup(table, (st_data_t)key, &val))
282
- {
283
- return (prof_method_t *) val;
284
- }
285
- else
286
- {
287
- return NULL;
288
- }
289
- }
290
-
291
- /* ================ Method Info =================*/
292
- /* Document-class: RubyProf::MethodInfo
293
- The RubyProf::MethodInfo class stores profiling data for a method.
294
- One instance of the RubyProf::MethodInfo class is created per method
295
- called per thread. Thus, if a method is called in two different
296
- thread then there will be two RubyProf::MethodInfo objects
297
- created. RubyProf::MethodInfo objects can be accessed via
298
- the RubyProf::Result object.
299
- */
300
-
301
- /* call-seq:
302
- line_no -> int
303
-
304
- returns the line number of the method */
305
- static VALUE
306
- prof_method_line(VALUE self)
307
- {
308
- return rb_int_new(get_prof_method(self)->line);
309
- }
310
-
311
- /* call-seq:
312
- source_file => string
313
-
314
- return the source file of the method
315
- */
316
- static VALUE prof_method_source_file(VALUE self)
317
- {
318
- const char* sf = get_prof_method(self)->source_file;
319
- if(!sf)
320
- {
321
- return rb_str_new2("ruby_runtime");
322
- }
323
- else
324
- {
325
- return rb_str_new2(sf);
326
- }
327
- }
328
-
329
-
330
- /* call-seq:
331
- method_class -> klass
332
-
333
- Returns the Ruby klass that owns this method. */
334
- static VALUE
335
- prof_method_klass(VALUE self)
336
- {
337
- prof_method_t *result = get_prof_method(self);
338
- return result->key->klass;
339
- }
340
-
341
- /* call-seq:
342
- method_id -> ID
343
-
344
- Returns the id of this method. */
345
- static VALUE
346
- prof_method_id(VALUE self)
347
- {
348
- prof_method_t *result = get_prof_method(self);
349
- return ID2SYM(result->key->mid);
350
- }
351
-
352
- /* call-seq:
353
- klass_name -> string
354
-
355
- Returns the name of this method's class. Singleton classes
356
- will have the form <Object::Object>. */
357
-
358
- static VALUE
359
- prof_klass_name(VALUE self)
360
- {
361
- prof_method_t *method = get_prof_method(self);
362
- return klass_name(method->key->klass);
363
- }
364
-
365
- /* call-seq:
366
- method_name -> string
367
-
368
- Returns the name of this method in the format Object#method. Singletons
369
- methods will be returned in the format <Object::Object>#method.*/
370
-
371
- static VALUE
372
- prof_method_name(VALUE self)
373
- {
374
- prof_method_t *method = get_prof_method(self);
375
- return method_name(method->key->mid);
376
- }
377
-
378
- /* call-seq:
379
- full_name -> string
380
-
381
- Returns the full name of this method in the format Object#method.*/
382
-
383
- static VALUE
384
- prof_full_name(VALUE self)
385
- {
386
- prof_method_t *method = get_prof_method(self);
387
- return full_name(method->key->klass, method->key->mid);
388
- }
389
-
390
- /* call-seq:
391
- call_infos -> Array of call_info
392
-
393
- Returns an array of call info objects that contain profiling information
394
- about the current method.*/
395
- static VALUE
396
- prof_method_call_infos(VALUE self)
397
- {
398
- prof_method_t *method = get_prof_method(self);
399
- if (method->call_infos->object == Qnil)
400
- {
401
- method->call_infos->object = prof_call_infos_wrap(method->call_infos);
402
- }
403
- return method->call_infos->object;
404
- }
405
-
406
- void rp_init_method_info()
407
- {
408
- /* MethodInfo */
409
- cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
410
- rb_undef_method(CLASS_OF(cMethodInfo), "new");
411
-
412
- rb_define_method(cMethodInfo, "klass", prof_method_klass, 0);
413
- rb_define_method(cMethodInfo, "klass_name", prof_klass_name, 0);
414
- rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
415
- rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
416
- rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
417
- rb_define_method(cMethodInfo, "source_file", prof_method_source_file,0);
418
- rb_define_method(cMethodInfo, "line", prof_method_line, 0);
419
- rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
420
- }
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
+ #include "rp_allocation.h"
5
+ #include "rp_call_trees.h"
6
+ #include "rp_method.h"
7
+
8
+ VALUE cRpMethodInfo;
9
+
10
+ /* ================ Helper Functions =================*/
11
+ VALUE resolve_klass(VALUE klass, unsigned int* klass_flags)
12
+ {
13
+ VALUE result = klass;
14
+
15
+ if (klass == 0 || klass == Qnil)
16
+ {
17
+ result = Qnil;
18
+ }
19
+ else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
20
+ {
21
+ /* We have come across a singleton object. First
22
+ figure out what it is attached to.*/
23
+ VALUE attached = rb_iv_get(klass, "__attached__");
24
+
25
+ /* Is this a singleton class acting as a metaclass? */
26
+ if (BUILTIN_TYPE(attached) == T_CLASS)
27
+ {
28
+ *klass_flags |= kClassSingleton;
29
+ result = attached;
30
+ }
31
+ /* Is this for singleton methods on a module? */
32
+ else if (BUILTIN_TYPE(attached) == T_MODULE)
33
+ {
34
+ *klass_flags |= kModuleSingleton;
35
+ result = attached;
36
+ }
37
+ /* Is this for singleton methods on an object? */
38
+ else if (BUILTIN_TYPE(attached) == T_OBJECT)
39
+ {
40
+ *klass_flags |= kObjectSingleton;
41
+ result = rb_class_superclass(klass);
42
+ }
43
+ /* Ok, this could be other things like an array made put onto
44
+ a singleton object (yeah, it happens, see the singleton
45
+ objects test case). */
46
+ else
47
+ {
48
+ *klass_flags |= kOtherSingleton;
49
+ result = klass;
50
+ }
51
+ }
52
+ /* Is this an include for a module? If so get the actual
53
+ module class since we want to combine all profiling
54
+ results for that module. */
55
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
56
+ {
57
+ unsigned int dummy;
58
+ *klass_flags |= kModuleIncludee;
59
+ result = resolve_klass(RBASIC(klass)->klass, &dummy);
60
+ }
61
+ return result;
62
+ }
63
+
64
+ VALUE resolve_klass_name(VALUE klass, unsigned int* klass_flags)
65
+ {
66
+ VALUE result = Qnil;
67
+
68
+ if (klass == Qnil)
69
+ {
70
+ result = rb_str_new2("[global]");
71
+ }
72
+ else if (*klass_flags & kOtherSingleton)
73
+ {
74
+ result = rb_any_to_s(klass);
75
+ }
76
+ else
77
+ {
78
+ result = rb_class_name(klass);
79
+ }
80
+
81
+ return result;
82
+ }
83
+
84
+ st_data_t method_key(VALUE klass, VALUE msym)
85
+ {
86
+ VALUE resolved_klass = klass;
87
+
88
+ /* Is this an include for a module? If so get the actual
89
+ module class since we want to combine all profiling
90
+ results for that module. */
91
+ if (klass == 0 || klass == Qnil)
92
+ {
93
+ resolved_klass = Qnil;
94
+ }
95
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
96
+ {
97
+ resolved_klass = RBASIC(klass)->klass;
98
+ }
99
+
100
+ return (resolved_klass << 4) + (msym);
101
+ }
102
+
103
+ /* ====== Allocation Table ====== */
104
+ st_table* allocations_table_create()
105
+ {
106
+ return rb_st_init_numtable();
107
+ }
108
+
109
+ static int allocations_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
110
+ {
111
+ prof_allocation_free((prof_allocation_t*)value);
112
+ return ST_CONTINUE;
113
+ }
114
+
115
+ static int prof_method_collect_allocations(st_data_t key, st_data_t value, st_data_t result)
116
+ {
117
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
118
+ VALUE arr = (VALUE)result;
119
+ rb_ary_push(arr, prof_allocation_wrap(allocation));
120
+ return ST_CONTINUE;
121
+ }
122
+
123
+ static int prof_method_mark_allocations(st_data_t key, st_data_t value, st_data_t data)
124
+ {
125
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
126
+ prof_allocation_mark(allocation);
127
+ return ST_CONTINUE;
128
+ }
129
+
130
+ void allocations_table_free(st_table* table)
131
+ {
132
+ rb_st_foreach(table, allocations_table_free_iterator, 0);
133
+ rb_st_free_table(table);
134
+ }
135
+
136
+ /* ================ prof_method_t =================*/
137
+ prof_method_t* prof_get_method(VALUE self)
138
+ {
139
+ /* Can't use Data_Get_Struct because that triggers the event hook
140
+ ending up in endless recursion. */
141
+ prof_method_t* result = RTYPEDDATA_DATA(self);
142
+
143
+ if (!result)
144
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
145
+
146
+ return result;
147
+ }
148
+
149
+ prof_method_t* prof_method_create(VALUE profile, VALUE klass, VALUE msym, VALUE source_file, int source_line)
150
+ {
151
+ prof_method_t* result = ALLOC(prof_method_t);
152
+ result->profile = profile;
153
+
154
+ result->key = method_key(klass, msym);
155
+ result->klass_flags = 0;
156
+
157
+ /* Note we do not call resolve_klass_name now because that causes an object allocation that shows up
158
+ in the allocation results so we want to avoid it until after the profile run is complete. */
159
+ result->klass = resolve_klass(klass, &result->klass_flags);
160
+ result->klass_name = Qnil;
161
+ result->method_name = msym;
162
+ result->measurement = prof_measurement_create();
163
+
164
+ result->call_trees = prof_call_trees_create();
165
+ result->allocations_table = allocations_table_create();
166
+
167
+ result->visits = 0;
168
+ result->recursive = false;
169
+
170
+ result->object = Qnil;
171
+
172
+ result->source_file = source_file;
173
+ result->source_line = source_line;
174
+
175
+ return result;
176
+ }
177
+
178
+ /* The underlying c structures are freed when the parent profile is freed.
179
+ However, on shutdown the Ruby GC frees objects in any will-nilly order.
180
+ That means the ruby thread object wrapping the c thread struct may
181
+ be freed before the parent profile. Thus we add in a free function
182
+ for the garbage collector so that if it does get called will nil
183
+ out our Ruby object reference.*/
184
+ static void prof_method_ruby_gc_free(void* data)
185
+ {
186
+ if (data)
187
+ {
188
+ prof_method_t* method = (prof_method_t*)data;
189
+ method->object = Qnil;
190
+ }
191
+ }
192
+
193
+ static void prof_method_free(prof_method_t* method)
194
+ {
195
+ /* Has this method object been accessed by Ruby? If
196
+ yes clean it up so to avoid a segmentation fault. */
197
+ if (method->object != Qnil)
198
+ {
199
+ RTYPEDDATA(method->object)->data = NULL;
200
+ method->object = Qnil;
201
+ }
202
+
203
+ allocations_table_free(method->allocations_table);
204
+ prof_call_trees_free(method->call_trees);
205
+ prof_measurement_free(method->measurement);
206
+ xfree(method);
207
+ }
208
+
209
+ size_t prof_method_size(const void* data)
210
+ {
211
+ return sizeof(prof_method_t);
212
+ }
213
+
214
+ void prof_method_mark(void* data)
215
+ {
216
+ if (!data) return;
217
+
218
+ prof_method_t* method = (prof_method_t*)data;
219
+
220
+ if (method->profile != Qnil)
221
+ rb_gc_mark(method->profile);
222
+
223
+ if (method->object != Qnil)
224
+ rb_gc_mark(method->object);
225
+
226
+ rb_gc_mark(method->klass_name);
227
+ rb_gc_mark(method->method_name);
228
+ rb_gc_mark(method->source_file);
229
+
230
+ if (method->klass != Qnil)
231
+ rb_gc_mark(method->klass);
232
+
233
+ prof_measurement_mark(method->measurement);
234
+
235
+ rb_st_foreach(method->allocations_table, prof_method_mark_allocations, 0);
236
+ }
237
+
238
+ static VALUE prof_method_allocate(VALUE klass)
239
+ {
240
+ prof_method_t* method_data = prof_method_create(Qnil, Qnil, Qnil, Qnil, 0);
241
+ method_data->object = prof_method_wrap(method_data);
242
+ return method_data->object;
243
+ }
244
+
245
+ static const rb_data_type_t method_info_type =
246
+ {
247
+ .wrap_struct_name = "MethodInfo",
248
+ .function =
249
+ {
250
+ .dmark = prof_method_mark,
251
+ .dfree = prof_method_ruby_gc_free,
252
+ .dsize = prof_method_size,
253
+ },
254
+ .data = NULL,
255
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
256
+ };
257
+
258
+ VALUE prof_method_wrap(prof_method_t* method)
259
+ {
260
+ if (method->object == Qnil)
261
+ {
262
+ method->object = TypedData_Wrap_Struct(cRpMethodInfo, &method_info_type, method);
263
+ }
264
+ return method->object;
265
+ }
266
+
267
+ st_table* method_table_create()
268
+ {
269
+ return rb_st_init_numtable();
270
+ }
271
+
272
+ static int method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
273
+ {
274
+ prof_method_free((prof_method_t*)value);
275
+ return ST_CONTINUE;
276
+ }
277
+
278
+ void method_table_free(st_table* table)
279
+ {
280
+ rb_st_foreach(table, method_table_free_iterator, 0);
281
+ rb_st_free_table(table);
282
+ }
283
+
284
+ size_t method_table_insert(st_table* table, st_data_t key, prof_method_t* val)
285
+ {
286
+ return rb_st_insert(table, (st_data_t)key, (st_data_t)val);
287
+ }
288
+
289
+ prof_method_t* method_table_lookup(st_table* table, st_data_t key)
290
+ {
291
+ st_data_t val;
292
+ if (rb_st_lookup(table, (st_data_t)key, &val))
293
+ {
294
+ return (prof_method_t*)val;
295
+ }
296
+ else
297
+ {
298
+ return NULL;
299
+ }
300
+ }
301
+
302
+ /* ================ Method Info =================*/
303
+ /* Document-class: RubyProf::MethodInfo
304
+ The RubyProf::MethodInfo class stores profiling data for a method.
305
+ One instance of the RubyProf::MethodInfo class is created per method
306
+ called per thread. Thus, if a method is called in two different
307
+ thread then there will be two RubyProf::MethodInfo objects
308
+ created. RubyProf::MethodInfo objects can be accessed via
309
+ the RubyProf::Profile object.
310
+ */
311
+
312
+ /* call-seq:
313
+ allocations -> array
314
+
315
+ Returns an array of allocation information.*/
316
+ static VALUE prof_method_allocations(VALUE self)
317
+ {
318
+ prof_method_t* method = prof_get_method(self);
319
+ VALUE result = rb_ary_new();
320
+ rb_st_foreach(method->allocations_table, prof_method_collect_allocations, result);
321
+ return result;
322
+ }
323
+
324
+ /* call-seq:
325
+ called -> Measurement
326
+
327
+ Returns the measurement associated with this method. */
328
+ static VALUE prof_method_measurement(VALUE self)
329
+ {
330
+ prof_method_t* method = prof_get_method(self);
331
+ return prof_measurement_wrap(method->measurement);
332
+ }
333
+
334
+ /* call-seq:
335
+ source_file => string
336
+
337
+ return the source file of the method
338
+ */
339
+ static VALUE prof_method_source_file(VALUE self)
340
+ {
341
+ prof_method_t* method = prof_get_method(self);
342
+ return method->source_file;
343
+ }
344
+
345
+ /* call-seq:
346
+ line_no -> int
347
+
348
+ returns the line number of the method */
349
+ static VALUE prof_method_line(VALUE self)
350
+ {
351
+ prof_method_t* method = prof_get_method(self);
352
+ return INT2FIX(method->source_line);
353
+ }
354
+
355
+ /* call-seq:
356
+ klass_name -> string
357
+
358
+ Returns the name of this method's class. Singleton classes
359
+ will have the form <Object::Object>. */
360
+
361
+ static VALUE prof_method_klass_name(VALUE self)
362
+ {
363
+ prof_method_t* method = prof_get_method(self);
364
+ if (method->klass_name == Qnil)
365
+ method->klass_name = resolve_klass_name(method->klass, &method->klass_flags);
366
+
367
+ return method->klass_name;
368
+ }
369
+
370
+ /* call-seq:
371
+ klass_flags -> integer
372
+
373
+ Returns the klass flags */
374
+
375
+ static VALUE prof_method_klass_flags(VALUE self)
376
+ {
377
+ prof_method_t* method = prof_get_method(self);
378
+ return INT2FIX(method->klass_flags);
379
+ }
380
+
381
+ /* call-seq:
382
+ method_name -> string
383
+
384
+ Returns the name of this method in the format Object#method. Singletons
385
+ methods will be returned in the format <Object::Object>#method.*/
386
+
387
+ static VALUE prof_method_name(VALUE self)
388
+ {
389
+ prof_method_t* method = prof_get_method(self);
390
+ return method->method_name;
391
+ }
392
+
393
+ /* call-seq:
394
+ recursive? -> boolean
395
+
396
+ Returns the true if this method is recursively invoked */
397
+ static VALUE prof_method_recursive(VALUE self)
398
+ {
399
+ prof_method_t* method = prof_get_method(self);
400
+ return method->recursive ? Qtrue : Qfalse;
401
+ }
402
+
403
+ /* call-seq:
404
+ call_trees -> CallTrees
405
+
406
+ Returns the CallTrees associated with this method. */
407
+ static VALUE prof_method_call_trees(VALUE self)
408
+ {
409
+ prof_method_t* method = prof_get_method(self);
410
+ return prof_call_trees_wrap(method->call_trees);
411
+ }
412
+
413
+ /* :nodoc: */
414
+ static VALUE prof_method_dump(VALUE self)
415
+ {
416
+ prof_method_t* method_data = prof_get_method(self);
417
+ VALUE result = rb_hash_new();
418
+
419
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_method_klass_name(self));
420
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(method_data->klass_flags));
421
+ rb_hash_aset(result, ID2SYM(rb_intern("method_name")), method_data->method_name);
422
+
423
+ rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(method_data->key));
424
+ rb_hash_aset(result, ID2SYM(rb_intern("recursive")), prof_method_recursive(self));
425
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), method_data->source_file);
426
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(method_data->source_line));
427
+
428
+ rb_hash_aset(result, ID2SYM(rb_intern("call_trees")), prof_call_trees_wrap(method_data->call_trees));
429
+ rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(method_data->measurement));
430
+ rb_hash_aset(result, ID2SYM(rb_intern("allocations")), prof_method_allocations(self));
431
+
432
+ return result;
433
+ }
434
+
435
+ /* :nodoc: */
436
+ static VALUE prof_method_load(VALUE self, VALUE data)
437
+ {
438
+ prof_method_t* method_data = prof_get_method(self);
439
+ method_data->object = self;
440
+
441
+ method_data->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
442
+ method_data->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
443
+ method_data->method_name = rb_hash_aref(data, ID2SYM(rb_intern("method_name")));
444
+ method_data->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
445
+
446
+ method_data->recursive = rb_hash_aref(data, ID2SYM(rb_intern("recursive"))) == Qtrue ? true : false;
447
+
448
+ method_data->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
449
+ method_data->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
450
+
451
+ VALUE call_trees = rb_hash_aref(data, ID2SYM(rb_intern("call_trees")));
452
+ method_data->call_trees = prof_get_call_trees(call_trees);
453
+
454
+ VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
455
+ method_data->measurement = prof_get_measurement(measurement);
456
+
457
+ VALUE allocations = rb_hash_aref(data, ID2SYM(rb_intern("allocations")));
458
+ for (int i = 0; i < rb_array_len(allocations); i++)
459
+ {
460
+ VALUE allocation = rb_ary_entry(allocations, i);
461
+ prof_allocation_t* allocation_data = prof_allocation_get(allocation);
462
+
463
+ rb_st_insert(method_data->allocations_table, allocation_data->key, (st_data_t)allocation_data);
464
+ }
465
+ return data;
466
+ }
467
+
468
+ void rp_init_method_info()
469
+ {
470
+ /* MethodInfo */
471
+ cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
472
+ rb_undef_method(CLASS_OF(cRpMethodInfo), "new");
473
+ rb_define_alloc_func(cRpMethodInfo, prof_method_allocate);
474
+
475
+ rb_define_method(cRpMethodInfo, "klass_name", prof_method_klass_name, 0);
476
+ rb_define_method(cRpMethodInfo, "klass_flags", prof_method_klass_flags, 0);
477
+ rb_define_method(cRpMethodInfo, "method_name", prof_method_name, 0);
478
+
479
+ rb_define_method(cRpMethodInfo, "call_trees", prof_method_call_trees, 0);
480
+
481
+ rb_define_method(cRpMethodInfo, "allocations", prof_method_allocations, 0);
482
+ rb_define_method(cRpMethodInfo, "measurement", prof_method_measurement, 0);
483
+
484
+ rb_define_method(cRpMethodInfo, "source_file", prof_method_source_file, 0);
485
+ rb_define_method(cRpMethodInfo, "line", prof_method_line, 0);
486
+
487
+ rb_define_method(cRpMethodInfo, "recursive?", prof_method_recursive, 0);
488
+
489
+ rb_define_method(cRpMethodInfo, "_dump_data", prof_method_dump, 0);
490
+ rb_define_method(cRpMethodInfo, "_load_data", prof_method_load, 1);
491
+ }