airbnb-ruby-prof 0.0.1

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