ruby-prof 0.16.2 → 1.1.0

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 (203) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +532 -467
  3. data/LICENSE +24 -24
  4. data/README.rdoc +5 -454
  5. data/Rakefile +110 -113
  6. data/bin/ruby-prof +380 -340
  7. data/bin/ruby-prof-check-trace +45 -45
  8. data/ext/ruby_prof/extconf.rb +36 -64
  9. data/ext/ruby_prof/rp_allocation.c +279 -0
  10. data/ext/ruby_prof/rp_allocation.h +31 -0
  11. data/ext/ruby_prof/rp_call_info.c +271 -407
  12. data/ext/ruby_prof/rp_call_info.h +35 -48
  13. data/ext/ruby_prof/rp_measure_allocations.c +52 -76
  14. data/ext/ruby_prof/rp_measure_memory.c +42 -77
  15. data/ext/ruby_prof/rp_measure_process_time.c +67 -71
  16. data/ext/ruby_prof/rp_measure_wall_time.c +62 -45
  17. data/ext/ruby_prof/rp_measurement.c +230 -0
  18. data/ext/ruby_prof/rp_measurement.h +50 -0
  19. data/ext/ruby_prof/rp_method.c +630 -411
  20. data/ext/ruby_prof/rp_method.h +70 -52
  21. data/ext/ruby_prof/rp_profile.c +895 -0
  22. data/ext/ruby_prof/rp_profile.h +37 -0
  23. data/ext/ruby_prof/rp_stack.c +196 -128
  24. data/ext/ruby_prof/rp_stack.h +56 -51
  25. data/ext/ruby_prof/rp_thread.c +337 -273
  26. data/ext/ruby_prof/rp_thread.h +36 -27
  27. data/ext/ruby_prof/ruby_prof.c +48 -671
  28. data/ext/ruby_prof/ruby_prof.h +17 -56
  29. data/ext/ruby_prof/vc/ruby_prof.sln +20 -21
  30. data/ext/ruby_prof/vc/{ruby_prof_20.vcxproj → ruby_prof.vcxproj} +38 -5
  31. data/lib/ruby-prof.rb +52 -58
  32. data/lib/ruby-prof/assets/call_stack_printer.html.erb +713 -0
  33. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  34. data/lib/ruby-prof/assets/graph_printer.html.erb +356 -0
  35. data/lib/ruby-prof/call_info.rb +57 -126
  36. data/lib/ruby-prof/call_info_visitor.rb +38 -40
  37. data/lib/ruby-prof/compatibility.rb +109 -178
  38. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  39. data/lib/ruby-prof/measurement.rb +14 -0
  40. data/lib/ruby-prof/method_info.rb +90 -129
  41. data/lib/ruby-prof/printers/abstract_printer.rb +127 -85
  42. data/lib/ruby-prof/printers/call_info_printer.rb +51 -41
  43. data/lib/ruby-prof/printers/call_stack_printer.rb +182 -260
  44. data/lib/ruby-prof/printers/call_tree_printer.rb +151 -130
  45. data/lib/ruby-prof/printers/dot_printer.rb +132 -132
  46. data/lib/ruby-prof/printers/flat_printer.rb +52 -70
  47. data/lib/ruby-prof/printers/graph_html_printer.rb +63 -244
  48. data/lib/ruby-prof/printers/graph_printer.rb +114 -116
  49. data/lib/ruby-prof/printers/multi_printer.rb +127 -58
  50. data/lib/ruby-prof/profile.rb +33 -55
  51. data/lib/ruby-prof/rack.rb +171 -95
  52. data/lib/ruby-prof/task.rb +147 -147
  53. data/lib/ruby-prof/thread.rb +35 -41
  54. data/lib/ruby-prof/version.rb +3 -3
  55. data/lib/unprof.rb +10 -10
  56. data/ruby-prof.gemspec +58 -57
  57. data/test/abstract_printer_test.rb +26 -0
  58. data/test/alias_test.rb +129 -0
  59. data/test/basic_test.rb +129 -128
  60. data/test/call_info_visitor_test.rb +31 -31
  61. data/test/duplicate_names_test.rb +32 -32
  62. data/test/dynamic_method_test.rb +53 -55
  63. data/test/enumerable_test.rb +21 -21
  64. data/test/exceptions_test.rb +24 -16
  65. data/test/exclude_methods_test.rb +146 -0
  66. data/test/exclude_threads_test.rb +53 -53
  67. data/test/fiber_test.rb +73 -79
  68. data/test/gc_test.rb +96 -0
  69. data/test/line_number_test.rb +161 -71
  70. data/test/marshal_test.rb +119 -0
  71. data/test/measure_allocations.rb +30 -0
  72. data/test/measure_allocations_test.rb +385 -26
  73. data/test/measure_allocations_trace_test.rb +385 -0
  74. data/test/measure_memory_trace_test.rb +756 -0
  75. data/test/measure_process_time_test.rb +849 -63
  76. data/test/measure_times.rb +54 -0
  77. data/test/measure_wall_time_test.rb +459 -255
  78. data/test/multi_printer_test.rb +71 -83
  79. data/test/no_method_class_test.rb +15 -15
  80. data/test/parser_timings.rb +24 -0
  81. data/test/pause_resume_test.rb +166 -166
  82. data/test/prime.rb +56 -54
  83. data/test/printer_call_stack_test.rb +28 -0
  84. data/test/printer_call_tree_test.rb +31 -0
  85. data/test/printer_flat_test.rb +68 -0
  86. data/test/printer_graph_html_test.rb +60 -0
  87. data/test/printer_graph_test.rb +41 -0
  88. data/test/printers_test.rb +141 -255
  89. data/test/printing_recursive_graph_test.rb +81 -127
  90. data/test/rack_test.rb +157 -93
  91. data/test/recursive_test.rb +210 -215
  92. data/test/singleton_test.rb +38 -38
  93. data/test/stack_printer_test.rb +64 -78
  94. data/test/start_stop_test.rb +109 -112
  95. data/test/test_helper.rb +24 -264
  96. data/test/thread_test.rb +144 -187
  97. data/test/unique_call_path_test.rb +190 -202
  98. data/test/yarv_test.rb +56 -55
  99. metadata +34 -114
  100. data/doc/LICENSE.html +0 -114
  101. data/doc/README_rdoc.html +0 -603
  102. data/doc/Rack.html +0 -95
  103. data/doc/Rack/RubyProf.html +0 -226
  104. data/doc/RubyProf.html +0 -962
  105. data/doc/RubyProf/AbstractPrinter.html +0 -546
  106. data/doc/RubyProf/AggregateCallInfo.html +0 -551
  107. data/doc/RubyProf/CallInfo.html +0 -639
  108. data/doc/RubyProf/CallInfoPrinter.html +0 -120
  109. data/doc/RubyProf/CallInfoVisitor.html +0 -198
  110. data/doc/RubyProf/CallStackPrinter.html +0 -1121
  111. data/doc/RubyProf/CallTreePrinter.html +0 -641
  112. data/doc/RubyProf/Cmd.html +0 -631
  113. data/doc/RubyProf/DotPrinter.html +0 -257
  114. data/doc/RubyProf/FlatPrinter.html +0 -163
  115. data/doc/RubyProf/FlatPrinterWithLineNumbers.html +0 -208
  116. data/doc/RubyProf/GraphHtmlPrinter.html +0 -552
  117. data/doc/RubyProf/GraphPrinter.html +0 -139
  118. data/doc/RubyProf/MethodInfo.html +0 -745
  119. data/doc/RubyProf/MultiPrinter.html +0 -360
  120. data/doc/RubyProf/Profile.html +0 -763
  121. data/doc/RubyProf/ProfileTask.html +0 -490
  122. data/doc/RubyProf/Thread.html +0 -310
  123. data/doc/created.rid +0 -31
  124. data/doc/css/fonts.css +0 -167
  125. data/doc/css/rdoc.css +0 -590
  126. data/doc/examples/flat_txt.html +0 -138
  127. data/doc/examples/graph_html.html +0 -909
  128. data/doc/examples/graph_txt.html +0 -247
  129. data/doc/fonts/Lato-Light.ttf +0 -0
  130. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  131. data/doc/fonts/Lato-Regular.ttf +0 -0
  132. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  133. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  134. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  135. data/doc/images/add.png +0 -0
  136. data/doc/images/arrow_up.png +0 -0
  137. data/doc/images/brick.png +0 -0
  138. data/doc/images/brick_link.png +0 -0
  139. data/doc/images/bug.png +0 -0
  140. data/doc/images/bullet_black.png +0 -0
  141. data/doc/images/bullet_toggle_minus.png +0 -0
  142. data/doc/images/bullet_toggle_plus.png +0 -0
  143. data/doc/images/date.png +0 -0
  144. data/doc/images/delete.png +0 -0
  145. data/doc/images/find.png +0 -0
  146. data/doc/images/loadingAnimation.gif +0 -0
  147. data/doc/images/macFFBgHack.png +0 -0
  148. data/doc/images/package.png +0 -0
  149. data/doc/images/page_green.png +0 -0
  150. data/doc/images/page_white_text.png +0 -0
  151. data/doc/images/page_white_width.png +0 -0
  152. data/doc/images/plugin.png +0 -0
  153. data/doc/images/ruby.png +0 -0
  154. data/doc/images/tag_blue.png +0 -0
  155. data/doc/images/tag_green.png +0 -0
  156. data/doc/images/transparent.png +0 -0
  157. data/doc/images/wrench.png +0 -0
  158. data/doc/images/wrench_orange.png +0 -0
  159. data/doc/images/zoom.png +0 -0
  160. data/doc/index.html +0 -626
  161. data/doc/js/darkfish.js +0 -161
  162. data/doc/js/jquery.js +0 -4
  163. data/doc/js/navigation.js +0 -142
  164. data/doc/js/navigation.js.gz +0 -0
  165. data/doc/js/search.js +0 -109
  166. data/doc/js/search_index.js +0 -1
  167. data/doc/js/search_index.js.gz +0 -0
  168. data/doc/js/searcher.js +0 -228
  169. data/doc/js/searcher.js.gz +0 -0
  170. data/doc/table_of_contents.html +0 -942
  171. data/examples/cachegrind.out.1 +0 -114
  172. data/examples/cachegrind.out.1.32313213 +0 -114
  173. data/examples/flat.txt +0 -50
  174. data/examples/graph.dot +0 -84
  175. data/examples/graph.html +0 -823
  176. data/examples/graph.txt +0 -139
  177. data/examples/multi.flat.txt +0 -23
  178. data/examples/multi.graph.html +0 -760
  179. data/examples/multi.grind.dat +0 -114
  180. data/examples/multi.stack.html +0 -547
  181. data/examples/stack.html +0 -547
  182. data/ext/ruby_prof/rp_measure.c +0 -40
  183. data/ext/ruby_prof/rp_measure.h +0 -45
  184. data/ext/ruby_prof/rp_measure_cpu_time.c +0 -136
  185. data/ext/ruby_prof/rp_measure_gc_runs.c +0 -73
  186. data/ext/ruby_prof/rp_measure_gc_time.c +0 -60
  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/lib/ruby-prof/aggregate_call_info.rb +0 -76
  190. data/lib/ruby-prof/assets/call_stack_printer.css.html +0 -117
  191. data/lib/ruby-prof/assets/call_stack_printer.js.html +0 -385
  192. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +0 -64
  193. data/test/aggregate_test.rb +0 -136
  194. data/test/block_test.rb +0 -74
  195. data/test/call_info_test.rb +0 -78
  196. data/test/issue137_test.rb +0 -63
  197. data/test/measure_cpu_time_test.rb +0 -213
  198. data/test/measure_gc_runs_test.rb +0 -32
  199. data/test/measure_gc_time_test.rb +0 -36
  200. data/test/measure_memory_test.rb +0 -33
  201. data/test/method_elimination_test.rb +0 -84
  202. data/test/module_test.rb +0 -45
  203. data/test/stack_test.rb +0 -138
@@ -1,45 +1,62 @@
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
- /* :nodoc: */
5
- #include "ruby_prof.h"
6
- #if HAVE_GETTIMEOFDAY && !defined(_WIN32)
7
- #include <sys/time.h>
8
- #endif
9
-
10
- static VALUE cMeasureWallTime;
11
-
12
- static double
13
- measure_wall_time()
14
- {
15
- struct timeval tv;
16
- gettimeofday(&tv, NULL);
17
- return tv.tv_sec + (tv.tv_usec/1000000.0);
18
- }
19
-
20
- prof_measurer_t* prof_measurer_wall_time()
21
- {
22
- prof_measurer_t* measure = ALLOC(prof_measurer_t);
23
- measure->measure = measure_wall_time;
24
- return measure;
25
- }
26
-
27
- /* Document-method: prof_measure_wall_time
28
- call-seq:
29
- measure_wall_time -> float
30
-
31
- Returns the wall time.*/
32
- static VALUE
33
- prof_measure_wall_time(VALUE self)
34
- {
35
- return rb_float_new(measure_wall_time());
36
- }
37
-
38
- void rp_init_measure_wall_time()
39
- {
40
- rb_define_const(mProf, "WALL_TIME", INT2NUM(MEASURE_WALL_TIME));
41
- rb_define_const(mProf, "WALL_TIME_ENABLED", Qtrue);
42
-
43
- cMeasureWallTime = rb_define_class_under(mMeasure, "WallTime", rb_cObject);
44
- rb_define_singleton_method(cMeasureWallTime, "measure", prof_measure_wall_time, 0);
45
- }
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
+ /* :nodoc: */
5
+ #include "rp_measurement.h"
6
+
7
+ #if defined(__APPLE__)
8
+ #include <mach/mach_time.h>
9
+ #elif !defined(_WIN32)
10
+ #include <time.h>
11
+ #endif
12
+
13
+ static VALUE cMeasureWallTime;
14
+
15
+ static double
16
+ measure_wall_time(rb_trace_arg_t* trace_arg)
17
+ {
18
+ #if defined(_WIN32)
19
+ return GetTickCount();
20
+ #elif defined(__APPLE__)
21
+ return mach_absolute_time();// * (uint64_t)mach_timebase.numer / (uint64_t)mach_timebase.denom;
22
+ #elif defined(__linux__)
23
+ struct timespec tv;
24
+ clock_gettime(CLOCK_MONOTONIC, &tv);
25
+ return tv.tv_sec + (tv.tv_nsec / 1000000000.0);
26
+ #else
27
+ struct timeval tv;
28
+ gettimeofday(&tv, NULL);
29
+ return tv.tv_sec + (tv.tv_usec / 1000000.0);
30
+ #endif
31
+ }
32
+
33
+ static double
34
+ multiplier_wall_time(void)
35
+ {
36
+ #if defined(_WIN32)
37
+ return 1.0/1000.0;
38
+ #elif defined(__APPLE__)
39
+ mach_timebase_info_data_t mach_timebase;
40
+ mach_timebase_info (&mach_timebase);
41
+ return (uint64_t)mach_timebase.numer / (uint64_t)mach_timebase.denom / 1000000000.0;
42
+ #else
43
+ return 1.0;
44
+ #endif
45
+ }
46
+
47
+ prof_measurer_t* prof_measurer_wall_time(bool track_allocations)
48
+ {
49
+ prof_measurer_t* measure = ALLOC(prof_measurer_t);
50
+ measure->mode = MEASURE_WALL_TIME;
51
+ measure->measure = measure_wall_time;
52
+ measure->multiplier = multiplier_wall_time();
53
+ measure->track_allocations = track_allocations;
54
+ return measure;
55
+ }
56
+
57
+ void rp_init_measure_wall_time()
58
+ {
59
+ rb_define_const(mProf, "WALL_TIME", INT2NUM(MEASURE_WALL_TIME));
60
+
61
+ cMeasureWallTime = rb_define_class_under(mMeasure, "WallTime", rb_cObject);
62
+ }
@@ -0,0 +1,230 @@
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
+ static void
55
+ prof_measurement_ruby_gc_free(void *data)
56
+ {
57
+ prof_measurement_t* measurement = (prof_measurement_t*)data;
58
+
59
+ /* Has this measurement object been accessed by Ruby? If
60
+ yes clean it up so to avoid a segmentation fault. */
61
+ if (measurement->object != Qnil)
62
+ {
63
+ RDATA(measurement->object)->dmark = NULL;
64
+ RDATA(measurement->object)->dfree = NULL;
65
+ RDATA(measurement->object)->data = NULL;
66
+ measurement->object = Qnil;
67
+ }
68
+ }
69
+
70
+ void
71
+ prof_measurement_free(prof_measurement_t* measurement)
72
+ {
73
+ prof_measurement_ruby_gc_free(measurement);
74
+ xfree(measurement);
75
+ }
76
+
77
+ size_t
78
+ prof_measurement_size(const void *data)
79
+ {
80
+ return sizeof(prof_measurement_t);
81
+ }
82
+
83
+ void
84
+ prof_measurement_mark(void *data)
85
+ {
86
+ prof_measurement_t* measurement = (prof_measurement_t*)data;
87
+ if (measurement->object != Qnil)
88
+ rb_gc_mark(measurement->object);
89
+ }
90
+
91
+ VALUE
92
+ prof_measurement_wrap(prof_measurement_t* measurement)
93
+ {
94
+ if (measurement->object == Qnil)
95
+ {
96
+ measurement->object = Data_Wrap_Struct(cRpMeasurement, prof_measurement_mark, prof_measurement_ruby_gc_free, measurement);
97
+ }
98
+ return measurement->object;
99
+ }
100
+
101
+ static VALUE
102
+ prof_measurement_allocate(VALUE klass)
103
+ {
104
+ prof_measurement_t *measurement = prof_measurement_create();
105
+ measurement->object = prof_measurement_wrap(measurement);
106
+ return measurement->object;
107
+ }
108
+
109
+ prof_measurement_t*
110
+ prof_get_measurement(VALUE self)
111
+ {
112
+ /* Can't use Data_Get_Struct because that triggers the event hook
113
+ ending up in endless recursion. */
114
+ prof_measurement_t* result = DATA_PTR(self);
115
+
116
+ if (!result)
117
+ rb_raise(rb_eRuntimeError, "This RubyProf::Measurement instance has already been freed, likely because its profile has been freed.");
118
+
119
+ return result;
120
+ }
121
+
122
+ /* call-seq:
123
+ total_time -> float
124
+
125
+ Returns the total amount of time spent in this method and its children. */
126
+ static VALUE
127
+ prof_measurement_total_time(VALUE self)
128
+ {
129
+ prof_measurement_t* result = prof_get_measurement(self);
130
+ return rb_float_new(result->total_time);
131
+ }
132
+
133
+ /* call-seq:
134
+ self_time -> float
135
+
136
+ Returns the total amount of time spent in this method. */
137
+ static VALUE
138
+ prof_measurement_self_time(VALUE self)
139
+ {
140
+ prof_measurement_t* result = prof_get_measurement(self);
141
+
142
+ return rb_float_new(result->self_time);
143
+ }
144
+
145
+ /* call-seq:
146
+ wait_time -> float
147
+
148
+ Returns the total amount of time this method waited for other threads. */
149
+ static VALUE
150
+ prof_measurement_wait_time(VALUE self)
151
+ {
152
+ prof_measurement_t* result = prof_get_measurement(self);
153
+
154
+ return rb_float_new(result->wait_time);
155
+ }
156
+
157
+ /* call-seq:
158
+ called -> int
159
+
160
+ Returns the total amount of times this method was called. */
161
+ static VALUE
162
+ prof_measurement_called(VALUE self)
163
+ {
164
+ prof_measurement_t *result = prof_get_measurement(self);
165
+ return INT2NUM(result->called);
166
+ }
167
+
168
+ /* call-seq:
169
+ called=n -> n
170
+
171
+ Sets the call count to n. */
172
+ static VALUE
173
+ prof_measurement_set_called(VALUE self, VALUE called)
174
+ {
175
+ prof_measurement_t *result = prof_get_measurement(self);
176
+ result->called = NUM2INT(called);
177
+ return called;
178
+ }
179
+
180
+ /* :nodoc: */
181
+ static VALUE
182
+ prof_measurement_dump(VALUE self)
183
+ {
184
+ prof_measurement_t* measurement_data = prof_get_measurement(self);
185
+ VALUE result = rb_hash_new();
186
+
187
+ rb_hash_aset(result, ID2SYM(rb_intern("total_time")), rb_float_new(measurement_data->total_time));
188
+ rb_hash_aset(result, ID2SYM(rb_intern("self_time")), rb_float_new(measurement_data->self_time));
189
+ rb_hash_aset(result, ID2SYM(rb_intern("wait_time")), rb_float_new(measurement_data->wait_time));
190
+ rb_hash_aset(result, ID2SYM(rb_intern("called")), INT2FIX(measurement_data->called));
191
+
192
+ return result;
193
+ }
194
+
195
+ /* :nodoc: */
196
+ static VALUE
197
+ prof_measurement_load(VALUE self, VALUE data)
198
+ {
199
+ prof_measurement_t* measurement = prof_get_measurement(self);
200
+ measurement->object = self;
201
+
202
+ measurement->total_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("total_time"))));
203
+ measurement->self_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("self_time"))));
204
+ measurement->wait_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("wait_time"))));
205
+ measurement->called = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("called"))));
206
+
207
+ return data;
208
+ }
209
+
210
+ void rp_init_measure()
211
+ {
212
+ mMeasure = rb_define_module_under(mProf, "Measure");
213
+ rp_init_measure_wall_time();
214
+ rp_init_measure_process_time();
215
+ rp_init_measure_allocations();
216
+ rp_init_measure_memory();
217
+
218
+ cRpMeasurement = rb_define_class_under(mProf, "Measurement", rb_cData);
219
+ rb_undef_method(CLASS_OF(cRpMeasurement), "new");
220
+ rb_define_alloc_func(cRpMeasurement, prof_measurement_allocate);
221
+
222
+ rb_define_method(cRpMeasurement, "called", prof_measurement_called, 0);
223
+ rb_define_method(cRpMeasurement, "called=", prof_measurement_set_called, 1);
224
+ rb_define_method(cRpMeasurement, "total_time", prof_measurement_total_time, 0);
225
+ rb_define_method(cRpMeasurement, "self_time", prof_measurement_self_time, 0);
226
+ rb_define_method(cRpMeasurement, "wait_time", prof_measurement_wait_time, 0);
227
+
228
+ rb_define_method(cRpMeasurement, "_dump_data", prof_measurement_dump, 0);
229
+ rb_define_method(cRpMeasurement, "_load_data", prof_measurement_load, 1);
230
+ }
@@ -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
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,411 +1,630 @@
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
- VALUE super = rb_class_superclass(klass);
41
- result = rb_str_new2("<Object::");
42
- rb_str_append(result, rb_inspect(super));
43
- rb_str_cat2(result, ">");
44
- }
45
-
46
- /* Ok, this could be other things like an array made put onto
47
- a singleton object (yeah, it happens, see the singleton
48
- objects test case). */
49
- else
50
- {
51
- result = rb_inspect(klass);
52
- }
53
-
54
- return result;
55
- }
56
-
57
- static VALUE
58
- klass_name(VALUE klass)
59
- {
60
- VALUE result = Qnil;
61
-
62
- if (klass == 0 || klass == Qnil)
63
- {
64
- result = rb_str_new2("Global");
65
- }
66
- else if (BUILTIN_TYPE(klass) == T_MODULE)
67
- {
68
- result = rb_inspect(klass);
69
- }
70
- else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
71
- {
72
- result = figure_singleton_name(klass);
73
- }
74
- else if (BUILTIN_TYPE(klass) == T_CLASS)
75
- {
76
- result = rb_inspect(klass);
77
- }
78
- else
79
- {
80
- /* Should never happen. */
81
- result = rb_str_new2("Unknown");
82
- }
83
-
84
- return result;
85
- }
86
-
87
- static VALUE
88
- method_name(ID mid)
89
- {
90
- VALUE result;
91
-
92
- if (mid == 0)
93
- result = rb_str_new2("[No method]");
94
- #ifdef ID_ALLOCATOR
95
- else if (mid == ID_ALLOCATOR)
96
- result = rb_str_new2("allocate");
97
- #endif
98
- else
99
- result = rb_String(ID2SYM(mid));
100
-
101
- return result;
102
- }
103
-
104
- static VALUE
105
- full_name(VALUE klass, ID mid)
106
- {
107
- VALUE result = rb_str_dup(klass_name(klass));
108
- rb_str_cat2(result, "#");
109
- rb_str_append(result, method_name(mid));
110
-
111
- return result;
112
- }
113
-
114
- void
115
- method_key(prof_method_key_t* key, VALUE klass, ID mid)
116
- {
117
- /* Is this an include for a module? If so get the actual
118
- module class since we want to combine all profiling
119
- results for that module. */
120
- if (klass != 0)
121
- klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
122
-
123
- key->klass = klass;
124
- key->mid = mid;
125
- key->key = (klass << 4) + (mid << 2);
126
- }
127
-
128
- /* ================ prof_method_t =================*/
129
- prof_method_t*
130
- prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
131
- {
132
- prof_method_t *result = ALLOC(prof_method_t);
133
- result->object = Qnil;
134
- result->call_infos = prof_call_infos_create();
135
-
136
- result->key = ALLOC(prof_method_key_t);
137
- method_key(result->key, klass, mid);
138
-
139
- //result->call_info_table = call_info_table_create();
140
-
141
- if (source_file != NULL)
142
- {
143
- size_t len = strlen(source_file) + 1;
144
- char *buffer = ALLOC_N(char, len);
145
-
146
- MEMCPY(buffer, source_file, char, len);
147
- result->source_file = buffer;
148
- }
149
- else
150
- {
151
- result->source_file = source_file;
152
- }
153
- result->line = line;
154
-
155
- return result;
156
- }
157
-
158
- /* The underlying c structures are freed when the parent profile is freed.
159
- However, on shutdown the Ruby GC frees objects in any will-nilly order.
160
- That means the ruby thread object wrapping the c thread struct may
161
- be freed before the parent profile. Thus we add in a free function
162
- for the garbage collector so that if it does get called will nil
163
- out our Ruby object reference.*/
164
- static void
165
- prof_method_ruby_gc_free(prof_method_t* method)
166
- {
167
- /* Has this thread object been accessed by Ruby? If
168
- yes clean it up so to avoid a segmentation fault. */
169
- if (method->object != Qnil)
170
- {
171
- RDATA(method->object)->data = NULL;
172
- RDATA(method->object)->dfree = NULL;
173
- RDATA(method->object)->dmark = NULL;
174
- }
175
- method->object = Qnil;
176
- }
177
-
178
- static void
179
- prof_method_free(prof_method_t* method)
180
- {
181
- prof_method_ruby_gc_free(method);
182
- prof_call_infos_free(method->call_infos);
183
- xfree(method->call_infos);
184
-
185
- xfree(method->key);
186
- method->key = NULL;
187
-
188
- xfree(method);
189
- }
190
-
191
- void
192
- prof_method_mark(prof_method_t *method)
193
- {
194
- if (method->object)
195
- rb_gc_mark(method->object);
196
-
197
- prof_call_infos_mark(method->call_infos);
198
- }
199
-
200
- VALUE
201
- prof_method_wrap(prof_method_t *result)
202
- {
203
- if (result->object == Qnil)
204
- {
205
- result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_ruby_gc_free, result);
206
- }
207
- return result->object;
208
- }
209
-
210
- static prof_method_t *
211
- get_prof_method(VALUE self)
212
- {
213
- /* Can't use Data_Get_Struct because that triggers the event hook
214
- ending up in endless recursion. */
215
- prof_method_t* result = DATA_PTR(self);
216
-
217
- if (!result)
218
- rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
219
-
220
- return result;
221
- }
222
-
223
- /* ================ Method Table =================*/
224
- int
225
- method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2)
226
- {
227
- return (key1->klass != key2->klass) || (key1->mid != key2->mid);
228
- }
229
-
230
- st_index_t
231
- method_table_hash(prof_method_key_t *key)
232
- {
233
- return key->key;
234
- }
235
-
236
- struct st_hash_type type_method_hash = {
237
- method_table_cmp,
238
- method_table_hash
239
- };
240
-
241
- st_table *
242
- method_table_create()
243
- {
244
- return st_init_table(&type_method_hash);
245
- }
246
-
247
- static int
248
- method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
249
- {
250
- prof_method_free((prof_method_t*)value);
251
- return ST_CONTINUE;
252
- }
253
-
254
- void
255
- method_table_free(st_table *table)
256
- {
257
- st_foreach(table, method_table_free_iterator, 0);
258
- st_free_table(table);
259
- }
260
-
261
-
262
- size_t
263
- method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
264
- {
265
- return st_insert(table, (st_data_t) key, (st_data_t) val);
266
- }
267
-
268
- prof_method_t *
269
- method_table_lookup(st_table *table, const prof_method_key_t* key)
270
- {
271
- st_data_t val;
272
- if (st_lookup(table, (st_data_t)key, &val))
273
- {
274
- return (prof_method_t *) val;
275
- }
276
- else
277
- {
278
- return NULL;
279
- }
280
- }
281
-
282
- /* ================ Method Info =================*/
283
- /* Document-class: RubyProf::MethodInfo
284
- The RubyProf::MethodInfo class stores profiling data for a method.
285
- One instance of the RubyProf::MethodInfo class is created per method
286
- called per thread. Thus, if a method is called in two different
287
- thread then there will be two RubyProf::MethodInfo objects
288
- created. RubyProf::MethodInfo objects can be accessed via
289
- the RubyProf::Profile object.
290
- */
291
-
292
- /* call-seq:
293
- line_no -> int
294
-
295
- returns the line number of the method */
296
- static VALUE
297
- prof_method_line(VALUE self)
298
- {
299
- return rb_int_new(get_prof_method(self)->line);
300
- }
301
-
302
- /* call-seq:
303
- source_file => string
304
-
305
- return the source file of the method
306
- */
307
- static VALUE prof_method_source_file(VALUE self)
308
- {
309
- const char* sf = get_prof_method(self)->source_file;
310
- if(!sf)
311
- {
312
- return rb_str_new2("ruby_runtime");
313
- }
314
- else
315
- {
316
- return rb_str_new2(sf);
317
- }
318
- }
319
-
320
-
321
- /* call-seq:
322
- method_class -> klass
323
-
324
- Returns the Ruby klass that owns this method. */
325
- static VALUE
326
- prof_method_klass(VALUE self)
327
- {
328
- prof_method_t *result = get_prof_method(self);
329
- return result->key->klass;
330
- }
331
-
332
- /* call-seq:
333
- method_id -> ID
334
-
335
- Returns the id of this method. */
336
- static VALUE
337
- prof_method_id(VALUE self)
338
- {
339
- prof_method_t *result = get_prof_method(self);
340
- return ID2SYM(result->key->mid);
341
- }
342
-
343
- /* call-seq:
344
- klass_name -> string
345
-
346
- Returns the name of this method's class. Singleton classes
347
- will have the form <Object::Object>. */
348
-
349
- static VALUE
350
- prof_klass_name(VALUE self)
351
- {
352
- prof_method_t *method = get_prof_method(self);
353
- return klass_name(method->key->klass);
354
- }
355
-
356
- /* call-seq:
357
- method_name -> string
358
-
359
- Returns the name of this method in the format Object#method. Singletons
360
- methods will be returned in the format <Object::Object>#method.*/
361
-
362
- static VALUE
363
- prof_method_name(VALUE self)
364
- {
365
- prof_method_t *method = get_prof_method(self);
366
- return method_name(method->key->mid);
367
- }
368
-
369
- /* call-seq:
370
- full_name -> string
371
-
372
- Returns the full name of this method in the format Object#method.*/
373
-
374
- static VALUE
375
- prof_full_name(VALUE self)
376
- {
377
- prof_method_t *method = get_prof_method(self);
378
- return full_name(method->key->klass, method->key->mid);
379
- }
380
-
381
- /* call-seq:
382
- call_infos -> Array of call_info
383
-
384
- Returns an array of call info objects that contain profiling information
385
- about the current method.*/
386
- static VALUE
387
- prof_method_call_infos(VALUE self)
388
- {
389
- prof_method_t *method = get_prof_method(self);
390
- if (method->call_infos->object == Qnil)
391
- {
392
- method->call_infos->object = prof_call_infos_wrap(method->call_infos);
393
- }
394
- return method->call_infos->object;
395
- }
396
-
397
- void rp_init_method_info()
398
- {
399
- /* MethodInfo */
400
- cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
401
- rb_undef_method(CLASS_OF(cMethodInfo), "new");
402
-
403
- rb_define_method(cMethodInfo, "klass", prof_method_klass, 0);
404
- rb_define_method(cMethodInfo, "klass_name", prof_klass_name, 0);
405
- rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
406
- rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
407
- rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
408
- rb_define_method(cMethodInfo, "source_file", prof_method_source_file,0);
409
- rb_define_method(cMethodInfo, "line", prof_method_line, 0);
410
- rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
411
- }
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_info.h"
6
+ #include "rp_method.h"
7
+
8
+ VALUE cRpMethodInfo;
9
+
10
+ /* ================ Helper Functions =================*/
11
+ VALUE
12
+ resolve_klass(VALUE klass, unsigned int *klass_flags)
13
+ {
14
+ VALUE result = klass;
15
+
16
+ if (klass == 0 || klass == Qnil)
17
+ {
18
+ result = Qnil;
19
+ }
20
+ else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
21
+ {
22
+ /* We have come across a singleton object. First
23
+ figure out what it is attached to.*/
24
+ VALUE attached = rb_iv_get(klass, "__attached__");
25
+
26
+ /* Is this a singleton class acting as a metaclass? */
27
+ if (BUILTIN_TYPE(attached) == T_CLASS)
28
+ {
29
+ *klass_flags |= kClassSingleton;
30
+ result = attached;
31
+ }
32
+ /* Is this for singleton methods on a module? */
33
+ else if (BUILTIN_TYPE(attached) == T_MODULE)
34
+ {
35
+ *klass_flags |= kModuleSingleton;
36
+ result = attached;
37
+ }
38
+ /* Is this for singleton methods on an object? */
39
+ else if (BUILTIN_TYPE(attached) == T_OBJECT)
40
+ {
41
+ *klass_flags |= kObjectSingleton;
42
+ result = rb_class_superclass(klass);
43
+ }
44
+ /* Ok, this could be other things like an array made put onto
45
+ a singleton object (yeah, it happens, see the singleton
46
+ objects test case). */
47
+ else
48
+ {
49
+ *klass_flags |= kOtherSingleton;
50
+ result = klass;
51
+ }
52
+ }
53
+ /* Is this an include for a module? If so get the actual
54
+ module class since we want to combine all profiling
55
+ results for that module. */
56
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
57
+ {
58
+ unsigned int dummy;
59
+ *klass_flags |= kModuleIncludee;
60
+ result = resolve_klass(RBASIC(klass)->klass, &dummy);
61
+ }
62
+ return result;
63
+ }
64
+
65
+ VALUE
66
+ resolve_klass_name(VALUE klass, unsigned int* klass_flags)
67
+ {
68
+ VALUE result = Qnil;
69
+
70
+ if (klass == Qnil)
71
+ {
72
+ result = rb_str_new2("[global]");
73
+ }
74
+ else if (*klass_flags & kOtherSingleton)
75
+ {
76
+ result = rb_any_to_s(klass);
77
+ }
78
+ else
79
+ {
80
+ result = rb_class_name(klass);
81
+ }
82
+
83
+ return result;
84
+ }
85
+
86
+ st_data_t
87
+ method_key(VALUE klass, VALUE msym)
88
+ {
89
+ VALUE resolved_klass = klass;
90
+
91
+ /* Is this an include for a module? If so get the actual
92
+ module class since we want to combine all profiling
93
+ results for that module. */
94
+ if (klass == 0 || klass == Qnil)
95
+ {
96
+ resolved_klass = Qnil;
97
+ }
98
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
99
+ {
100
+ resolved_klass = RBASIC(klass)->klass;
101
+ }
102
+
103
+ return (resolved_klass << 4) + (msym);
104
+ }
105
+
106
+ /* ====== Allocation Table ====== */
107
+ st_table*
108
+ allocations_table_create()
109
+ {
110
+ return st_init_numtable();
111
+ }
112
+
113
+ static int
114
+ allocations_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
115
+ {
116
+ prof_allocation_free((prof_allocation_t*)value);
117
+ return ST_CONTINUE;
118
+ }
119
+
120
+ static int
121
+ prof_method_collect_allocations(st_data_t key, st_data_t value, st_data_t result)
122
+ {
123
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
124
+ VALUE arr = (VALUE)result;
125
+ rb_ary_push(arr, prof_allocation_wrap(allocation));
126
+ return ST_CONTINUE;
127
+ }
128
+
129
+ static int
130
+ prof_method_mark_allocations(st_data_t key, st_data_t value, st_data_t data)
131
+ {
132
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
133
+ prof_allocation_mark(allocation);
134
+ return ST_CONTINUE;
135
+ }
136
+
137
+ void
138
+ allocations_table_free(st_table* table)
139
+ {
140
+ st_foreach(table, allocations_table_free_iterator, 0);
141
+ st_free_table(table);
142
+ }
143
+
144
+ /* ================ prof_method_t =================*/
145
+ static prof_method_t*
146
+ prof_get_method(VALUE self)
147
+ {
148
+ /* Can't use Data_Get_Struct because that triggers the event hook
149
+ ending up in endless recursion. */
150
+ prof_method_t* result = DATA_PTR(self);
151
+
152
+ if (!result)
153
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
154
+
155
+ return result;
156
+ }
157
+
158
+ prof_method_t*
159
+ prof_method_create(VALUE klass, VALUE msym, VALUE source_file, int source_line)
160
+ {
161
+ prof_method_t *result = ALLOC(prof_method_t);
162
+ result->key = method_key(klass, msym);
163
+ result->klass_flags = 0;
164
+
165
+ /* Note we do not call resolve_klass_name now because that causes an object allocation that shows up
166
+ in the allocation results so we want to avoid it until after the profile run is complete. */
167
+ result->klass = resolve_klass(klass, &result->klass_flags);
168
+ result->klass_name = Qnil;
169
+ result->method_name = msym;
170
+ result->measurement = prof_measurement_create();
171
+
172
+ result->root = false;
173
+ result->excluded = false;
174
+
175
+ result->parent_call_infos = method_table_create();
176
+ result->child_call_infos = method_table_create();
177
+ result->allocations_table = allocations_table_create();
178
+
179
+ result->visits = 0;
180
+ result->recursive = false;
181
+
182
+ result->object = Qnil;
183
+
184
+ result->source_file = source_file;
185
+ result->source_line = source_line;
186
+
187
+ return result;
188
+ }
189
+
190
+ prof_method_t*
191
+ prof_method_create_excluded(VALUE klass, VALUE msym)
192
+ {
193
+ prof_method_t* result = prof_method_create(klass, msym, Qnil, 0);
194
+ result->excluded = 1;
195
+ return result;
196
+ }
197
+
198
+ static int
199
+ prof_method_collect_call_infos(st_data_t key, st_data_t value, st_data_t result)
200
+ {
201
+ prof_call_info_t* call_info = (prof_call_info_t*)value;
202
+ VALUE arr = (VALUE)result;
203
+ rb_ary_push(arr, prof_call_info_wrap(call_info));
204
+ return ST_CONTINUE;
205
+ }
206
+
207
+ static int
208
+ prof_method_mark_call_infos(st_data_t key, st_data_t value, st_data_t data)
209
+ {
210
+ prof_call_info_t* call_info = (prof_call_info_t*)value;
211
+ prof_call_info_mark(call_info);
212
+ return ST_CONTINUE;
213
+ }
214
+
215
+ static int
216
+ call_infos_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
217
+ {
218
+ prof_call_info_free((prof_call_info_t*)value);
219
+ return ST_CONTINUE;
220
+ }
221
+
222
+ void
223
+ call_info_table_free(st_table* table)
224
+ {
225
+ st_foreach(table, call_infos_free_iterator, 0);
226
+ st_free_table(table);
227
+ }
228
+
229
+ /* The underlying c structures are freed when the parent profile is freed.
230
+ However, on shutdown the Ruby GC frees objects in any will-nilly order.
231
+ That means the ruby thread object wrapping the c thread struct may
232
+ be freed before the parent profile. Thus we add in a free function
233
+ for the garbage collector so that if it does get called will nil
234
+ out our Ruby object reference.*/
235
+ static void
236
+ prof_method_ruby_gc_free(void *data)
237
+ {
238
+ prof_method_t* method = (prof_method_t*)data;
239
+
240
+ /* Has this method object been accessed by Ruby? If
241
+ yes clean it up so to avoid a segmentation fault. */
242
+ if (method->object != Qnil)
243
+ {
244
+ RDATA(method->object)->dmark = NULL;
245
+ RDATA(method->object)->dfree = NULL;
246
+ RDATA(method->object)->data = NULL;
247
+ method->object = Qnil;
248
+ }
249
+ method->klass_name = Qnil;
250
+ method->method_name = Qnil;
251
+ }
252
+
253
+ static void
254
+ prof_method_free(prof_method_t* method)
255
+ {
256
+ prof_method_ruby_gc_free(method);
257
+ allocations_table_free(method->allocations_table);
258
+
259
+ /* Remember call infos are referenced by their parent method and child method, so we only want
260
+ to iterate over one of them to avoid a double freeing */
261
+ call_info_table_free(method->parent_call_infos);
262
+ xfree(method->child_call_infos);
263
+
264
+ prof_measurement_free(method->measurement);
265
+ xfree(method);
266
+ }
267
+
268
+ size_t
269
+ prof_method_size(const void *data)
270
+ {
271
+ return sizeof(prof_method_t);
272
+ }
273
+
274
+ void
275
+ prof_method_mark(void *data)
276
+ {
277
+ prof_method_t* method = (prof_method_t*)data;
278
+ rb_gc_mark(method->klass_name);
279
+ rb_gc_mark(method->method_name);
280
+
281
+ if (method->klass != Qnil)
282
+ rb_gc_mark(method->klass);
283
+
284
+ if (method->object != Qnil)
285
+ rb_gc_mark(method->object);
286
+
287
+ prof_measurement_mark(method->measurement);
288
+
289
+ st_foreach(method->parent_call_infos, prof_method_mark_call_infos, 0);
290
+ st_foreach(method->child_call_infos, prof_method_mark_call_infos, 0);
291
+ st_foreach(method->allocations_table, prof_method_mark_allocations, 0);
292
+ }
293
+
294
+ static VALUE
295
+ prof_method_allocate(VALUE klass)
296
+ {
297
+ prof_method_t* method_data = prof_method_create(Qnil, Qnil, Qnil, 0);
298
+ method_data->object = prof_method_wrap(method_data);
299
+ return method_data->object;
300
+ }
301
+
302
+ VALUE
303
+ prof_method_wrap(prof_method_t *method)
304
+ {
305
+ if (method->object == Qnil)
306
+ {
307
+ method->object = Data_Wrap_Struct(cRpMethodInfo, prof_method_mark, prof_method_ruby_gc_free, method);
308
+ }
309
+ return method->object;
310
+ }
311
+
312
+ prof_method_t *
313
+ prof_method_get(VALUE self)
314
+ {
315
+ /* Can't use Data_Get_Struct because that triggers the event hook
316
+ ending up in endless recursion. */
317
+ prof_method_t* result = DATA_PTR(self);
318
+
319
+ if (!result)
320
+ {
321
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
322
+ }
323
+
324
+ return result;
325
+ }
326
+
327
+ st_table *
328
+ method_table_create()
329
+ {
330
+ return st_init_numtable();
331
+ }
332
+
333
+ static int
334
+ method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
335
+ {
336
+ prof_method_free((prof_method_t *)value);
337
+ return ST_CONTINUE;
338
+ }
339
+
340
+ void
341
+ method_table_free(st_table *table)
342
+ {
343
+ st_foreach(table, method_table_free_iterator, 0);
344
+ st_free_table(table);
345
+ }
346
+
347
+ size_t
348
+ method_table_insert(st_table *table, st_data_t key, prof_method_t *val)
349
+ {
350
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
351
+ }
352
+
353
+ prof_method_t *
354
+ method_table_lookup(st_table *table, st_data_t key)
355
+ {
356
+ st_data_t val;
357
+ if (st_lookup(table, (st_data_t)key, &val))
358
+ {
359
+ return (prof_method_t *) val;
360
+ }
361
+ else
362
+ {
363
+ return NULL;
364
+ }
365
+ }
366
+
367
+ /* ================ Method Info =================*/
368
+ /* Document-class: RubyProf::MethodInfo
369
+ The RubyProf::MethodInfo class stores profiling data for a method.
370
+ One instance of the RubyProf::MethodInfo class is created per method
371
+ called per thread. Thus, if a method is called in two different
372
+ thread then there will be two RubyProf::MethodInfo objects
373
+ created. RubyProf::MethodInfo objects can be accessed via
374
+ the RubyProf::Profile object.
375
+ */
376
+
377
+ /* call-seq:
378
+ callers -> array
379
+
380
+ Returns an array of call info objects that called this method (ie, parents).*/
381
+ static VALUE
382
+ prof_method_callers(VALUE self)
383
+ {
384
+ prof_method_t* method = prof_get_method(self);
385
+ VALUE result = rb_ary_new();
386
+ st_foreach(method->parent_call_infos, prof_method_collect_call_infos, result);
387
+ return result;
388
+ }
389
+
390
+ /* call-seq:
391
+ callees -> array
392
+
393
+ Returns an array of call info objects that this method called (ie, children).*/
394
+ static VALUE
395
+ prof_method_callees(VALUE self)
396
+ {
397
+ prof_method_t* method = prof_get_method(self);
398
+ VALUE result = rb_ary_new();
399
+ st_foreach(method->child_call_infos, prof_method_collect_call_infos, result);
400
+ return result;
401
+ }
402
+
403
+ /* call-seq:
404
+ allocations -> array
405
+
406
+ Returns an array of allocation information.*/
407
+ static VALUE
408
+ prof_method_allocations(VALUE self)
409
+ {
410
+ prof_method_t* method = prof_get_method(self);
411
+ VALUE result = rb_ary_new();
412
+ st_foreach(method->allocations_table, prof_method_collect_allocations, result);
413
+ return result;
414
+ }
415
+
416
+ /* call-seq:
417
+ called -> Measurement
418
+
419
+ Returns the measurement associated with this method. */
420
+ static VALUE
421
+ prof_method_measurement(VALUE self)
422
+ {
423
+ prof_method_t* method = prof_get_method(self);
424
+ return prof_measurement_wrap(method->measurement);
425
+ }
426
+
427
+ /* call-seq:
428
+ source_file => string
429
+
430
+ return the source file of the method
431
+ */
432
+ static VALUE prof_method_source_file(VALUE self)
433
+ {
434
+ prof_method_t* method = prof_method_get(self);
435
+ return method->source_file;
436
+ }
437
+
438
+ /* call-seq:
439
+ line_no -> int
440
+
441
+ returns the line number of the method */
442
+ static VALUE
443
+ prof_method_line(VALUE self)
444
+ {
445
+ prof_method_t* method = prof_method_get(self);
446
+ return INT2FIX(method->source_line);
447
+ }
448
+
449
+ /* call-seq:
450
+ klass_name -> string
451
+
452
+ Returns the name of this method's class. Singleton classes
453
+ will have the form <Object::Object>. */
454
+
455
+ static VALUE
456
+ prof_method_klass_name(VALUE self)
457
+ {
458
+ prof_method_t *method = prof_method_get(self);
459
+ if (method->klass_name == Qnil)
460
+ method->klass_name = resolve_klass_name(method->klass, &method->klass_flags);
461
+
462
+ return method->klass_name;
463
+ }
464
+
465
+ /* call-seq:
466
+ klass_flags -> integer
467
+
468
+ Returns the klass flags */
469
+
470
+ static VALUE
471
+ prof_method_klass_flags(VALUE self)
472
+ {
473
+ prof_method_t* method = prof_method_get(self);
474
+ return INT2FIX(method->klass_flags);
475
+ }
476
+
477
+ /* call-seq:
478
+ method_name -> string
479
+
480
+ Returns the name of this method in the format Object#method. Singletons
481
+ methods will be returned in the format <Object::Object>#method.*/
482
+
483
+ static VALUE
484
+ prof_method_name(VALUE self)
485
+ {
486
+ prof_method_t *method = prof_method_get(self);
487
+ return method->method_name;
488
+ }
489
+
490
+ /* call-seq:
491
+ root? -> boolean
492
+
493
+ Returns the true if this method is at the top of the call stack */
494
+ static VALUE
495
+ prof_method_root(VALUE self)
496
+ {
497
+ prof_method_t *method = prof_method_get(self);
498
+ return method->root ? Qtrue : Qfalse;
499
+ }
500
+
501
+ /* call-seq:
502
+ recursive? -> boolean
503
+
504
+ Returns the true if this method is recursively invoked */
505
+ static VALUE
506
+ prof_method_recursive(VALUE self)
507
+ {
508
+ prof_method_t* method = prof_method_get(self);
509
+ return method->recursive ? Qtrue : Qfalse;
510
+ }
511
+
512
+ /* call-seq:
513
+ excluded? -> boolean
514
+
515
+ Returns the true if this method was excluded */
516
+ static VALUE
517
+ prof_method_excluded(VALUE self)
518
+ {
519
+ prof_method_t* method = prof_method_get(self);
520
+ return method->excluded ? Qtrue : Qfalse;
521
+ }
522
+
523
+ /* :nodoc: */
524
+ static VALUE
525
+ prof_method_dump(VALUE self)
526
+ {
527
+ prof_method_t* method_data = DATA_PTR(self);
528
+ VALUE result = rb_hash_new();
529
+
530
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_method_klass_name(self));
531
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(method_data->klass_flags));
532
+ rb_hash_aset(result, ID2SYM(rb_intern("method_name")), method_data->method_name);
533
+
534
+ rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(method_data->key));
535
+ rb_hash_aset(result, ID2SYM(rb_intern("root")), prof_method_root(self));
536
+ rb_hash_aset(result, ID2SYM(rb_intern("recursive")), prof_method_recursive(self));
537
+ rb_hash_aset(result, ID2SYM(rb_intern("excluded")), prof_method_excluded(self));
538
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), method_data->source_file);
539
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(method_data->source_line));
540
+
541
+ rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(method_data->measurement));
542
+
543
+ rb_hash_aset(result, ID2SYM(rb_intern("callers")), prof_method_callers(self));
544
+ rb_hash_aset(result, ID2SYM(rb_intern("callees")), prof_method_callees(self));
545
+
546
+ rb_hash_aset(result, ID2SYM(rb_intern("allocations")), prof_method_allocations(self));
547
+
548
+ return result;
549
+ }
550
+
551
+ /* :nodoc: */
552
+ static VALUE
553
+ prof_method_load(VALUE self, VALUE data)
554
+ {
555
+ prof_method_t* method_data = RDATA(self)->data;
556
+ method_data->object = self;
557
+
558
+ method_data->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
559
+ method_data->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
560
+ method_data->method_name = rb_hash_aref(data, ID2SYM(rb_intern("method_name")));
561
+ method_data->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
562
+
563
+ method_data->root = rb_hash_aref(data, ID2SYM(rb_intern("root"))) == Qtrue ? true : false;
564
+ method_data->recursive = rb_hash_aref(data, ID2SYM(rb_intern("recursive"))) == Qtrue ? true : false;
565
+ method_data->excluded = rb_hash_aref(data, ID2SYM(rb_intern("excluded"))) == Qtrue ? true : false;
566
+
567
+ method_data->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
568
+ method_data->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
569
+
570
+ VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
571
+ method_data->measurement = prof_get_measurement(measurement);
572
+
573
+ VALUE callers = rb_hash_aref(data, ID2SYM(rb_intern("callers")));
574
+ for (int i = 0; i < rb_array_len(callers); i++)
575
+ {
576
+ VALUE call_info = rb_ary_entry(callers, i);
577
+ prof_call_info_t *call_info_data = prof_get_call_info(call_info);
578
+ st_data_t key = call_info_data->parent ? call_info_data->parent->key : method_key(Qnil, 0);
579
+ call_info_table_insert(method_data->parent_call_infos, key, call_info_data);
580
+ }
581
+
582
+ VALUE callees = rb_hash_aref(data, ID2SYM(rb_intern("callees")));
583
+ for (int i = 0; i < rb_array_len(callees); i++)
584
+ {
585
+ VALUE call_info = rb_ary_entry(callees, i);
586
+ prof_call_info_t *call_info_data = prof_get_call_info(call_info);
587
+
588
+ st_data_t key = call_info_data->method ? call_info_data->method->key : method_key(Qnil, 0);
589
+ call_info_table_insert(method_data->child_call_infos, key, call_info_data);
590
+ }
591
+
592
+ VALUE allocations = rb_hash_aref(data, ID2SYM(rb_intern("allocations")));
593
+ for (int i = 0; i < rb_array_len(allocations); i++)
594
+ {
595
+ VALUE allocation = rb_ary_entry(allocations, i);
596
+ prof_allocation_t* allocation_data = prof_allocation_get(allocation);
597
+
598
+ st_insert(method_data->allocations_table, allocation_data->key, (st_data_t)allocation_data);
599
+ }
600
+ return data;
601
+ }
602
+
603
+ void rp_init_method_info()
604
+ {
605
+ /* MethodInfo */
606
+ cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cData);
607
+ rb_undef_method(CLASS_OF(cRpMethodInfo), "new");
608
+ rb_define_alloc_func(cRpMethodInfo, prof_method_allocate);
609
+
610
+ rb_define_method(cRpMethodInfo, "klass_name", prof_method_klass_name, 0);
611
+ rb_define_method(cRpMethodInfo, "klass_flags", prof_method_klass_flags, 0);
612
+
613
+ rb_define_method(cRpMethodInfo, "method_name", prof_method_name, 0);
614
+
615
+ rb_define_method(cRpMethodInfo, "callers", prof_method_callers, 0);
616
+ rb_define_method(cRpMethodInfo, "callees", prof_method_callees, 0);
617
+ rb_define_method(cRpMethodInfo, "allocations", prof_method_allocations, 0);
618
+
619
+ rb_define_method(cRpMethodInfo, "measurement", prof_method_measurement, 0);
620
+
621
+ rb_define_method(cRpMethodInfo, "source_file", prof_method_source_file, 0);
622
+ rb_define_method(cRpMethodInfo, "line", prof_method_line, 0);
623
+
624
+ rb_define_method(cRpMethodInfo, "root?", prof_method_root, 0);
625
+ rb_define_method(cRpMethodInfo, "recursive?", prof_method_recursive, 0);
626
+ rb_define_method(cRpMethodInfo, "excluded?", prof_method_excluded, 0);
627
+
628
+ rb_define_method(cRpMethodInfo, "_dump_data", prof_method_dump, 0);
629
+ rb_define_method(cRpMethodInfo, "_load_data", prof_method_load, 1);
630
+ }