ruby-prof 0.16.2 → 0.17.0

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