airbnb-ruby-prof 0.0.1

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