ruby-prof 0.18.0-x64-mingw32

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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +500 -0
  3. data/LICENSE +25 -0
  4. data/README.rdoc +487 -0
  5. data/Rakefile +113 -0
  6. data/bin/ruby-prof +345 -0
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/examples/flat.txt +50 -0
  9. data/examples/graph.dot +84 -0
  10. data/examples/graph.html +823 -0
  11. data/examples/graph.txt +139 -0
  12. data/examples/multi.flat.txt +23 -0
  13. data/examples/multi.graph.html +760 -0
  14. data/examples/multi.grind.dat +114 -0
  15. data/examples/multi.stack.html +547 -0
  16. data/examples/stack.html +547 -0
  17. data/ext/ruby_prof/extconf.rb +68 -0
  18. data/ext/ruby_prof/rp_call_info.c +425 -0
  19. data/ext/ruby_prof/rp_call_info.h +53 -0
  20. data/ext/ruby_prof/rp_measure.c +40 -0
  21. data/ext/ruby_prof/rp_measure.h +45 -0
  22. data/ext/ruby_prof/rp_measure_allocations.c +76 -0
  23. data/ext/ruby_prof/rp_measure_cpu_time.c +136 -0
  24. data/ext/ruby_prof/rp_measure_gc_runs.c +73 -0
  25. data/ext/ruby_prof/rp_measure_gc_time.c +60 -0
  26. data/ext/ruby_prof/rp_measure_memory.c +77 -0
  27. data/ext/ruby_prof/rp_measure_process_time.c +71 -0
  28. data/ext/ruby_prof/rp_measure_wall_time.c +45 -0
  29. data/ext/ruby_prof/rp_method.c +630 -0
  30. data/ext/ruby_prof/rp_method.h +75 -0
  31. data/ext/ruby_prof/rp_stack.c +173 -0
  32. data/ext/ruby_prof/rp_stack.h +63 -0
  33. data/ext/ruby_prof/rp_thread.c +277 -0
  34. data/ext/ruby_prof/rp_thread.h +27 -0
  35. data/ext/ruby_prof/ruby_prof.c +794 -0
  36. data/ext/ruby_prof/ruby_prof.h +60 -0
  37. data/ext/ruby_prof/vc/ruby_prof.sln +31 -0
  38. data/ext/ruby_prof/vc/ruby_prof.vcxproj +141 -0
  39. data/lib/2.6.3/ruby_prof.so +0 -0
  40. data/lib/ruby-prof.rb +68 -0
  41. data/lib/ruby-prof/aggregate_call_info.rb +76 -0
  42. data/lib/ruby-prof/assets/call_stack_printer.css.html +117 -0
  43. data/lib/ruby-prof/assets/call_stack_printer.js.html +385 -0
  44. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  45. data/lib/ruby-prof/call_info.rb +115 -0
  46. data/lib/ruby-prof/call_info_visitor.rb +40 -0
  47. data/lib/ruby-prof/compatibility.rb +179 -0
  48. data/lib/ruby-prof/method_info.rb +121 -0
  49. data/lib/ruby-prof/printers/abstract_printer.rb +104 -0
  50. data/lib/ruby-prof/printers/call_info_printer.rb +41 -0
  51. data/lib/ruby-prof/printers/call_stack_printer.rb +265 -0
  52. data/lib/ruby-prof/printers/call_tree_printer.rb +143 -0
  53. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  54. data/lib/ruby-prof/printers/flat_printer.rb +70 -0
  55. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +83 -0
  56. data/lib/ruby-prof/printers/graph_html_printer.rb +249 -0
  57. data/lib/ruby-prof/printers/graph_printer.rb +116 -0
  58. data/lib/ruby-prof/printers/multi_printer.rb +84 -0
  59. data/lib/ruby-prof/profile.rb +26 -0
  60. data/lib/ruby-prof/profile/exclude_common_methods.rb +207 -0
  61. data/lib/ruby-prof/profile/legacy_method_elimination.rb +50 -0
  62. data/lib/ruby-prof/rack.rb +174 -0
  63. data/lib/ruby-prof/task.rb +147 -0
  64. data/lib/ruby-prof/thread.rb +35 -0
  65. data/lib/ruby-prof/version.rb +3 -0
  66. data/lib/unprof.rb +10 -0
  67. data/ruby-prof.gemspec +58 -0
  68. data/test/abstract_printer_test.rb +53 -0
  69. data/test/aggregate_test.rb +136 -0
  70. data/test/basic_test.rb +128 -0
  71. data/test/block_test.rb +74 -0
  72. data/test/call_info_test.rb +78 -0
  73. data/test/call_info_visitor_test.rb +31 -0
  74. data/test/duplicate_names_test.rb +32 -0
  75. data/test/dynamic_method_test.rb +55 -0
  76. data/test/enumerable_test.rb +21 -0
  77. data/test/exceptions_test.rb +24 -0
  78. data/test/exclude_methods_test.rb +146 -0
  79. data/test/exclude_threads_test.rb +53 -0
  80. data/test/fiber_test.rb +79 -0
  81. data/test/issue137_test.rb +63 -0
  82. data/test/line_number_test.rb +80 -0
  83. data/test/measure_allocations_test.rb +26 -0
  84. data/test/measure_cpu_time_test.rb +212 -0
  85. data/test/measure_gc_runs_test.rb +32 -0
  86. data/test/measure_gc_time_test.rb +36 -0
  87. data/test/measure_memory_test.rb +33 -0
  88. data/test/measure_process_time_test.rb +61 -0
  89. data/test/measure_wall_time_test.rb +255 -0
  90. data/test/method_elimination_test.rb +84 -0
  91. data/test/module_test.rb +45 -0
  92. data/test/multi_printer_test.rb +104 -0
  93. data/test/no_method_class_test.rb +15 -0
  94. data/test/pause_resume_test.rb +166 -0
  95. data/test/prime.rb +54 -0
  96. data/test/printers_test.rb +275 -0
  97. data/test/printing_recursive_graph_test.rb +127 -0
  98. data/test/rack_test.rb +157 -0
  99. data/test/recursive_test.rb +215 -0
  100. data/test/singleton_test.rb +38 -0
  101. data/test/stack_printer_test.rb +77 -0
  102. data/test/stack_test.rb +138 -0
  103. data/test/start_stop_test.rb +112 -0
  104. data/test/test_helper.rb +267 -0
  105. data/test/thread_test.rb +187 -0
  106. data/test/unique_call_path_test.rb +202 -0
  107. data/test/yarv_test.rb +55 -0
  108. metadata +199 -0
@@ -0,0 +1,77 @@
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
+
6
+ #include "ruby_prof.h"
7
+
8
+ static VALUE cMeasureMemory;
9
+
10
+
11
+ #if defined(HAVE_RB_GC_ALLOCATED_SIZE)
12
+ VALUE rb_gc_allocated_size();
13
+ #endif
14
+
15
+ #if defined(HAVE_RB_GC_MALLOC_ALLOCATED_SIZE)
16
+ size_t rb_gc_malloc_allocated_size();
17
+ #endif
18
+
19
+ #if defined(HAVE_RB_HEAP_TOTAL_MEM)
20
+ //FIXME: did not find the patch to check prototype, assuming it to return size_t
21
+ size_t rb_heap_total_mem();
22
+ #endif
23
+
24
+ static double
25
+ measure_memory()
26
+ {
27
+ #if defined(HAVE_RB_GC_ALLOCATED_SIZE)
28
+ #define MEASURE_MEMORY_ENABLED Qtrue
29
+ #if defined(HAVE_LONG_LONG)
30
+ return NUM2LL(rb_gc_allocated_size()) / 1024.0;
31
+ #else
32
+ return NUM2ULONG(rb_gc_allocated_size()) / 1024.0;
33
+ #endif
34
+
35
+ #elif defined(HAVE_RB_GC_MALLOC_ALLOCATED_SIZE)
36
+ #define MEASURE_MEMORY_ENABLED Qtrue
37
+ return rb_gc_malloc_allocated_size() / 1024.0;
38
+
39
+ #elif defined(HAVE_RB_GC_TOTAL_MALLOCED_BYTES)
40
+ #define MEASURE_MEMORY_ENABLED Qtrue
41
+ return rb_gc_total_malloced_bytes() / 1024.0;
42
+
43
+ #elif defined(HAVE_RB_HEAP_TOTAL_MEM)
44
+ #define MEASURE_MEMORY_ENABLED Qtrue
45
+ return rb_heap_total_mem() / 1024.0;
46
+
47
+ #else
48
+ #define MEASURE_MEMORY_ENABLED Qfalse
49
+ return 0;
50
+ #endif
51
+ }
52
+
53
+ prof_measurer_t* prof_measurer_memory()
54
+ {
55
+ prof_measurer_t* measure = ALLOC(prof_measurer_t);
56
+ measure->measure = measure_memory;
57
+ return measure;
58
+ }
59
+
60
+ /* call-seq:
61
+ measure_process_time -> float
62
+
63
+ Returns the process time.*/
64
+ static VALUE
65
+ prof_measure_memory(VALUE self)
66
+ {
67
+ return rb_float_new(measure_memory());
68
+ }
69
+
70
+ void rp_init_measure_memory()
71
+ {
72
+ rb_define_const(mProf, "MEMORY", INT2NUM(MEASURE_MEMORY));
73
+ rb_define_const(mProf, "MEMORY_ENABLED", MEASURE_MEMORY_ENABLED);
74
+
75
+ cMeasureMemory = rb_define_class_under(mMeasure, "Memory", rb_cObject);
76
+ rb_define_singleton_method(cMeasureMemory, "measure", prof_measure_memory, 0);
77
+ }
@@ -0,0 +1,71 @@
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
+ #include <time.h>
6
+
7
+ static VALUE cMeasureProcessTime;
8
+
9
+ static double
10
+ measure_process_time()
11
+ {
12
+ #if defined(__linux__)
13
+ struct timespec clock;
14
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID , &clock);
15
+ return clock.tv_sec + (clock.tv_nsec/1000000000.0);
16
+ #elif defined(_win32)
17
+ FILETIME createTime;
18
+ FILETIME exitTime;
19
+ FILETIME sysTime;
20
+ FILETIME cpuTime;
21
+
22
+ ULARGE_INTEGER sysTimeInt;
23
+ ULARGE_INTEGER cpuTimeInt;
24
+ ULONGLONG totalTime;
25
+
26
+ GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &sysTime, &cpuTime);
27
+
28
+ /* Doing this based on MSFT's recommendation in the FILETIME structure documentation at
29
+ http://msdn.microsoft.com/en-us/library/ms724284%28VS.85%29.aspx*/
30
+
31
+ sysTimeInt.LowPart = sysTime.dwLowDateTime;
32
+ sysTimeInt.HighPart = sysTime.dwHighDateTime;
33
+ cpuTimeInt.LowPart = cpuTime.dwLowDateTime;
34
+ cpuTimeInt.HighPart = cpuTime.dwHighDateTime;
35
+
36
+ totalTime = sysTimeInt.QuadPart + cpuTimeInt.QuadPart;
37
+
38
+ // Times are in 100-nanosecond time units. So instead of 10-9 use 10-7
39
+ return totalTime / 10000000.0;
40
+ #else
41
+ return ((double)clock()) / CLOCKS_PER_SEC;
42
+ #endif
43
+ }
44
+
45
+ /* call-seq:
46
+ measure_process_time -> float
47
+
48
+ Returns the process time.*/
49
+ static VALUE
50
+ prof_measure_process_time(VALUE self)
51
+ {
52
+ return rb_float_new(measure_process_time());
53
+ }
54
+
55
+ prof_measurer_t* prof_measurer_process_time()
56
+ {
57
+ prof_measurer_t* measure = ALLOC(prof_measurer_t);
58
+ measure->measure = measure_process_time;
59
+ return measure;
60
+ }
61
+
62
+
63
+ void rp_init_measure_process_time()
64
+ {
65
+ rb_define_const(mProf, "CLOCKS_PER_SEC", INT2NUM(CLOCKS_PER_SEC));
66
+ rb_define_const(mProf, "PROCESS_TIME", INT2NUM(MEASURE_PROCESS_TIME));
67
+ rb_define_const(mProf, "PROCESS_TIME_ENABLED", Qtrue);
68
+
69
+ cMeasureProcessTime = rb_define_class_under(mMeasure, "ProcessTime", rb_cObject);
70
+ rb_define_singleton_method(cMeasureProcessTime, "measure", prof_measure_process_time, 0);
71
+ }
@@ -0,0 +1,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 "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
+ }
@@ -0,0 +1,630 @@
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 "ruby_prof.h"
5
+
6
+ #define RP_REL_GET(r, off) ((r) & (1 << (off)))
7
+ #define RP_REL_SET(r, off) \
8
+ do { \
9
+ r |= (1 << (off)); \
10
+ } while (0)
11
+
12
+ VALUE cMethodInfo;
13
+
14
+ /* ================ Helper Functions =================*/
15
+ static VALUE
16
+ figure_singleton_name(VALUE klass)
17
+ {
18
+ volatile VALUE attached, super;
19
+ volatile VALUE attached_str, super_str;
20
+ volatile VALUE result = Qnil;
21
+
22
+ /* We have come across a singleton object. First
23
+ figure out what it is attached to.*/
24
+ 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
+ attached_str = rb_class_name(attached);
30
+ result = rb_str_new2("<Class::");
31
+ rb_str_append(result, attached_str);
32
+ rb_str_cat2(result, ">");
33
+ }
34
+
35
+ /* Is this for singleton methods on a module? */
36
+ else if (BUILTIN_TYPE(attached) == T_MODULE)
37
+ {
38
+ attached_str = rb_class_name(attached);
39
+ result = rb_str_new2("<Module::");
40
+ rb_str_append(result, attached_str);
41
+ rb_str_cat2(result, ">");
42
+ }
43
+
44
+ /* Is this for singleton methods on an object? */
45
+ else if (BUILTIN_TYPE(attached) == T_OBJECT)
46
+ {
47
+ /* Make sure to get the super class so that we don't
48
+ mistakenly grab a T_ICLASS which would lead to
49
+ unknown method errors. */
50
+ super = rb_class_superclass(klass);
51
+ super_str = rb_class_name(super);
52
+ result = rb_str_new2("<Object::");
53
+ rb_str_append(result, super_str);
54
+ rb_str_cat2(result, ">");
55
+ }
56
+
57
+ /* Ok, this could be other things like an array made put onto
58
+ a singleton object (yeah, it happens, see the singleton
59
+ objects test case). */
60
+ else
61
+ {
62
+ result = rb_any_to_s(klass);
63
+ }
64
+
65
+ return result;
66
+ }
67
+
68
+ static VALUE
69
+ klass_name(VALUE klass)
70
+ {
71
+ volatile VALUE result = Qnil;
72
+
73
+ if (klass == 0 || klass == Qnil)
74
+ {
75
+ result = rb_str_new2("[global]");
76
+ }
77
+ else if (BUILTIN_TYPE(klass) == T_MODULE)
78
+ {
79
+ result = rb_class_name(klass);
80
+ }
81
+ else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
82
+ {
83
+ result = figure_singleton_name(klass);
84
+ }
85
+ else if (BUILTIN_TYPE(klass) == T_CLASS)
86
+ {
87
+ result = rb_class_name(klass);
88
+ }
89
+ else
90
+ {
91
+ /* Should never happen. */
92
+ result = rb_str_new2("[unknown]");
93
+ }
94
+
95
+ return result;
96
+ }
97
+
98
+ static VALUE
99
+ method_name(ID mid)
100
+ {
101
+ volatile VALUE name = Qnil;
102
+
103
+ if (RTEST(mid)) {
104
+ name = rb_id2str(mid);
105
+ return rb_str_dup(name);
106
+ } else {
107
+ return rb_str_new2("[no method]");
108
+ }
109
+ }
110
+
111
+ static VALUE
112
+ full_name(VALUE klass, ID mid)
113
+ {
114
+ volatile VALUE klass_str, method_str;
115
+ volatile VALUE result = Qnil;
116
+
117
+ klass_str = klass_name(klass);
118
+ method_str = method_name(mid);
119
+
120
+ result = rb_str_dup(klass_str);
121
+ rb_str_cat2(result, "#");
122
+ rb_str_append(result, method_str);
123
+
124
+ return result;
125
+ }
126
+
127
+ static VALUE
128
+ source_klass_name(VALUE source_klass)
129
+ {
130
+ volatile VALUE klass_str;
131
+ volatile VALUE result = Qnil;
132
+
133
+ if (RTEST(source_klass)) {
134
+ klass_str = rb_class_name(source_klass);
135
+ result = rb_str_dup(klass_str);
136
+ } else {
137
+ result = rb_str_new2("[global]");
138
+ }
139
+
140
+ return result;
141
+ }
142
+
143
+ static VALUE
144
+ calltree_name(VALUE source_klass, int relation, ID mid)
145
+ {
146
+ volatile VALUE klass_str, klass_path, joiner;
147
+ volatile VALUE method_str;
148
+ volatile VALUE result = Qnil;
149
+
150
+ klass_str = source_klass_name(source_klass);
151
+ method_str = method_name(mid);
152
+
153
+ klass_path = rb_str_split(klass_str, "::");
154
+ joiner = rb_str_new2("/");
155
+ result = rb_ary_join(klass_path, joiner);
156
+
157
+ rb_str_cat2(result, "::");
158
+
159
+ if (RP_REL_GET(relation, kObjectSingleton)) {
160
+ rb_str_cat2(result, "*");
161
+ }
162
+
163
+ if (RP_REL_GET(relation, kModuleSingleton)) {
164
+ rb_str_cat2(result, "^");
165
+ }
166
+
167
+ rb_str_append(result, method_str);
168
+
169
+ return result;
170
+ }
171
+
172
+ void
173
+ method_key(prof_method_key_t* key, VALUE klass, ID mid)
174
+ {
175
+ /* Is this an include for a module? If so get the actual
176
+ module class since we want to combine all profiling
177
+ results for that module. */
178
+ if (klass != 0 && BUILTIN_TYPE(klass) == T_ICLASS) {
179
+ klass = RBASIC(klass)->klass;
180
+ }
181
+
182
+ key->klass = klass;
183
+ key->mid = mid;
184
+ key->key = (klass << 4) + (mid << 2);
185
+ }
186
+
187
+ /* ================ prof_method_t =================*/
188
+
189
+ prof_method_t*
190
+ prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
191
+ {
192
+ prof_method_t *result = ALLOC(prof_method_t);
193
+
194
+ result->key = ALLOC(prof_method_key_t);
195
+ method_key(result->key, klass, mid);
196
+
197
+ result->excluded = 0;
198
+ result->recursive = 0;
199
+
200
+ result->call_infos = prof_call_infos_create();
201
+ result->visits = 0;
202
+
203
+ result->object = Qnil;
204
+
205
+ if (source_file != NULL)
206
+ {
207
+ size_t len = strlen(source_file) + 1;
208
+ char *buffer = ALLOC_N(char, len);
209
+
210
+ MEMCPY(buffer, source_file, char, len);
211
+ result->source_file = buffer;
212
+ } else {
213
+ result->source_file = source_file;
214
+ }
215
+
216
+ result->source_klass = Qnil;
217
+ result->line = line;
218
+
219
+ result->resolved = 0;
220
+ result->relation = 0;
221
+
222
+ return result;
223
+ }
224
+
225
+ prof_method_t*
226
+ prof_method_create_excluded(VALUE klass, ID mid)
227
+ {
228
+ prof_method_t *result = ALLOC(prof_method_t);
229
+
230
+ result->key = ALLOC(prof_method_key_t);
231
+ method_key(result->key, klass, mid);
232
+
233
+ /* Invisible with this flag set. */
234
+ result->excluded = 1;
235
+ result->recursive = 0;
236
+
237
+ result->call_infos = 0;
238
+ result->visits = 0;
239
+
240
+ result->object = Qnil;
241
+ result->source_klass = Qnil;
242
+ result->line = 0;
243
+
244
+ result->resolved = 0;
245
+ result->relation = 0;
246
+
247
+ return result;
248
+ }
249
+
250
+ /* The underlying c structures are freed when the parent profile is freed.
251
+ However, on shutdown the Ruby GC frees objects in any will-nilly order.
252
+ That means the ruby thread object wrapping the c thread struct may
253
+ be freed before the parent profile. Thus we add in a free function
254
+ for the garbage collector so that if it does get called will nil
255
+ out our Ruby object reference.*/
256
+ static void
257
+ prof_method_ruby_gc_free(prof_method_t* method)
258
+ {
259
+ /* Has this thread object been accessed by Ruby? If
260
+ yes clean it up so to avoid a segmentation fault. */
261
+ if (method->object != Qnil)
262
+ {
263
+ RDATA(method->object)->data = NULL;
264
+ RDATA(method->object)->dfree = NULL;
265
+ RDATA(method->object)->dmark = NULL;
266
+ }
267
+ method->object = Qnil;
268
+ }
269
+
270
+ static void
271
+ prof_method_free(prof_method_t* method)
272
+ {
273
+ prof_method_ruby_gc_free(method);
274
+
275
+ if (method->call_infos) {
276
+ prof_call_infos_free(method->call_infos);
277
+ xfree(method->call_infos);
278
+ }
279
+
280
+ xfree(method->key);
281
+
282
+ method->key = NULL;
283
+ method->source_klass = Qnil;
284
+
285
+ xfree(method);
286
+ }
287
+
288
+ void
289
+ prof_method_mark(prof_method_t *method)
290
+ {
291
+ if (method->key->klass) {
292
+ rb_gc_mark(method->key->klass);
293
+ }
294
+
295
+ if (method->source_klass) {
296
+ rb_gc_mark(method->source_klass);
297
+ }
298
+
299
+ if (method->object) {
300
+ rb_gc_mark(method->object);
301
+ }
302
+
303
+ if (method->call_infos) {
304
+ prof_call_infos_mark(method->call_infos);
305
+ }
306
+ }
307
+
308
+ static VALUE
309
+ resolve_source_klass(prof_method_t* method)
310
+ {
311
+ volatile VALUE klass, next_klass;
312
+ volatile VALUE attached;
313
+ unsigned int relation;
314
+
315
+ /* We want to group methods according to their source-level
316
+ definitions, not their implementation class. Follow module
317
+ inclusions and singleton classes back to a meaningful root
318
+ while keeping track of these relationships. */
319
+
320
+ if (method->resolved) {
321
+ return method->source_klass;
322
+ }
323
+
324
+ klass = method->key->klass;
325
+ relation = 0;
326
+
327
+ while (1)
328
+ {
329
+ /* This is a global/unknown class */
330
+ if (klass == 0 || klass == Qnil)
331
+ break;
332
+
333
+ /* Is this a singleton class? (most common case) */
334
+ if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
335
+ {
336
+ /* We have come across a singleton object. First
337
+ figure out what it is attached to.*/
338
+ attached = rb_iv_get(klass, "__attached__");
339
+
340
+ /* Is this a singleton class acting as a metaclass?
341
+ Or for singleton methods on a module? */
342
+ if (BUILTIN_TYPE(attached) == T_CLASS ||
343
+ BUILTIN_TYPE(attached) == T_MODULE)
344
+ {
345
+ RP_REL_SET(relation, kModuleSingleton);
346
+ klass = attached;
347
+ }
348
+ /* Is this for singleton methods on an object? */
349
+ else if (BUILTIN_TYPE(attached) == T_OBJECT)
350
+ {
351
+ RP_REL_SET(relation, kObjectSingleton);
352
+ next_klass = rb_class_superclass(klass);
353
+ klass = next_klass;
354
+ }
355
+ /* This is a singleton of an instance of a builtin type. */
356
+ else
357
+ {
358
+ RP_REL_SET(relation, kObjectSingleton);
359
+ next_klass = rb_class_superclass(klass);
360
+ klass = next_klass;
361
+ }
362
+ }
363
+ /* Is this an include for a module? If so get the actual
364
+ module class since we want to combine all profiling
365
+ results for that module. */
366
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
367
+ {
368
+ RP_REL_SET(relation, kModuleIncludee);
369
+ next_klass = RBASIC(klass)->klass;
370
+ klass = next_klass;
371
+ }
372
+ /* No transformations apply; so bail. */
373
+ else
374
+ {
375
+ break;
376
+ }
377
+ }
378
+
379
+ method->resolved = 1;
380
+ method->relation = relation;
381
+ method->source_klass = klass;
382
+
383
+ return klass;
384
+ }
385
+
386
+ VALUE
387
+ prof_method_wrap(prof_method_t *result)
388
+ {
389
+ if (result->object == Qnil) {
390
+ result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_ruby_gc_free, result);
391
+ }
392
+ return result->object;
393
+ }
394
+
395
+ static prof_method_t *
396
+ get_prof_method(VALUE self)
397
+ {
398
+ /* Can't use Data_Get_Struct because that triggers the event hook
399
+ ending up in endless recursion. */
400
+ prof_method_t* result = DATA_PTR(self);
401
+
402
+ if (!result) {
403
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
404
+ }
405
+
406
+ return result;
407
+ }
408
+
409
+ /* ================ Method Table =================*/
410
+ int
411
+ method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2)
412
+ {
413
+ return (key1->klass != key2->klass) || (key1->mid != key2->mid);
414
+ }
415
+
416
+ st_index_t
417
+ method_table_hash(prof_method_key_t *key)
418
+ {
419
+ return key->key;
420
+ }
421
+
422
+ struct st_hash_type type_method_hash = {
423
+ method_table_cmp,
424
+ method_table_hash
425
+ };
426
+
427
+ st_table *
428
+ method_table_create()
429
+ {
430
+ return st_init_table(&type_method_hash);
431
+ }
432
+
433
+ static int
434
+ method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
435
+ {
436
+ prof_method_free((prof_method_t *)value);
437
+ return ST_CONTINUE;
438
+ }
439
+
440
+ void
441
+ method_table_free(st_table *table)
442
+ {
443
+ st_foreach(table, method_table_free_iterator, 0);
444
+ st_free_table(table);
445
+ }
446
+
447
+ size_t
448
+ method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
449
+ {
450
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
451
+ }
452
+
453
+ prof_method_t *
454
+ method_table_lookup(st_table *table, const prof_method_key_t* key)
455
+ {
456
+ st_data_t val;
457
+ if (st_lookup(table, (st_data_t)key, &val)) {
458
+ return (prof_method_t *) val;
459
+ } else {
460
+ return NULL;
461
+ }
462
+ }
463
+
464
+ /* ================ Method Info =================*/
465
+ /* Document-class: RubyProf::MethodInfo
466
+ The RubyProf::MethodInfo class stores profiling data for a method.
467
+ One instance of the RubyProf::MethodInfo class is created per method
468
+ called per thread. Thus, if a method is called in two different
469
+ thread then there will be two RubyProf::MethodInfo objects
470
+ created. RubyProf::MethodInfo objects can be accessed via
471
+ the RubyProf::Profile object.
472
+ */
473
+
474
+ /* call-seq:
475
+ line_no -> int
476
+
477
+ returns the line number of the method */
478
+ static VALUE
479
+ prof_method_line(VALUE self)
480
+ {
481
+ int line = get_prof_method(self)->line;
482
+ return rb_int_new(line);
483
+ }
484
+
485
+ /* call-seq:
486
+ source_file => string
487
+
488
+ return the source file of the method
489
+ */
490
+ static VALUE prof_method_source_file(VALUE self)
491
+ {
492
+ const char* sf = get_prof_method(self)->source_file;
493
+ if(!sf) {
494
+ return rb_str_new2("ruby_runtime");
495
+ } else {
496
+ return rb_str_new2(sf);
497
+ }
498
+ }
499
+
500
+
501
+ /* call-seq:
502
+ method_class -> klass
503
+
504
+ Returns the Ruby klass that owns this method. */
505
+ static VALUE
506
+ prof_method_klass(VALUE self)
507
+ {
508
+ prof_method_t *result = get_prof_method(self);
509
+ return result->key->klass;
510
+ }
511
+
512
+ /* call-seq:
513
+ method_id -> ID
514
+
515
+ Returns the id of this method. */
516
+ static VALUE
517
+ prof_method_id(VALUE self)
518
+ {
519
+ prof_method_t *result = get_prof_method(self);
520
+ return ID2SYM(result->key->mid);
521
+ }
522
+
523
+ /* call-seq:
524
+ klass_name -> string
525
+
526
+ Returns the name of this method's class. Singleton classes
527
+ will have the form <Object::Object>. */
528
+
529
+ static VALUE
530
+ prof_klass_name(VALUE self)
531
+ {
532
+ prof_method_t *method = get_prof_method(self);
533
+ return klass_name(method->key->klass);
534
+ }
535
+
536
+ /* call-seq:
537
+ method_name -> string
538
+
539
+ Returns the name of this method in the format Object#method. Singletons
540
+ methods will be returned in the format <Object::Object>#method.*/
541
+
542
+ static VALUE
543
+ prof_method_name(VALUE self)
544
+ {
545
+ prof_method_t *method = get_prof_method(self);
546
+ return method_name(method->key->mid);
547
+ }
548
+
549
+ /* call-seq:
550
+ full_name -> string
551
+
552
+ Returns the full name of this method in the format Object#method.*/
553
+
554
+ static VALUE
555
+ prof_full_name(VALUE self)
556
+ {
557
+ prof_method_t *method = get_prof_method(self);
558
+ return full_name(method->key->klass, method->key->mid);
559
+ }
560
+
561
+ /* call-seq:
562
+ call_infos -> Array of call_info
563
+
564
+ Returns an array of call info objects that contain profiling information
565
+ about the current method.*/
566
+ static VALUE
567
+ prof_method_call_infos(VALUE self)
568
+ {
569
+ prof_method_t *method = get_prof_method(self);
570
+ if (method->call_infos->object == Qnil) {
571
+ method->call_infos->object = prof_call_infos_wrap(method->call_infos);
572
+ }
573
+ return method->call_infos->object;
574
+ }
575
+
576
+ /* call-seq:
577
+ recursive? -> boolean
578
+
579
+ Returns the true if this method is recursive */
580
+ static VALUE
581
+ prof_method_recursive(VALUE self)
582
+ {
583
+ prof_method_t *method = get_prof_method(self);
584
+ return method->recursive ? Qtrue : Qfalse;
585
+ }
586
+
587
+ /* call-seq:
588
+ source_klass -> klass
589
+
590
+ Returns the Ruby klass of the natural source-level definition. */
591
+ static VALUE
592
+ prof_source_klass(VALUE self)
593
+ {
594
+ prof_method_t *method = get_prof_method(self);
595
+ return resolve_source_klass(method);
596
+ }
597
+
598
+ /* call-seq:
599
+ calltree_name -> string
600
+
601
+ Returns the full name of this method in the calltree format.*/
602
+
603
+ static VALUE
604
+ prof_calltree_name(VALUE self)
605
+ {
606
+ prof_method_t *method = get_prof_method(self);
607
+ volatile VALUE source_klass = resolve_source_klass(method);
608
+ return calltree_name(source_klass, method->relation, method->key->mid);
609
+ }
610
+
611
+ void rp_init_method_info()
612
+ {
613
+ /* MethodInfo */
614
+ cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
615
+ rb_undef_method(CLASS_OF(cMethodInfo), "new");
616
+
617
+ rb_define_method(cMethodInfo, "klass", prof_method_klass, 0);
618
+ rb_define_method(cMethodInfo, "klass_name", prof_klass_name, 0);
619
+ rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
620
+ rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
621
+ rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
622
+
623
+ rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
624
+ rb_define_method(cMethodInfo, "source_klass", prof_source_klass, 0);
625
+ rb_define_method(cMethodInfo, "source_file", prof_method_source_file, 0);
626
+ rb_define_method(cMethodInfo, "line", prof_method_line, 0);
627
+
628
+ rb_define_method(cMethodInfo, "recursive?", prof_method_recursive, 0);
629
+ rb_define_method(cMethodInfo, "calltree_name", prof_calltree_name, 0);
630
+ }