ruby-prof 0.16.2 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +15 -0
  3. data/README.rdoc +36 -5
  4. data/bin/ruby-prof +7 -2
  5. data/doc/LICENSE.html +2 -1
  6. data/doc/README_rdoc.html +42 -8
  7. data/doc/Rack.html +2 -1
  8. data/doc/Rack/RubyProf.html +25 -18
  9. data/doc/Rack/RubyProf/RackProfiler.html +343 -0
  10. data/doc/RubyProf.html +14 -2
  11. data/doc/RubyProf/AbstractPrinter.html +91 -12
  12. data/doc/RubyProf/AggregateCallInfo.html +2 -1
  13. data/doc/RubyProf/CallInfo.html +18 -78
  14. data/doc/RubyProf/CallInfoPrinter.html +2 -1
  15. data/doc/RubyProf/CallInfoVisitor.html +2 -1
  16. data/doc/RubyProf/CallStackPrinter.html +35 -29
  17. data/doc/RubyProf/CallTreePrinter.html +98 -14
  18. data/doc/RubyProf/Cmd.html +11 -5
  19. data/doc/RubyProf/DeprecationWarnings.html +148 -0
  20. data/doc/RubyProf/DotPrinter.html +2 -1
  21. data/doc/RubyProf/FlatPrinter.html +2 -1
  22. data/doc/RubyProf/FlatPrinterWithLineNumbers.html +7 -5
  23. data/doc/RubyProf/GraphHtmlPrinter.html +18 -12
  24. data/doc/RubyProf/GraphPrinter.html +2 -1
  25. data/doc/RubyProf/MethodInfo.html +19 -88
  26. data/doc/RubyProf/MultiPrinter.html +231 -17
  27. data/doc/RubyProf/Profile.html +184 -39
  28. data/doc/RubyProf/Profile/ExcludeCommonMethods.html +411 -0
  29. data/doc/RubyProf/Profile/LegacyMethodElimination.html +158 -0
  30. data/doc/RubyProf/ProfileTask.html +2 -1
  31. data/doc/RubyProf/Thread.html +4 -39
  32. data/doc/created.rid +21 -19
  33. data/doc/css/fonts.css +6 -6
  34. data/doc/examples/flat_txt.html +2 -1
  35. data/doc/examples/graph_html.html +2 -1
  36. data/doc/examples/graph_txt.html +2 -1
  37. data/doc/index.html +47 -7
  38. data/doc/js/darkfish.js +7 -7
  39. data/doc/js/search_index.js +1 -1
  40. data/doc/js/search_index.js.gz +0 -0
  41. data/doc/js/searcher.js +1 -0
  42. data/doc/js/searcher.js.gz +0 -0
  43. data/doc/table_of_contents.html +190 -80
  44. data/ext/ruby_prof/extconf.rb +4 -0
  45. data/ext/ruby_prof/rp_call_info.c +19 -1
  46. data/ext/ruby_prof/rp_call_info.h +8 -3
  47. data/ext/ruby_prof/rp_method.c +282 -57
  48. data/ext/ruby_prof/rp_method.h +28 -5
  49. data/ext/ruby_prof/rp_stack.c +69 -24
  50. data/ext/ruby_prof/rp_stack.h +21 -9
  51. data/ext/ruby_prof/rp_thread.c +4 -1
  52. data/ext/ruby_prof/ruby_prof.c +142 -39
  53. data/ext/ruby_prof/ruby_prof.h +3 -0
  54. data/lib/ruby-prof.rb +10 -0
  55. data/lib/ruby-prof/call_info.rb +0 -11
  56. data/lib/ruby-prof/method_info.rb +4 -12
  57. data/lib/ruby-prof/printers/abstract_printer.rb +19 -1
  58. data/lib/ruby-prof/printers/call_info_printer.rb +1 -1
  59. data/lib/ruby-prof/printers/call_stack_printer.rb +9 -4
  60. data/lib/ruby-prof/printers/call_tree_printer.rb +15 -2
  61. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +23 -4
  62. data/lib/ruby-prof/printers/graph_html_printer.rb +10 -5
  63. data/lib/ruby-prof/printers/graph_printer.rb +2 -2
  64. data/lib/ruby-prof/printers/multi_printer.rb +44 -18
  65. data/lib/ruby-prof/profile.rb +13 -42
  66. data/lib/ruby-prof/profile/exclude_common_methods.rb +201 -0
  67. data/lib/ruby-prof/profile/legacy_method_elimination.rb +49 -0
  68. data/lib/ruby-prof/rack.rb +130 -51
  69. data/lib/ruby-prof/thread.rb +0 -6
  70. data/lib/ruby-prof/version.rb +1 -1
  71. data/ruby-prof.gemspec +4 -3
  72. data/test/aggregate_test.rb +1 -1
  73. data/test/exclude_methods_test.rb +146 -0
  74. data/test/line_number_test.rb +12 -3
  75. data/test/multi_printer_test.rb +23 -2
  76. data/test/no_method_class_test.rb +1 -1
  77. data/test/printers_test.rb +21 -1
  78. data/test/rack_test.rb +64 -0
  79. data/test/recursive_test.rb +15 -15
  80. data/test/test_helper.rb +11 -0
  81. metadata +20 -13
@@ -10,9 +10,13 @@ if RUBY_VERSION < "1.9.3"
10
10
  exit(1)
11
11
  end
12
12
 
13
+ # For the love of bitfields...
14
+ $CFLAGS += ' -std=c99'
15
+
13
16
  # standard ruby methods
14
17
  have_func("rb_gc_stat")
15
18
  have_func("rb_gc_count")
19
+ have_func("rb_remove_event_hook_with_data")
16
20
 
17
21
  # Alexander Dymo GC patch
18
22
  have_func("rb_os_allocated_objects")
@@ -23,11 +23,16 @@ prof_call_info_create(prof_method_t* method, prof_call_info_t* parent)
23
23
  result->call_infos = call_info_table_create();
24
24
  result->children = Qnil;
25
25
 
26
- result->called = 0;
27
26
  result->total_time = 0;
28
27
  result->self_time = 0;
29
28
  result->wait_time = 0;
29
+
30
+ result->called = 0;
31
+
32
+ result->recursive = 0;
33
+ result->depth = 0;
30
34
  result->line = 0;
35
+
31
36
  return result;
32
37
  }
33
38
  static void
@@ -162,6 +167,17 @@ prof_call_info_set_called(VALUE self, VALUE called)
162
167
  return called;
163
168
  }
164
169
 
170
+ /* call-seq:
171
+ recursive? -> boolean
172
+
173
+ Returns the true if this call info is a recursive invocation */
174
+ static VALUE
175
+ prof_call_info_recursive(VALUE self)
176
+ {
177
+ prof_call_info_t *result = prof_get_call_info(self);
178
+ return result->recursive ? Qtrue : Qfalse;
179
+ }
180
+
165
181
  /* call-seq:
166
182
  depth -> int
167
183
 
@@ -402,6 +418,8 @@ void rp_init_call_info()
402
418
  rb_define_method(cCallInfo, "add_self_time", prof_call_info_add_self_time, 1);
403
419
  rb_define_method(cCallInfo, "wait_time", prof_call_info_wait_time, 0);
404
420
  rb_define_method(cCallInfo, "add_wait_time", prof_call_info_add_wait_time, 1);
421
+
422
+ rb_define_method(cCallInfo, "recursive?", prof_call_info_recursive, 0);
405
423
  rb_define_method(cCallInfo, "depth", prof_call_info_depth, 0);
406
424
  rb_define_method(cCallInfo, "line", prof_call_info_line, 0);
407
425
  }
@@ -15,14 +15,19 @@ typedef struct prof_call_info_t
15
15
  prof_method_t *target; /* Use target instead of method to avoid conflict with Ruby method */
16
16
  struct prof_call_info_t *parent;
17
17
  st_table *call_infos;
18
- int called;
19
- int depth;
18
+
20
19
  double total_time;
21
20
  double self_time;
22
21
  double wait_time;
23
- int line;
22
+
24
23
  VALUE object;
25
24
  VALUE children;
25
+
26
+ int called;
27
+
28
+ unsigned int recursive : 1;
29
+ unsigned int depth : 15;
30
+ unsigned int line : 16;
26
31
  } prof_call_info_t;
27
32
 
28
33
  /* Array of call_info objects */
@@ -3,31 +3,41 @@
3
3
 
4
4
  #include "ruby_prof.h"
5
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
+
6
12
  VALUE cMethodInfo;
7
13
 
8
14
  /* ================ Helper Functions =================*/
9
15
  static VALUE
10
16
  figure_singleton_name(VALUE klass)
11
17
  {
12
- VALUE result = Qnil;
18
+ volatile VALUE attached, super;
19
+ volatile VALUE attached_str, super_str;
20
+ volatile VALUE result = Qnil;
13
21
 
14
22
  /* We have come across a singleton object. First
15
23
  figure out what it is attached to.*/
16
- VALUE attached = rb_iv_get(klass, "__attached__");
24
+ attached = rb_iv_get(klass, "__attached__");
17
25
 
18
26
  /* Is this a singleton class acting as a metaclass? */
19
27
  if (BUILTIN_TYPE(attached) == T_CLASS)
20
28
  {
29
+ attached_str = rb_class_name(attached);
21
30
  result = rb_str_new2("<Class::");
22
- rb_str_append(result, rb_inspect(attached));
31
+ rb_str_append(result, attached_str);
23
32
  rb_str_cat2(result, ">");
24
33
  }
25
34
 
26
35
  /* Is this for singleton methods on a module? */
27
36
  else if (BUILTIN_TYPE(attached) == T_MODULE)
28
37
  {
38
+ attached_str = rb_class_name(attached);
29
39
  result = rb_str_new2("<Module::");
30
- rb_str_append(result, rb_inspect(attached));
40
+ rb_str_append(result, attached_str);
31
41
  rb_str_cat2(result, ">");
32
42
  }
33
43
 
@@ -37,9 +47,10 @@ figure_singleton_name(VALUE klass)
37
47
  /* Make sure to get the super class so that we don't
38
48
  mistakenly grab a T_ICLASS which would lead to
39
49
  unknown method errors. */
40
- VALUE super = rb_class_superclass(klass);
50
+ super = rb_class_superclass(klass);
51
+ super_str = rb_class_name(super);
41
52
  result = rb_str_new2("<Object::");
42
- rb_str_append(result, rb_inspect(super));
53
+ rb_str_append(result, super_str);
43
54
  rb_str_cat2(result, ">");
44
55
  }
45
56
 
@@ -48,7 +59,7 @@ figure_singleton_name(VALUE klass)
48
59
  objects test case). */
49
60
  else
50
61
  {
51
- result = rb_inspect(klass);
62
+ result = rb_any_to_s(klass);
52
63
  }
53
64
 
54
65
  return result;
@@ -57,15 +68,15 @@ figure_singleton_name(VALUE klass)
57
68
  static VALUE
58
69
  klass_name(VALUE klass)
59
70
  {
60
- VALUE result = Qnil;
71
+ volatile VALUE result = Qnil;
61
72
 
62
73
  if (klass == 0 || klass == Qnil)
63
74
  {
64
- result = rb_str_new2("Global");
75
+ result = rb_str_new2("[global]");
65
76
  }
66
77
  else if (BUILTIN_TYPE(klass) == T_MODULE)
67
78
  {
68
- result = rb_inspect(klass);
79
+ result = rb_class_name(klass);
69
80
  }
70
81
  else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
71
82
  {
@@ -73,12 +84,12 @@ klass_name(VALUE klass)
73
84
  }
74
85
  else if (BUILTIN_TYPE(klass) == T_CLASS)
75
86
  {
76
- result = rb_inspect(klass);
87
+ result = rb_class_name(klass);
77
88
  }
78
89
  else
79
90
  {
80
91
  /* Should never happen. */
81
- result = rb_str_new2("Unknown");
92
+ result = rb_str_new2("[unknown]");
82
93
  }
83
94
 
84
95
  return result;
@@ -87,26 +98,79 @@ klass_name(VALUE klass)
87
98
  static VALUE
88
99
  method_name(ID mid)
89
100
  {
90
- VALUE result;
101
+ volatile VALUE name = Qnil;
91
102
 
92
- if (mid == 0)
93
- result = rb_str_new2("[No method]");
94
103
  #ifdef ID_ALLOCATOR
95
- else if (mid == ID_ALLOCATOR)
96
- result = rb_str_new2("allocate");
104
+ if (mid == ID_ALLOCATOR) {
105
+ return rb_str_new2("allocate");
106
+ }
97
107
  #endif
98
- else
99
- result = rb_String(ID2SYM(mid));
100
108
 
101
- return result;
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
+ }
102
115
  }
103
116
 
104
117
  static VALUE
105
118
  full_name(VALUE klass, ID mid)
106
119
  {
107
- VALUE result = rb_str_dup(klass_name(klass));
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);
108
127
  rb_str_cat2(result, "#");
109
- rb_str_append(result, method_name(mid));
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
+ calltree_name(VALUE source_klass, int relation, ID mid)
151
+ {
152
+ volatile VALUE klass_str, klass_path, joiner;
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
+
159
+ klass_path = rb_str_split(klass_str, "::");
160
+ joiner = rb_str_new2("/");
161
+ result = rb_ary_join(klass_path, joiner);
162
+
163
+ rb_str_cat2(result, "::");
164
+
165
+ if (RP_REL_GET(relation, kObjectSingleton)) {
166
+ rb_str_cat2(result, "*");
167
+ }
168
+
169
+ if (RP_REL_GET(relation, kModuleSingleton)) {
170
+ rb_str_cat2(result, "^");
171
+ }
172
+
173
+ rb_str_append(result, method_str);
110
174
 
111
175
  return result;
112
176
  }
@@ -117,8 +181,9 @@ method_key(prof_method_key_t* key, VALUE klass, ID mid)
117
181
  /* Is this an include for a module? If so get the actual
118
182
  module class since we want to combine all profiling
119
183
  results for that module. */
120
- if (klass != 0)
121
- klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
184
+ if (klass != 0 && BUILTIN_TYPE(klass) == T_ICLASS) {
185
+ klass = RBASIC(klass)->klass;
186
+ }
122
187
 
123
188
  key->klass = klass;
124
189
  key->mid = mid;
@@ -126,17 +191,22 @@ method_key(prof_method_key_t* key, VALUE klass, ID mid)
126
191
  }
127
192
 
128
193
  /* ================ prof_method_t =================*/
194
+
129
195
  prof_method_t*
130
196
  prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
131
197
  {
132
198
  prof_method_t *result = ALLOC(prof_method_t);
133
- result->object = Qnil;
134
- result->call_infos = prof_call_infos_create();
135
199
 
136
200
  result->key = ALLOC(prof_method_key_t);
137
201
  method_key(result->key, klass, mid);
138
202
 
139
- //result->call_info_table = call_info_table_create();
203
+ result->excluded = 0;
204
+ result->recursive = 0;
205
+
206
+ result->call_infos = prof_call_infos_create();
207
+ result->visits = 0;
208
+
209
+ result->object = Qnil;
140
210
 
141
211
  if (source_file != NULL)
142
212
  {
@@ -145,21 +215,49 @@ prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
145
215
 
146
216
  MEMCPY(buffer, source_file, char, len);
147
217
  result->source_file = buffer;
148
- }
149
- else
150
- {
218
+ } else {
151
219
  result->source_file = source_file;
152
220
  }
221
+
222
+ result->source_klass = Qnil;
153
223
  result->line = line;
154
224
 
225
+ result->resolved = 0;
226
+ result->relation = 0;
227
+
155
228
  return result;
156
229
  }
157
230
 
158
- /* The underlying c structures are freed when the parent profile is freed.
231
+ prof_method_t*
232
+ prof_method_create_excluded(VALUE klass, ID mid)
233
+ {
234
+ prof_method_t *result = ALLOC(prof_method_t);
235
+
236
+ result->key = ALLOC(prof_method_key_t);
237
+ method_key(result->key, klass, mid);
238
+
239
+ /* Invisible with this flag set. */
240
+ result->excluded = 1;
241
+ result->recursive = 0;
242
+
243
+ result->call_infos = 0;
244
+ result->visits = 0;
245
+
246
+ result->object = Qnil;
247
+ result->source_klass = Qnil;
248
+ result->line = 0;
249
+
250
+ result->resolved = 0;
251
+ result->relation = 0;
252
+
253
+ return result;
254
+ }
255
+
256
+ /* The underlying c structures are freed when the parent profile is freed.
159
257
  However, on shutdown the Ruby GC frees objects in any will-nilly order.
160
258
  That means the ruby thread object wrapping the c thread struct may
161
259
  be freed before the parent profile. Thus we add in a free function
162
- for the garbage collector so that if it does get called will nil
260
+ for the garbage collector so that if it does get called will nil
163
261
  out our Ruby object reference.*/
164
262
  static void
165
263
  prof_method_ruby_gc_free(prof_method_t* method)
@@ -171,7 +269,7 @@ prof_method_ruby_gc_free(prof_method_t* method)
171
269
  RDATA(method->object)->data = NULL;
172
270
  RDATA(method->object)->dfree = NULL;
173
271
  RDATA(method->object)->dmark = NULL;
174
- }
272
+ }
175
273
  method->object = Qnil;
176
274
  }
177
275
 
@@ -179,11 +277,16 @@ static void
179
277
  prof_method_free(prof_method_t* method)
180
278
  {
181
279
  prof_method_ruby_gc_free(method);
182
- prof_call_infos_free(method->call_infos);
183
- xfree(method->call_infos);
280
+
281
+ if (method->call_infos) {
282
+ prof_call_infos_free(method->call_infos);
283
+ xfree(method->call_infos);
284
+ }
184
285
 
185
286
  xfree(method->key);
287
+
186
288
  method->key = NULL;
289
+ method->source_klass = Qnil;
187
290
 
188
291
  xfree(method);
189
292
  }
@@ -191,17 +294,105 @@ prof_method_free(prof_method_t* method)
191
294
  void
192
295
  prof_method_mark(prof_method_t *method)
193
296
  {
194
- if (method->object)
297
+ if (method->key->klass) {
298
+ rb_gc_mark(method->key->klass);
299
+ }
300
+
301
+ if (method->source_klass) {
302
+ rb_gc_mark(method->source_klass);
303
+ }
304
+
305
+ if (method->object) {
195
306
  rb_gc_mark(method->object);
307
+ }
196
308
 
197
- prof_call_infos_mark(method->call_infos);
309
+ if (method->call_infos) {
310
+ prof_call_infos_mark(method->call_infos);
311
+ }
312
+ }
313
+
314
+ static VALUE
315
+ resolve_source_klass(prof_method_t* method)
316
+ {
317
+ volatile VALUE klass, next_klass;
318
+ volatile VALUE attached;
319
+ unsigned int relation;
320
+
321
+ /* We want to group methods according to their source-level
322
+ definitions, not their implementation class. Follow module
323
+ inclusions and singleton classes back to a meaningful root
324
+ while keeping track of these relationships. */
325
+
326
+ if (method->resolved) {
327
+ return method->source_klass;
328
+ }
329
+
330
+ klass = method->key->klass;
331
+ relation = 0;
332
+
333
+ while (1)
334
+ {
335
+ /* This is a global/unknown class */
336
+ if (klass == 0 || klass == Qnil)
337
+ break;
338
+
339
+ /* Is this a singleton class? (most common case) */
340
+ if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
341
+ {
342
+ /* We have come across a singleton object. First
343
+ figure out what it is attached to.*/
344
+ attached = rb_iv_get(klass, "__attached__");
345
+
346
+ /* Is this a singleton class acting as a metaclass?
347
+ Or for singleton methods on a module? */
348
+ if (BUILTIN_TYPE(attached) == T_CLASS ||
349
+ BUILTIN_TYPE(attached) == T_MODULE)
350
+ {
351
+ RP_REL_SET(relation, kModuleSingleton);
352
+ klass = attached;
353
+ }
354
+ /* Is this for singleton methods on an object? */
355
+ else if (BUILTIN_TYPE(attached) == T_OBJECT)
356
+ {
357
+ RP_REL_SET(relation, kObjectSingleton);
358
+ next_klass = rb_class_superclass(klass);
359
+ klass = next_klass;
360
+ }
361
+ /* This is a singleton of an instance of a builtin type. */
362
+ else
363
+ {
364
+ RP_REL_SET(relation, kObjectSingleton);
365
+ next_klass = rb_class_superclass(klass);
366
+ klass = next_klass;
367
+ }
368
+ }
369
+ /* Is this an include for a module? If so get the actual
370
+ module class since we want to combine all profiling
371
+ results for that module. */
372
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
373
+ {
374
+ RP_REL_SET(relation, kModuleIncludee);
375
+ next_klass = RBASIC(klass)->klass;
376
+ klass = next_klass;
377
+ }
378
+ /* No transformations apply; so bail. */
379
+ else
380
+ {
381
+ break;
382
+ }
383
+ }
384
+
385
+ method->resolved = 1;
386
+ method->relation = relation;
387
+ method->source_klass = klass;
388
+
389
+ return klass;
198
390
  }
199
391
 
200
392
  VALUE
201
393
  prof_method_wrap(prof_method_t *result)
202
394
  {
203
- if (result->object == Qnil)
204
- {
395
+ if (result->object == Qnil) {
205
396
  result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_ruby_gc_free, result);
206
397
  }
207
398
  return result->object;
@@ -214,8 +405,9 @@ get_prof_method(VALUE self)
214
405
  ending up in endless recursion. */
215
406
  prof_method_t* result = DATA_PTR(self);
216
407
 
217
- if (!result)
408
+ if (!result) {
218
409
  rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
410
+ }
219
411
 
220
412
  return result;
221
413
  }
@@ -247,7 +439,7 @@ method_table_create()
247
439
  static int
248
440
  method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
249
441
  {
250
- prof_method_free((prof_method_t*)value);
442
+ prof_method_free((prof_method_t *)value);
251
443
  return ST_CONTINUE;
252
444
  }
253
445
 
@@ -258,7 +450,6 @@ method_table_free(st_table *table)
258
450
  st_free_table(table);
259
451
  }
260
452
 
261
-
262
453
  size_t
263
454
  method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
264
455
  {
@@ -269,12 +460,9 @@ prof_method_t *
269
460
  method_table_lookup(st_table *table, const prof_method_key_t* key)
270
461
  {
271
462
  st_data_t val;
272
- if (st_lookup(table, (st_data_t)key, &val))
273
- {
463
+ if (st_lookup(table, (st_data_t)key, &val)) {
274
464
  return (prof_method_t *) val;
275
- }
276
- else
277
- {
465
+ } else {
278
466
  return NULL;
279
467
  }
280
468
  }
@@ -296,7 +484,8 @@ the RubyProf::Profile object.
296
484
  static VALUE
297
485
  prof_method_line(VALUE self)
298
486
  {
299
- return rb_int_new(get_prof_method(self)->line);
487
+ int line = get_prof_method(self)->line;
488
+ return rb_int_new(line);
300
489
  }
301
490
 
302
491
  /* call-seq:
@@ -307,12 +496,9 @@ return the source file of the method
307
496
  static VALUE prof_method_source_file(VALUE self)
308
497
  {
309
498
  const char* sf = get_prof_method(self)->source_file;
310
- if(!sf)
311
- {
499
+ if(!sf) {
312
500
  return rb_str_new2("ruby_runtime");
313
- }
314
- else
315
- {
501
+ } else {
316
502
  return rb_str_new2(sf);
317
503
  }
318
504
  }
@@ -387,13 +573,47 @@ static VALUE
387
573
  prof_method_call_infos(VALUE self)
388
574
  {
389
575
  prof_method_t *method = get_prof_method(self);
390
- if (method->call_infos->object == Qnil)
391
- {
576
+ if (method->call_infos->object == Qnil) {
392
577
  method->call_infos->object = prof_call_infos_wrap(method->call_infos);
393
578
  }
394
579
  return method->call_infos->object;
395
580
  }
396
581
 
582
+ /* call-seq:
583
+ recursive? -> boolean
584
+
585
+ Returns the true if this method is recursive */
586
+ static VALUE
587
+ prof_method_recursive(VALUE self)
588
+ {
589
+ prof_method_t *method = get_prof_method(self);
590
+ return method->recursive ? Qtrue : Qfalse;
591
+ }
592
+
593
+ /* call-seq:
594
+ source_klass -> klass
595
+
596
+ Returns the Ruby klass of the natural source-level definition. */
597
+ static VALUE
598
+ prof_source_klass(VALUE self)
599
+ {
600
+ prof_method_t *method = get_prof_method(self);
601
+ return resolve_source_klass(method);
602
+ }
603
+
604
+ /* call-seq:
605
+ calltree_name -> string
606
+
607
+ Returns the full name of this method in the calltree format.*/
608
+
609
+ static VALUE
610
+ prof_calltree_name(VALUE self)
611
+ {
612
+ prof_method_t *method = get_prof_method(self);
613
+ volatile VALUE source_klass = resolve_source_klass(method);
614
+ return calltree_name(source_klass, method->relation, method->key->mid);
615
+ }
616
+
397
617
  void rp_init_method_info()
398
618
  {
399
619
  /* MethodInfo */
@@ -405,7 +625,12 @@ void rp_init_method_info()
405
625
  rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
406
626
  rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
407
627
  rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
408
- rb_define_method(cMethodInfo, "source_file", prof_method_source_file,0);
409
- rb_define_method(cMethodInfo, "line", prof_method_line, 0);
628
+
410
629
  rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
630
+ rb_define_method(cMethodInfo, "source_klass", prof_source_klass, 0);
631
+ rb_define_method(cMethodInfo, "source_file", prof_method_source_file, 0);
632
+ rb_define_method(cMethodInfo, "line", prof_method_line, 0);
633
+
634
+ rb_define_method(cMethodInfo, "recursive?", prof_method_recursive, 0);
635
+ rb_define_method(cMethodInfo, "calltree_name", prof_calltree_name, 0);
411
636
  }