ruby-prof 0.18.0-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
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
+ }