rdp-ruby-prof 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/CHANGES +202 -0
  2. data/LICENSE +23 -0
  3. data/README +445 -0
  4. data/Rakefile +123 -0
  5. data/bin/ruby-prof +207 -0
  6. data/examples/flat.txt +55 -0
  7. data/examples/graph.html +823 -0
  8. data/examples/graph.txt +170 -0
  9. data/ext/#ruby_prof.c# +1679 -0
  10. data/ext/Makefile +180 -0
  11. data/ext/extconf.rb +40 -0
  12. data/ext/measure_allocations.h +58 -0
  13. data/ext/measure_cpu_time.h +152 -0
  14. data/ext/measure_gc_runs.h +76 -0
  15. data/ext/measure_gc_time.h +57 -0
  16. data/ext/measure_memory.h +101 -0
  17. data/ext/measure_process_time.h +52 -0
  18. data/ext/measure_wall_time.h +53 -0
  19. data/ext/mingw/Rakefile +23 -0
  20. data/ext/mingw/build.rake +38 -0
  21. data/ext/ruby_prof.c +1707 -0
  22. data/ext/ruby_prof.e +19984 -0
  23. data/ext/ruby_prof.h +188 -0
  24. data/ext/vc/ruby_prof.sln +20 -0
  25. data/ext/vc/ruby_prof.vcproj +241 -0
  26. data/ext/version.h +4 -0
  27. data/lib/ruby-prof.rb +48 -0
  28. data/lib/ruby-prof/abstract_printer.rb +41 -0
  29. data/lib/ruby-prof/aggregate_call_info.rb +62 -0
  30. data/lib/ruby-prof/call_info.rb +47 -0
  31. data/lib/ruby-prof/call_tree_printer.rb +84 -0
  32. data/lib/ruby-prof/flat_printer.rb +79 -0
  33. data/lib/ruby-prof/graph_html_printer.rb +256 -0
  34. data/lib/ruby-prof/graph_html_printer.rb.orig +256 -0
  35. data/lib/ruby-prof/graph_html_printer.rb.rej +34 -0
  36. data/lib/ruby-prof/graph_printer.rb +164 -0
  37. data/lib/ruby-prof/graph_printer.rb.orig +164 -0
  38. data/lib/ruby-prof/method_info.rb +111 -0
  39. data/lib/ruby-prof/task.rb +146 -0
  40. data/lib/ruby-prof/test.rb +148 -0
  41. data/lib/unprof.rb +8 -0
  42. data/rails/environment/profile.rb +24 -0
  43. data/rails/example/example_test.rb +9 -0
  44. data/rails/profile_test_helper.rb +21 -0
  45. data/test/aggregate_test.rb +121 -0
  46. data/test/basic_test.rb +283 -0
  47. data/test/duplicate_names_test.rb +32 -0
  48. data/test/exceptions_test.rb +15 -0
  49. data/test/exclude_threads_test.rb +54 -0
  50. data/test/line_number_test.rb +73 -0
  51. data/test/measurement_test.rb +121 -0
  52. data/test/module_test.rb +54 -0
  53. data/test/no_method_class_test.rb +13 -0
  54. data/test/prime.rb +58 -0
  55. data/test/prime_test.rb +13 -0
  56. data/test/printers_test.rb +71 -0
  57. data/test/recursive_test.rb +254 -0
  58. data/test/singleton_test.rb +37 -0
  59. data/test/stack_test.rb +138 -0
  60. data/test/start_stop_test.rb +95 -0
  61. data/test/test_suite.rb +23 -0
  62. data/test/thread_test.rb +159 -0
  63. data/test/unique_call_path_test.rb +206 -0
  64. metadata +124 -0
@@ -0,0 +1,170 @@
1
+ = Graph Profiles
2
+
3
+ Graph profiles show how long each method runs, which methods call it
4
+ and which methods it calls.
5
+
6
+ As an example, here is the output from running printers_test.rb:
7
+
8
+
9
+ Thread ID: 21277412
10
+ %total %self total self children calls Name
11
+ --------------------------------------------------------------------------------
12
+ 100.00% 0.00% 8.77 0.00 8.77 1 #toplevel
13
+ 8.77 0.00 8.77 1/1 Object#run_primes
14
+ --------------------------------------------------------------------------------
15
+ 8.77 0.00 8.77 1/1 #toplevel
16
+ 100.00% 0.00% 8.77 0.00 8.77 1 Object#run_primes
17
+ 0.02 0.00 0.02 1/1 Object#make_random_array
18
+ 2.09 0.00 2.09 1/1 Object#find_largest
19
+ 6.66 0.00 6.66 1/1 Object#find_primes
20
+ --------------------------------------------------------------------------------
21
+ 6.63 4.06 2.56 500/501 Object#is_prime
22
+ 2.09 0.00 2.09 1/501 Object#find_largest
23
+ 99.48% 46.34% 8.72 4.06 4.66 501 Integer#upto
24
+ 0.00 0.00 0.00 61/61 Array#[]
25
+ 0.00 0.00 0.00 61/61 Fixnum#>
26
+ 2.09 2.09 0.00 61/61 Kernel.sleep
27
+ 1.24 1.24 0.00 250862/250862 Fixnum#==
28
+ 1.33 1.33 0.00 250862/250862 Fixnum#%
29
+ --------------------------------------------------------------------------------
30
+ 6.66 0.01 6.64 1/1 Object#find_primes
31
+ 75.93% 0.17% 6.66 0.01 6.64 1 Array#select
32
+ 6.64 0.01 6.63 500/500 Object#is_prime
33
+ --------------------------------------------------------------------------------
34
+ 6.66 0.00 6.66 1/1 Object#run_primes
35
+ 75.93% 0.00% 6.66 0.00 6.66 1 Object#find_primes
36
+ 6.66 0.01 6.64 1/1 Array#select
37
+ --------------------------------------------------------------------------------
38
+ 6.64 0.01 6.63 500/500 Array#select
39
+ 75.76% 0.17% 6.64 0.01 6.63 500 Object#is_prime
40
+ 0.00 0.00 0.00 500/501 Fixnum#-
41
+ 6.63 4.06 2.56 500/501 Integer#upto
42
+ --------------------------------------------------------------------------------
43
+ 2.09 0.00 2.09 1/1 Object#run_primes
44
+ 23.89% 0.00% 2.09 0.00 2.09 1 Object#find_largest
45
+ 0.00 0.00 0.00 1/501 Fixnum#-
46
+ 2.09 0.00 2.09 1/501 Integer#upto
47
+ 0.00 0.00 0.00 1/1 Array#first
48
+ 0.00 0.00 0.00 1/1 Array#length
49
+ --------------------------------------------------------------------------------
50
+ 2.09 2.09 0.00 61/61 Integer#upto
51
+ 23.89% 23.89% 2.09 2.09 0.00 61 Kernel.sleep
52
+ --------------------------------------------------------------------------------
53
+ 1.33 1.33 0.00 250862/250862 Integer#upto
54
+ 15.12% 15.12% 1.33 1.33 0.00 250862 Fixnum#%
55
+ --------------------------------------------------------------------------------
56
+ 1.24 1.24 0.00 250862/250862 Integer#upto
57
+ 14.13% 14.13% 1.24 1.24 0.00 250862 Fixnum#==
58
+ --------------------------------------------------------------------------------
59
+ 0.02 0.00 0.02 1/1 Object#run_primes
60
+ 0.18% 0.00% 0.02 0.00 0.02 1 Object#make_random_array
61
+ 0.02 0.02 0.00 1/1 Array#each_index
62
+ 0.00 0.00 0.00 1/1 Class#new
63
+ --------------------------------------------------------------------------------
64
+ 0.02 0.02 0.00 1/1 Object#make_random_array
65
+ 0.18% 0.18% 0.02 0.02 0.00 1 Array#each_index
66
+ 0.00 0.00 0.00 500/500 Kernel.rand
67
+ 0.00 0.00 0.00 500/500 Array#[]=
68
+ --------------------------------------------------------------------------------
69
+ 0.00 0.00 0.00 500/501 Object#is_prime
70
+ 0.00 0.00 0.00 1/501 Object#find_largest
71
+ 0.00% 0.00% 0.00 0.00 0.00 501 Fixnum#-
72
+ --------------------------------------------------------------------------------
73
+ 0.00 0.00 0.00 1/1 Kernel.rand
74
+ 0.00% 0.00% 0.00 0.00 0.00 1 Integer#to_int
75
+ --------------------------------------------------------------------------------
76
+ 0.00 0.00 0.00 1/1 Object#find_largest
77
+ 0.00% 0.00% 0.00 0.00 0.00 1 Array#first
78
+ --------------------------------------------------------------------------------
79
+ 0.00 0.00 0.00 1/1 Class#new
80
+ 0.00% 0.00% 0.00 0.00 0.00 1 Array#initialize
81
+ --------------------------------------------------------------------------------
82
+ 0.00 0.00 0.00 1/1 Object#find_largest
83
+ 0.00% 0.00% 0.00 0.00 0.00 1 Array#length
84
+ --------------------------------------------------------------------------------
85
+ 0.00 0.00 0.00 1/1 Object#make_random_array
86
+ 0.00% 0.00% 0.00 0.00 0.00 1 Class#new
87
+ 0.00 0.00 0.00 1/1 Array#initialize
88
+ --------------------------------------------------------------------------------
89
+ 0.00 0.00 0.00 61/61 Integer#upto
90
+ 0.00% 0.00% 0.00 0.00 0.00 61 Fixnum#>
91
+ --------------------------------------------------------------------------------
92
+ 0.00 0.00 0.00 61/61 Integer#upto
93
+ 0.00% 0.00% 0.00 0.00 0.00 61 Array#[]
94
+ --------------------------------------------------------------------------------
95
+ 0.00 0.00 0.00 500/500 Array#each_index
96
+ 0.00% 0.00% 0.00 0.00 0.00 500 Array#[]=
97
+ --------------------------------------------------------------------------------
98
+ 0.00 0.00 0.00 500/500 Array#each_index
99
+ 0.00% 0.00% 0.00 0.00 0.00 500 Kernel.rand
100
+ 0.00 0.00 0.00 1/1 Integer#to_int
101
+
102
+
103
+
104
+ == Overview
105
+ Dashed lines divide the report into entries, with one entry
106
+ per method. Entries are sorted by total time which is the
107
+ time spent in the method plus its children.
108
+
109
+ Each entry has a primary line demarked by values in the
110
+ %total and %self columns. The primary line represents
111
+ the method being profiled. Lines above it are the methods
112
+ that called this method (parents) while the lines below it
113
+ are the methods it called (children).
114
+
115
+ All values are in seconds. For the primary line, the columns represent:
116
+
117
+ %total - The percentage of time spent in this method and its children
118
+ %self - The percentage of time spent in this method
119
+ total - The time spent in this method and its children.
120
+ self - The time spent in this method.
121
+ children - The time spent in this method's children.
122
+ calls - The number of times this method was called.
123
+ name - The name of the method.
124
+
125
+ The interpretation of method names is:
126
+ * #toplevel - The root method that calls all other methods
127
+ * MyObject#test - An instance method "test" of the class "MyObject"
128
+ * <Object:MyObject>#test - The <> characters indicate a singleton method on a singleton class.
129
+
130
+ For example, we see that 99.48% of the time was spent in Integer#upto and its children.
131
+ Of that time, 4.06 seconds was spent in Integer#upto itself and 4.66 in its children.
132
+ Overall, Integer#upto was called 501 times.
133
+
134
+ == Parents
135
+ In each entry, the lines above the primary line are the methods that
136
+ called the current method. If the current method is a root method then
137
+ no parents are shown.
138
+
139
+
140
+ For parent lines, the columns represent:
141
+
142
+ total - The time spent in the current method and it children on behalf of the parent method.
143
+ self - The time spent in this method on behalf of the parent method.
144
+ children - The time spent in this method's children on behalf of the parent.
145
+ calls - The number of times the parent method called this child
146
+
147
+ Looking at Integer#upto again, we see that it was called 500 times from Object#is_prime
148
+ and 1 time from find_largest. Of the 8.72 total seconds spent in Integer#upto, 6.63
149
+ were done for Object#is_prime and 2.09 for Object#find_largest.
150
+
151
+
152
+ == Children
153
+ In each entry, the lines below the primary line are the methods that
154
+ the current method called. If the current method is a leaf method then
155
+ no children are shown.
156
+
157
+ For children lines, the columns represent:
158
+
159
+ total - The time spent in the child, and its children, on behalf of the current method
160
+ self - The time spent in the child on behalf of the current method.
161
+ children - The time spent in the child's children (ie, granchildren) in behalf of the current method
162
+ calls - The number of times the child method was called by the current method.
163
+
164
+ Taking our example of Integer#upto, we see that it called five other methods - Array#[],
165
+ Fixnum#>, Kernel.sleep, Fixnum#= and Fixnum#%. Looking at Kernel.sleep, we see that
166
+ its spent 2.09 seconds working for Integer#upto and its children spent 0 time working for
167
+ Integer#upto. To see the overall time Kernel.sleep took we would have to look up its entry
168
+ in the graph table.
169
+
170
+
@@ -0,0 +1,1679 @@
1
+ /*
2
+ * Copyright (C) 2008 Shugo Maeda <shugo@ruby-lang.org>
3
+ * Charlie Savage <cfis@savagexi.com>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions
8
+ * are met:
9
+ * 1. Redistributions of source code must retain the above copyright
10
+ * notice, this list of conditions and the following disclaimer.
11
+ * 2. Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ *
15
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
+ * SUCH DAMAGE.
26
+ */
27
+
28
+ /* ruby-prof tracks the time spent executing every method in ruby programming.
29
+ The main players are:
30
+
31
+ prof_result_t - Its one field, values, contains the overall results
32
+ thread_data_t - Stores data about a single thread.
33
+ prof_stack_t - The method call stack in a particular thread
34
+ prof_method_t - Profiling information for each method
35
+ prof_call_info_t - Keeps track a method's callers and callees.
36
+
37
+ The final resulut is a hash table of thread_data_t, keyed on the thread
38
+ id. Each thread has an hash a table of prof_method_t, keyed on the
39
+ method id. A hash table is used for quick look up when doing a profile.
40
+ However, it is exposed to Ruby as an array.
41
+
42
+ Each prof_method_t has two hash tables, parent and children, of prof_call_info_t.
43
+ These objects keep track of a method's callers (who called the method) and its
44
+ callees (who the method called). These are keyed the method id, but once again,
45
+ are exposed to Ruby as arrays. Each prof_call_into_t maintains a pointer to the
46
+ caller or callee method, thereby making it easy to navigate through the call
47
+ hierarchy in ruby - which is very helpful for creating call graphs.
48
+ */
49
+
50
+ #include "ruby_prof.h"
51
+
52
+ /* ================ Helper Functions =================*/
53
+ static VALUE
54
+ figure_singleton_name(VALUE klass)
55
+ {
56
+ VALUE result = Qnil;
57
+
58
+ /* We have come across a singleton object. First
59
+ figure out what it is attached to.*/
60
+ VALUE attached = rb_iv_get(klass, "__attached__");
61
+
62
+ /* Is this a singleton class acting as a metaclass? */
63
+ if (BUILTIN_TYPE(attached) == T_CLASS)
64
+ {
65
+ result = rb_str_new2("<Class::");
66
+ rb_str_append(result, rb_inspect(attached));
67
+ rb_str_cat2(result, ">");
68
+ }
69
+
70
+ /* Is this for singleton methods on a module? */
71
+ else if (BUILTIN_TYPE(attached) == T_MODULE)
72
+ {
73
+ result = rb_str_new2("<Module::");
74
+ rb_str_append(result, rb_inspect(attached));
75
+ rb_str_cat2(result, ">");
76
+ }
77
+
78
+ /* Is this for singleton methods on an object? */
79
+ else if (BUILTIN_TYPE(attached) == T_OBJECT)
80
+ {
81
+ /* Make sure to get the super class so that we don't
82
+ mistakenly grab a T_ICLASS which would lead to
83
+ unknown method errors. */
84
+ #ifdef RCLASS_SUPER
85
+ VALUE super = rb_class_real(RCLASS_SUPER(klass));
86
+ #else
87
+ VALUE super = rb_class_real(RCLASS(klass)->super);
88
+ #endif
89
+ result = rb_str_new2("<Object::");
90
+ rb_str_append(result, rb_inspect(super));
91
+ rb_str_cat2(result, ">");
92
+ }
93
+
94
+ /* Ok, this could be other things like an array made put onto
95
+ a singleton object (yeah, it happens, see the singleton
96
+ objects test case). */
97
+ else
98
+ {
99
+ result = rb_inspect(klass);
100
+ }
101
+
102
+ return result;
103
+ }
104
+
105
+ static VALUE
106
+ klass_name(VALUE klass)
107
+ {
108
+ VALUE result = Qnil;
109
+
110
+ if (klass == 0 || klass == Qnil)
111
+ {
112
+ result = rb_str_new2("Global");
113
+ }
114
+ else if (BUILTIN_TYPE(klass) == T_MODULE)
115
+ {
116
+ result = rb_inspect(klass);
117
+ }
118
+ else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
119
+ {
120
+ result = figure_singleton_name(klass);
121
+ }
122
+ else if (BUILTIN_TYPE(klass) == T_CLASS)
123
+ {
124
+ result = rb_inspect(klass);
125
+ }
126
+ else
127
+ {
128
+ /* Should never happen. */
129
+ result = rb_str_new2("Unknown");
130
+ }
131
+
132
+ return result;
133
+ }
134
+
135
+ static VALUE
136
+ method_name(ID mid, int depth)
137
+ {
138
+ VALUE result;
139
+
140
+ if (mid == ID_ALLOCATOR)
141
+ result = rb_str_new2("allocate");
142
+ else if (mid == 0)
143
+ result = rb_str_new2("[No method]");
144
+ else
145
+ result = rb_String(ID2SYM(mid));
146
+
147
+ if (depth > 0)
148
+ {
149
+ char buffer[65];
150
+ sprintf(buffer, "%i", depth);
151
+ rb_str_cat2(result, "-");
152
+ rb_str_cat2(result, buffer);
153
+ }
154
+
155
+ return result;
156
+ }
157
+
158
+ static VALUE
159
+ full_name(VALUE klass, ID mid, int depth)
160
+ {
161
+ VALUE result = klass_name(klass);
162
+ rb_str_cat2(result, "#");
163
+ rb_str_append(result, method_name(mid, depth));
164
+
165
+ return result;
166
+ }
167
+
168
+ /* ================ Stack Handling =================*/
169
+ /* Creates a stack of prof_frame_t to keep track
170
+ of timings for active methods. */
171
+ static prof_stack_t *
172
+ stack_create()
173
+ {
174
+ prof_stack_t *stack = ALLOC(prof_stack_t);
175
+ stack->start = ALLOC_N(prof_frame_t, INITIAL_STACK_SIZE);
176
+ stack->ptr = stack->start;
177
+ stack->end = stack->start + INITIAL_STACK_SIZE;
178
+ return stack;
179
+ }
180
+
181
+ static void
182
+ stack_free(prof_stack_t *stack)
183
+ {
184
+ xfree(stack->start);
185
+ xfree(stack);
186
+ }
187
+
188
+ static prof_frame_t *
189
+ stack_push(prof_stack_t *stack)
190
+ {
191
+ /* Is there space on the stack? If not, double
192
+ its size. */
193
+ if (stack->ptr == stack->end)
194
+ {
195
+ size_t len = stack->ptr - stack->start;
196
+ size_t new_capacity = (stack->end - stack->start) * 2;
197
+ REALLOC_N(stack->start, prof_frame_t, new_capacity);
198
+ stack->ptr = stack->start + len;
199
+ stack->end = stack->start + new_capacity;
200
+ }
201
+ return stack->ptr++;
202
+ }
203
+
204
+ static prof_frame_t *
205
+ stack_pop(prof_stack_t *stack)
206
+ {
207
+ if (stack->ptr == stack->start)
208
+ return NULL;
209
+ else
210
+ return --stack->ptr;
211
+ }
212
+
213
+ static prof_frame_t *
214
+ stack_peek(prof_stack_t *stack)
215
+ {
216
+ if (stack->ptr == stack->start)
217
+ return NULL;
218
+ else
219
+ return stack->ptr - 1;
220
+ }
221
+
222
+ /* ================ Method Key =================*/
223
+ static int
224
+ method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2)
225
+ {
226
+ return (key1->klass != key2->klass) ||
227
+ (key1->mid != key2->mid) ||
228
+ (key1->depth != key2->depth);
229
+ }
230
+
231
+ static int
232
+ method_table_hash(prof_method_key_t *key)
233
+ {
234
+ return key->key;
235
+ }
236
+
237
+ static struct st_hash_type type_method_hash = {
238
+ method_table_cmp,
239
+ method_table_hash
240
+ };
241
+
242
+ static void
243
+ method_key(prof_method_key_t* key, VALUE klass, ID mid, int depth)
244
+ {
245
+ key->klass = klass;
246
+ key->mid = mid;
247
+ key->depth = depth;
248
+ key->key = (klass << 4) + (mid << 2) + depth;
249
+ }
250
+
251
+
252
+ /* ================ Call Info =================*/
253
+ static st_table *
254
+ call_info_table_create()
255
+ {
256
+ return st_init_table(&type_method_hash);
257
+ }
258
+
259
+ static size_t
260
+ call_info_table_insert(st_table *table, const prof_method_key_t *key, prof_call_info_t *val)
261
+ {
262
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
263
+ }
264
+
265
+ static prof_call_info_t *
266
+ call_info_table_lookup(st_table *table, const prof_method_key_t *key)
267
+ {
268
+ st_data_t val;
269
+ if (st_lookup(table, (st_data_t) key, &val))
270
+ {
271
+ return (prof_call_info_t *) val;
272
+ }
273
+ else
274
+ {
275
+ return NULL;
276
+ }
277
+ }
278
+
279
+ static void
280
+ call_info_table_free(st_table *table)
281
+ {
282
+ st_free_table(table);
283
+ }
284
+
285
+ /* Document-class: RubyProf::CallInfo
286
+ RubyProf::CallInfo is a helper class used by RubyProf::MethodInfo
287
+ to keep track of which child methods were called and how long
288
+ they took to execute. */
289
+
290
+ /* :nodoc: */
291
+ static prof_call_info_t *
292
+ prof_call_info_create(prof_method_t* method, prof_call_info_t* parent)
293
+ {
294
+ prof_call_info_t *result = ALLOC(prof_call_info_t);
295
+ result->object = Qnil;
296
+ result->target = method;
297
+ result->parent = parent;
298
+ result->call_infos = call_info_table_create();
299
+ result->children = Qnil;
300
+
301
+ result->called = 0;
302
+ result->total_time = 0;
303
+ result->self_time = 0;
304
+ result->wait_time = 0;
305
+ result->line = 0;
306
+ return result;
307
+ }
308
+
309
+ static void
310
+ prof_call_info_mark(prof_call_info_t *call_info)
311
+ {
312
+ rb_gc_mark(prof_method_wrap(call_info->target));
313
+ rb_gc_mark(call_info->children);
314
+ if (call_info->parent)
315
+ rb_gc_mark(prof_call_info_wrap(call_info->parent));
316
+ }
317
+
318
+ static void
319
+ prof_call_info_free(prof_call_info_t *call_info)
320
+ {
321
+ call_info_table_free(call_info->call_infos);
322
+ xfree(call_info);
323
+ }
324
+
325
+ static VALUE
326
+ prof_call_info_wrap(prof_call_info_t *call_info)
327
+ {
328
+ if (call_info->object == Qnil)
329
+ {
330
+ call_info->object = Data_Wrap_Struct(cCallInfo, prof_call_info_mark, prof_call_info_free, call_info);
331
+ }
332
+ return call_info->object;
333
+ }
334
+
335
+ static prof_call_info_t *
336
+ prof_get_call_info_result(VALUE obj)
337
+ {
338
+ if (BUILTIN_TYPE(obj) != T_DATA)
339
+ {
340
+ /* Should never happen */
341
+ rb_raise(rb_eTypeError, "Not a call info object");
342
+ }
343
+ return (prof_call_info_t *) DATA_PTR(obj);
344
+ }
345
+
346
+
347
+ /* call-seq:
348
+ called -> MethodInfo
349
+
350
+ Returns the target method. */
351
+ static VALUE
352
+ prof_call_info_target(VALUE self)
353
+ {
354
+ /* Target is a pointer to a method_info - so we have to be careful
355
+ about the GC. We will wrap the method_info but provide no
356
+ free method so the underlying object is not freed twice! */
357
+
358
+ prof_call_info_t *result = prof_get_call_info_result(self);
359
+ return prof_method_wrap(result->target);
360
+ }
361
+
362
+ /* call-seq:
363
+ called -> int
364
+
365
+ Returns the total amount of time this method was called. */
366
+ static VALUE
367
+ prof_call_info_called(VALUE self)
368
+ {
369
+ prof_call_info_t *result = prof_get_call_info_result(self);
370
+ return INT2NUM(result->called);
371
+ }
372
+
373
+ /* call-seq:
374
+ line_no -> int
375
+
376
+ returns the line number of the method */
377
+ static VALUE
378
+ prof_call_info_line(VALUE self)
379
+ {
380
+ prof_call_info_t *result = prof_get_call_info_result(self);
381
+ return rb_int_new(result->line);
382
+ }
383
+
384
+ /* call-seq:
385
+ total_time -> float
386
+
387
+ Returns the total amount of time spent in this method and its children. */
388
+ static VALUE
389
+ prof_call_info_total_time(VALUE self)
390
+ {
391
+ prof_call_info_t *result = prof_get_call_info_result(self);
392
+ return rb_float_new(convert_measurement(result->total_time));
393
+ }
394
+
395
+ /* call-seq:
396
+ self_time -> float
397
+
398
+ Returns the total amount of time spent in this method. */
399
+ static VALUE
400
+ prof_call_info_self_time(VALUE self)
401
+ {
402
+ prof_call_info_t *result = prof_get_call_info_result(self);
403
+
404
+ return rb_float_new(convert_measurement(result->self_time));
405
+ }
406
+
407
+ /* call-seq:
408
+ wait_time -> float
409
+
410
+ Returns the total amount of time this method waited for other threads. */
411
+ static VALUE
412
+ prof_call_info_wait_time(VALUE self)
413
+ {
414
+ prof_call_info_t *result = prof_get_call_info_result(self);
415
+
416
+ return rb_float_new(convert_measurement(result->wait_time));
417
+ }
418
+
419
+ /* call-seq:
420
+ parent -> call_info
421
+
422
+ Returns the call_infos parent call_info object (the method that called this method).*/
423
+ static VALUE
424
+ prof_call_info_parent(VALUE self)
425
+ {
426
+ prof_call_info_t *result = prof_get_call_info_result(self);
427
+ if (result->parent)
428
+ return prof_call_info_wrap(result->parent);
429
+ else
430
+ return Qnil;
431
+ }
432
+
433
+ static int
434
+ prof_call_info_collect_children(st_data_t key, st_data_t value, st_data_t result)
435
+ {
436
+ prof_call_info_t *call_info = (prof_call_info_t *) value;
437
+ VALUE arr = (VALUE) result;
438
+ rb_ary_push(arr, prof_call_info_wrap(call_info));
439
+ return ST_CONTINUE;
440
+ }
441
+
442
+ /* call-seq:
443
+ children -> hash
444
+
445
+ Returns an array of call info objects of methods that this method
446
+ called (ie, children).*/
447
+ static VALUE
448
+ prof_call_info_children(VALUE self)
449
+ {
450
+ prof_call_info_t *call_info = prof_get_call_info_result(self);
451
+ if (call_info->children == Qnil)
452
+ {
453
+ call_info->children = rb_ary_new();
454
+ st_foreach(call_info->call_infos, prof_call_info_collect_children, call_info->children);
455
+ }
456
+ return call_info->children;
457
+ }
458
+
459
+ /* ================ Call Infos =================*/
460
+ static prof_call_infos_t*
461
+ prof_call_infos_create()
462
+ {
463
+ prof_call_infos_t *result = ALLOC(prof_call_infos_t);
464
+ result->start = ALLOC_N(prof_call_info_t*, INITIAL_CALL_INFOS_SIZE);
465
+ result->end = result->start + INITIAL_CALL_INFOS_SIZE;
466
+ result->ptr = result->start;
467
+ result->object = Qnil;
468
+ return result;
469
+ }
470
+
471
+ static void
472
+ prof_call_infos_free(prof_call_infos_t *call_infos)
473
+ {
474
+ xfree(call_infos->start);
475
+ xfree(call_infos);
476
+ }
477
+
478
+ static void
479
+ prof_add_call_info(prof_call_infos_t *call_infos, prof_call_info_t *call_info)
480
+ {
481
+ if (call_infos->ptr == call_infos->end)
482
+ {
483
+ size_t len = call_infos->ptr - call_infos->start;
484
+ size_t new_capacity = (call_infos->end - call_infos->start) * 2;
485
+ REALLOC_N(call_infos->start, prof_call_info_t*, new_capacity);
486
+ call_infos->ptr = call_infos->start + len;
487
+ call_infos->end = call_infos->start + new_capacity;
488
+ }
489
+ *call_infos->ptr = call_info;
490
+ call_infos->ptr++;
491
+ }
492
+
493
+ static VALUE
494
+ prof_call_infos_wrap(prof_call_infos_t *call_infos)
495
+ {
496
+ if (call_infos->object == Qnil)
497
+ {
498
+ prof_call_info_t **i;
499
+ call_infos->object = rb_ary_new();
500
+ for(i=call_infos->start; i<call_infos->ptr; i++)
501
+ {
502
+ VALUE call_info = prof_call_info_wrap(*i);
503
+ rb_ary_push(call_infos->object, call_info);
504
+ }
505
+ }
506
+ return call_infos->object;
507
+ }
508
+
509
+
510
+ /* ================ Method Info =================*/
511
+ /* Document-class: RubyProf::MethodInfo
512
+ The RubyProf::MethodInfo class stores profiling data for a method.
513
+ One instance of the RubyProf::MethodInfo class is created per method
514
+ called per thread. Thus, if a method is called in two different
515
+ thread then there will be two RubyProf::MethodInfo objects
516
+ created. RubyProf::MethodInfo objects can be accessed via
517
+ the RubyProf::Result object.
518
+ */
519
+
520
+ static prof_method_t*
521
+ prof_method_create(prof_method_key_t *key, const char* source_file, int line)
522
+ {
523
+ prof_method_t *result = ALLOC(prof_method_t);
524
+ result->object = Qnil;
525
+ result->key = ALLOC(prof_method_key_t);
526
+ method_key(result->key, key->klass, key->mid, key->depth);
527
+
528
+ result->call_infos = prof_call_infos_create();
529
+
530
+ result->active = 0;
531
+
532
+ if (source_file != NULL)
533
+ {
534
+ int len = strlen(source_file) + 1;
535
+ char *buffer = ALLOC_N(char, len);
536
+
537
+ MEMCPY(buffer, source_file, char, len);
538
+ result->source_file = buffer;
539
+ }
540
+ else
541
+ {
542
+ result->source_file = source_file;
543
+ }
544
+ result->line = line;
545
+
546
+ return result;
547
+ }
548
+
549
+ static void
550
+ prof_method_mark(prof_method_t *method)
551
+ {
552
+ rb_gc_mark(method->call_infos->object);
553
+ rb_gc_mark(method->key->klass);
554
+ }
555
+
556
+ static void
557
+ prof_method_free(prof_method_t *method)
558
+ {
559
+ if (method->source_file)
560
+ {
561
+ xfree((char*)method->source_file);
562
+ }
563
+
564
+ prof_call_infos_free(method->call_infos);
565
+ xfree(method->key);
566
+ xfree(method);
567
+ }
568
+
569
+ static VALUE
570
+ prof_method_wrap(prof_method_t *result)
571
+ {
572
+ if (result->object == Qnil)
573
+ {
574
+ result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_free, result);
575
+ }
576
+ return result->object;
577
+ }
578
+
579
+ static prof_method_t *
580
+ get_prof_method(VALUE obj)
581
+ {
582
+ return (prof_method_t *) DATA_PTR(obj);
583
+ }
584
+
585
+ /* call-seq:
586
+ line_no -> int
587
+
588
+ returns the line number of the method */
589
+ static VALUE
590
+ prof_method_line(VALUE self)
591
+ {
592
+ return rb_int_new(get_prof_method(self)->line);
593
+ }
594
+
595
+ /* call-seq:
596
+ source_file => string
597
+
598
+ return the source file of the method
599
+ */
600
+ static VALUE prof_method_source_file(VALUE self)
601
+ {
602
+ const char* sf = get_prof_method(self)->source_file;
603
+ if(!sf)
604
+ {
605
+ return rb_str_new2("ruby_runtime");
606
+ }
607
+ else
608
+ {
609
+ return rb_str_new2(sf);
610
+ }
611
+ }
612
+
613
+
614
+ /* call-seq:
615
+ method_class -> klass
616
+
617
+ Returns the Ruby klass that owns this method. */
618
+ static VALUE
619
+ prof_method_klass(VALUE self)
620
+ {
621
+ prof_method_t *result = get_prof_method(self);
622
+ return result->key->klass;
623
+ }
624
+
625
+ /* call-seq:
626
+ method_id -> ID
627
+
628
+ Returns the id of this method. */
629
+ static VALUE
630
+ prof_method_id(VALUE self)
631
+ {
632
+ prof_method_t *result = get_prof_method(self);
633
+ return ID2SYM(result->key->mid);
634
+ }
635
+
636
+ /* call-seq:
637
+ klass_name -> string
638
+
639
+ Returns the name of this method's class. Singleton classes
640
+ will have the form <Object::Object>. */
641
+
642
+ static VALUE
643
+ prof_klass_name(VALUE self)
644
+ {
645
+ prof_method_t *method = get_prof_method(self);
646
+ return klass_name(method->key->klass);
647
+ }
648
+
649
+ /* call-seq:
650
+ method_name -> string
651
+
652
+ Returns the name of this method in the format Object#method. Singletons
653
+ methods will be returned in the format <Object::Object>#method.*/
654
+
655
+ static VALUE
656
+ prof_method_name(VALUE self, int depth)
657
+ {
658
+ prof_method_t *method = get_prof_method(self);
659
+ return method_name(method->key->mid, depth);
660
+ }
661
+
662
+ /* call-seq:
663
+ full_name -> string
664
+
665
+ Returns the full name of this method in the format Object#method.*/
666
+
667
+ static VALUE
668
+ prof_full_name(VALUE self)
669
+ {
670
+ prof_method_t *method = get_prof_method(self);
671
+ return full_name(method->key->klass, method->key->mid, method->key->depth);
672
+ }
673
+
674
+ /* call-seq:
675
+ call_infos -> Array of call_info
676
+
677
+ Returns an array of call info objects that contain profiling information
678
+ about the current method.*/
679
+ static VALUE
680
+ prof_method_call_infos(VALUE self)
681
+ {
682
+ prof_method_t *method = get_prof_method(self);
683
+ return prof_call_infos_wrap(method->call_infos);
684
+ }
685
+
686
+ static int
687
+ collect_methods(st_data_t key, st_data_t value, st_data_t result)
688
+ {
689
+ /* Called for each method stored in a thread's method table.
690
+ We want to store the method info information into an array.*/
691
+ VALUE methods = (VALUE) result;
692
+ prof_method_t *method = (prof_method_t *) value;
693
+ rb_ary_push(methods, prof_method_wrap(method));
694
+
695
+ /* Wrap call info objects */
696
+ prof_call_infos_wrap(method->call_infos);
697
+
698
+ return ST_CONTINUE;
699
+ }
700
+
701
+ /* ================ Method Table =================*/
702
+ static st_table *
703
+ method_table_create()
704
+ {
705
+ return st_init_table(&type_method_hash);
706
+ }
707
+
708
+ static size_t
709
+ method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
710
+ {
711
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
712
+ }
713
+
714
+ static prof_method_t *
715
+ method_table_lookup(st_table *table, const prof_method_key_t* key)
716
+ {
717
+ st_data_t val;
718
+ if (st_lookup(table, (st_data_t)key, &val))
719
+ {
720
+ return (prof_method_t *) val;
721
+ }
722
+ else
723
+ {
724
+ return NULL;
725
+ }
726
+ }
727
+
728
+
729
+ static void
730
+ method_table_free(st_table *table)
731
+ {
732
+ /* Don't free the contents since they are wrapped by
733
+ Ruby objects! */
734
+ st_free_table(table);
735
+ }
736
+
737
+
738
+ /* ================ Thread Handling =================*/
739
+
740
+ /* ---- Keeps track of thread's stack and methods ---- */
741
+ static thread_data_t*
742
+ thread_data_create()
743
+ {
744
+ thread_data_t* result = ALLOC(thread_data_t);
745
+ result->stack = stack_create();
746
+ result->method_table = method_table_create();
747
+ result->last_switch = get_measurement();
748
+ return result;
749
+ }
750
+
751
+ static void
752
+ thread_data_free(thread_data_t* thread_data)
753
+ {
754
+ method_table_free(thread_data->method_table);
755
+ stack_free(thread_data->stack);
756
+ xfree(thread_data);
757
+ }
758
+
759
+ /* ---- Hash, keyed on thread, that stores thread's stack
760
+ and methods---- */
761
+
762
+ static st_table *
763
+ threads_table_create()
764
+ {
765
+ return st_init_numtable();
766
+ }
767
+
768
+ static size_t
769
+ threads_table_insert(st_table *table, VALUE thread, thread_data_t *thread_data)
770
+ {
771
+ /* Its too slow to key on the real thread id so just typecast thread instead. */
772
+ return st_insert(table, (st_data_t) thread, (st_data_t) thread_data);
773
+ }
774
+
775
+ static thread_data_t *
776
+ threads_table_lookup(st_table *table, VALUE thread_id)
777
+ {
778
+ thread_data_t* result;
779
+ st_data_t val;
780
+
781
+ /* Its too slow to key on the real thread id so just typecast thread instead. */
782
+ if (st_lookup(table, (st_data_t) thread_id, &val))
783
+ {
784
+ result = (thread_data_t *) val;
785
+ }
786
+ else
787
+ {
788
+ result = thread_data_create();
789
+ result->thread_id = thread_id;
790
+
791
+ /* Insert the table */
792
+ threads_table_insert(threads_tbl, thread_id, result);
793
+ }
794
+ return result;
795
+ }
796
+
797
+ static int
798
+ free_thread_data(st_data_t key, st_data_t value, st_data_t dummy)
799
+ {
800
+ thread_data_free((thread_data_t*)value);
801
+ return ST_CONTINUE;
802
+ }
803
+
804
+
805
+ static void
806
+ threads_table_free(st_table *table)
807
+ {
808
+ st_foreach(table, free_thread_data, 0);
809
+ st_free_table(table);
810
+ }
811
+
812
+
813
+ static int
814
+ collect_threads(st_data_t key, st_data_t value, st_data_t result)
815
+ {
816
+ /* Although threads are keyed on an id, that is actually a
817
+ pointer to the VALUE object of the thread. So its bogus.
818
+ However, in thread_data is the real thread id stored
819
+ as an int. */
820
+ thread_data_t* thread_data = (thread_data_t*) value;
821
+ VALUE threads_hash = (VALUE) result;
822
+
823
+ VALUE methods = rb_ary_new();
824
+
825
+ /* Now collect an array of all the called methods */
826
+ st_table* method_table = thread_data->method_table;
827
+ st_foreach(method_table, collect_methods, methods);
828
+
829
+ /* Store the results in the threads hash keyed on the thread id. */
830
+ rb_hash_aset(threads_hash, thread_data->thread_id, methods);
831
+
832
+ return ST_CONTINUE;
833
+ }
834
+
835
+
836
+ /* ================ Profiling =================*/
837
+ /* Copied from eval.c */
838
+ #ifdef DEBUG
839
+ static char *
840
+ get_event_name(rb_event_flag_t event)
841
+ {
842
+ switch (event) {
843
+ case RUBY_EVENT_LINE:
844
+ return "line";
845
+ case RUBY_EVENT_CLASS:
846
+ return "class";
847
+ case RUBY_EVENT_END:
848
+ return "end";
849
+ case RUBY_EVENT_CALL:
850
+ return "call";
851
+ case RUBY_EVENT_RETURN:
852
+ return "return";
853
+ case RUBY_EVENT_C_CALL:
854
+ return "c-call";
855
+ case RUBY_EVENT_C_RETURN:
856
+ return "c-return";
857
+ case RUBY_EVENT_RAISE:
858
+ return "raise";
859
+ default:
860
+ return "unknown";
861
+ }
862
+ }
863
+ #endif
864
+
865
+ static prof_method_t*
866
+ get_method(rb_event_flag_t event, VALUE klass, ID mid, int depth, st_table* method_table)
867
+ {
868
+ prof_method_key_t key;
869
+ prof_method_t *method = NULL;
870
+
871
+ method_key(&key, klass, mid, depth);
872
+ method = method_table_lookup(method_table, &key);
873
+
874
+ if (!method)
875
+ {
876
+ const char* source_file = rb_sourcefile();
877
+ int line = rb_sourceline();
878
+
879
+ /* Line numbers are not accurate for c method calls */
880
+ if (event == RUBY_EVENT_C_CALL)
881
+ {
882
+ line = 0;
883
+ source_file = NULL;
884
+ }
885
+
886
+ method = prof_method_create(&key, source_file, line);
887
+ method_table_insert(method_table, method->key, method);
888
+ }
889
+ return method;
890
+ }
891
+
892
+ static void
893
+ update_result(prof_measure_t total_time,
894
+ prof_frame_t *parent_frame,
895
+ prof_frame_t *frame)
896
+ {
897
+ prof_measure_t self_time = total_time - frame->child_time - frame->wait_time;
898
+
899
+ prof_call_info_t *call_info = frame->call_info;
900
+
901
+ /* Update information about the current method */
902
+ call_info->called++;
903
+ call_info->total_time += total_time;
904
+ call_info->self_time += self_time;
905
+ call_info->wait_time += frame->wait_time;
906
+
907
+ /* Note where the current method was called from */
908
+ if (parent_frame)
909
+ call_info->line = parent_frame->line;
910
+ }
911
+
912
+ static thread_data_t *
913
+ switch_thread(VALUE thread_id, prof_measure_t now)
914
+ {
915
+ prof_frame_t *frame = NULL;
916
+ prof_measure_t wait_time = 0;
917
+
918
+ /* Get new thread information. */
919
+ thread_data_t *thread_data = threads_table_lookup(threads_tbl, thread_id);
920
+
921
+ /* How long has this thread been waiting? */
922
+ wait_time = now - thread_data->last_switch;
923
+ thread_data->last_switch = 0;
924
+
925
+ /* Get the frame at the top of the stack. This may represent
926
+ the current method (EVENT_LINE, EVENT_RETURN) or the
927
+ previous method (EVENT_CALL).*/
928
+ frame = stack_peek(thread_data->stack);
929
+
930
+ if (frame)
931
+ frame->wait_time += wait_time;
932
+
933
+ /* Save on the last thread the time of the context switch
934
+ and reset this thread's last context switch to 0.*/
935
+ if (last_thread_data)
936
+ last_thread_data->last_switch = now;
937
+
938
+ last_thread_data = thread_data;
939
+ return thread_data;
940
+ }
941
+
942
+ static prof_frame_t*
943
+ pop_frame(thread_data_t *thread_data, prof_measure_t now)
944
+ {
945
+ prof_frame_t *frame = NULL;
946
+ prof_frame_t* parent_frame = NULL;
947
+ prof_measure_t total_time;
948
+
949
+ frame = stack_pop(thread_data->stack);
950
+
951
+ /* Frame can be null. This can happen if RubProf.start is called from
952
+ a method that exits. And it can happen if an exception is raised
953
+ in code that is being profiled and the stack unwinds (RubProf is
954
+ not notified of that by the ruby runtime. */
955
+ if (frame == NULL) return NULL;
956
+
957
+ /* Calculate the total time this method took */
958
+ total_time = now - frame->start_time;
959
+
960
+ /* Now deactivate the method */
961
+ frame->call_info->target->active = 0;
962
+
963
+ parent_frame = stack_peek(thread_data->stack);
964
+ if (parent_frame)
965
+ {
966
+ parent_frame->child_time += total_time;
967
+ }
968
+
969
+ update_result(total_time, parent_frame, frame);
970
+ return frame;
971
+ }
972
+
973
+ static int
974
+ pop_frames(st_data_t key, st_data_t value, st_data_t now_arg)
975
+ {
976
+ VALUE thread_id = (VALUE)key;
977
+ thread_data_t* thread_data = (thread_data_t *) value;
978
+ prof_measure_t now = *(prof_measure_t *) now_arg;
979
+
980
+ if (!last_thread_data || last_thread_data->thread_id != thread_id)
981
+ thread_data = switch_thread(thread_id, now);
982
+ else
983
+ thread_data = last_thread_data;
984
+
985
+ while (pop_frame(thread_data, now))
986
+ {
987
+ }
988
+
989
+ return ST_CONTINUE;
990
+ }
991
+
992
+ static void
993
+ prof_pop_threads()
994
+ {
995
+ /* Get current measurement*/
996
+ prof_measure_t now = get_measurement();
997
+ st_foreach(threads_tbl, pop_frames, (st_data_t) &now);
998
+ }
999
+
1000
+
1001
+ #ifdef RUBY_VM
1002
+ static void
1003
+ prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
1004
+ #else
1005
+ static void
1006
+ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE klass)
1007
+ #endif
1008
+ {
1009
+
1010
+ VALUE thread = Qnil;
1011
+ VALUE thread_id = Qnil;
1012
+ prof_measure_t now = 0;
1013
+ thread_data_t* thread_data = NULL;
1014
+ prof_frame_t *frame = NULL;
1015
+
1016
+
1017
+ #ifdef RUBY_VM
1018
+
1019
+ if (event != RUBY_EVENT_C_CALL && event != RUBY_EVENT_C_RETURN) {
1020
+ rb_frame_method_id_and_class(&mid, &klass);
1021
+ }
1022
+ #endif
1023
+
1024
+ #ifdef DEBUG
1025
+ /* This code is here for debug purposes - uncomment it out
1026
+ when debugging to see a print out of exactly what the
1027
+ profiler is tracing. */
1028
+ {
1029
+ char* key = 0;
1030
+ static VALUE last_thread_id = Qnil;
1031
+
1032
+ VALUE thread = rb_thread_current();
1033
+ VALUE thread_id = rb_obj_id(thread);
1034
+ char* class_name = NULL;
1035
+ char* method_name = rb_id2name(mid);
1036
+ char* source_file = rb_sourcefile();
1037
+ unsigned int source_line = rb_sourceline();
1038
+
1039
+ char* event_name = get_event_name(event);
1040
+
1041
+ if (klass != 0)
1042
+ klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
1043
+
1044
+ class_name = rb_class2name(klass);
1045
+
1046
+ if (last_thread_id != thread_id)
1047
+ printf("\n");
1048
+
1049
+ printf("%2u: %-8s :%2d %s#%s\n",
1050
+ thread_id, event_name, source_line, class_name, method_name);
1051
+ fflush(stdout);
1052
+ last_thread_id = thread_id;
1053
+ }
1054
+ #endif
1055
+
1056
+ /* Special case - skip any methods from the mProf
1057
+ module, such as Prof.stop, since they clutter
1058
+ the results but aren't important to them results. */
1059
+ if (self == mProf) return;
1060
+
1061
+ /* Get current measurement*/
1062
+ now = get_measurement();
1063
+
1064
+ /* Get the current thread information. */
1065
+ thread = rb_thread_current();
1066
+ thread_id = rb_obj_id(thread);
1067
+
1068
+ if (exclude_threads_tbl &&
1069
+ st_lookup(exclude_threads_tbl, (st_data_t) thread_id, 0))
1070
+ {
1071
+ return;
1072
+ }
1073
+
1074
+ /* Was there a context switch? */
1075
+ if (!last_thread_data || last_thread_data->thread_id != thread_id)
1076
+ thread_data = switch_thread(thread_id, now);
1077
+ else
1078
+ thread_data = last_thread_data;
1079
+
1080
+ /* Get the current frame for the current thread. */
1081
+ frame = stack_peek(thread_data->stack);
1082
+
1083
+ switch (event) {
1084
+ case RUBY_EVENT_LINE:
1085
+ {
1086
+ /* Keep track of the current line number in this method. When
1087
+ a new method is called, we know what line number it was
1088
+ called from. */
1089
+ if (frame)
1090
+ {
1091
+ frame->line = rb_sourceline();
1092
+ break;
1093
+ }
1094
+
1095
+ /* If we get here there was no frame, which means this is
1096
+ the first method seen for this thread, so fall through
1097
+ to below to create it. */
1098
+ }
1099
+ case RUBY_EVENT_CALL:
1100
+ case RUBY_EVENT_C_CALL:
1101
+ {
1102
+ prof_call_info_t *call_info = NULL;
1103
+ prof_method_t *method = NULL;
1104
+
1105
+ /* Is this an include for a module? If so get the actual
1106
+ module class since we want to combine all profiling
1107
+ results for that module. */
1108
+
1109
+ if (klass != 0)
1110
+ klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
1111
+
1112
+ /* Assume this is the first time we have called this method. */
1113
+ method = get_method(event, klass, mid, 0, thread_data->method_table);
1114
+
1115
+ /* Check for a recursive call */
1116
+ if (method->active)
1117
+ {
1118
+ /* Yes, this method is already active */
1119
+ method = get_method(event, klass, mid, method->key->depth + 1, thread_data->method_table);
1120
+ }
1121
+ else
1122
+ {
1123
+ /* No, so make it active */
1124
+ method->active = 1;
1125
+ }
1126
+
1127
+ if (!frame)
1128
+ {
1129
+ call_info = prof_call_info_create(method, NULL);
1130
+ prof_add_call_info(method->call_infos, call_info);
1131
+ }
1132
+ else
1133
+ {
1134
+ call_info = call_info_table_lookup(frame->call_info->call_infos, method->key);
1135
+
1136
+ if (!call_info)
1137
+ {
1138
+ call_info = prof_call_info_create(method, frame->call_info);
1139
+ call_info_table_insert(frame->call_info->call_infos, method->key, call_info);
1140
+ prof_add_call_info(method->call_infos, call_info);
1141
+ }
1142
+ }
1143
+
1144
+ /* Push a new frame onto the stack */
1145
+ frame = stack_push(thread_data->stack);
1146
+ frame->call_info = call_info;
1147
+ frame->start_time = now;
1148
+ frame->wait_time = 0;
1149
+ frame->child_time = 0;
1150
+ frame->line = rb_sourceline();
1151
+
1152
+ break;
1153
+ }
1154
+ case RUBY_EVENT_RETURN:
1155
+ case RUBY_EVENT_C_RETURN:
1156
+ {
1157
+ pop_frame(thread_data, now);
1158
+ break;
1159
+ }
1160
+ }
1161
+ }
1162
+
1163
+
1164
+ /* ======== ProfResult ============== */
1165
+
1166
+ /* Document-class: RubyProf::Result
1167
+ The RubyProf::Result class is used to store the results of a
1168
+ profiling run. And instace of the class is returned from
1169
+ the methods RubyProf#stop and RubyProf#profile.
1170
+
1171
+ RubyProf::Result has one field, called threads, which is a hash
1172
+ table keyed on thread ID. For each thread id, the hash table
1173
+ stores another hash table that contains profiling information
1174
+ for each method called during the threads execution. That
1175
+ hash table is keyed on method name and contains
1176
+ RubyProf::MethodInfo objects. */
1177
+
1178
+
1179
+ static void
1180
+ prof_result_mark(prof_result_t *prof_result)
1181
+ {
1182
+ VALUE threads = prof_result->threads;
1183
+ rb_gc_mark(threads);
1184
+ }
1185
+
1186
+ static void
1187
+ prof_result_free(prof_result_t *prof_result)
1188
+ {
1189
+ prof_result->threads = Qnil;
1190
+ xfree(prof_result);
1191
+ }
1192
+
1193
+ static VALUE
1194
+ prof_result_new()
1195
+ {
1196
+ prof_result_t *prof_result = ALLOC(prof_result_t);
1197
+
1198
+ /* Wrap threads in Ruby regular Ruby hash table. */
1199
+ prof_result->threads = rb_hash_new();
1200
+ st_foreach(threads_tbl, collect_threads, prof_result->threads);
1201
+
1202
+ return Data_Wrap_Struct(cResult, prof_result_mark, prof_result_free, prof_result);
1203
+ }
1204
+
1205
+
1206
+ static prof_result_t *
1207
+ get_prof_result(VALUE obj)
1208
+ {
1209
+ if (BUILTIN_TYPE(obj) != T_DATA ||
1210
+ RDATA(obj)->dfree != (RUBY_DATA_FUNC) prof_result_free)
1211
+ {
1212
+ /* Should never happen */
1213
+ rb_raise(rb_eTypeError, "wrong result object");
1214
+ }
1215
+ return (prof_result_t *) DATA_PTR(obj);
1216
+ }
1217
+
1218
+ /* call-seq:
1219
+ threads -> Hash
1220
+
1221
+ Returns a hash table keyed on thread ID. For each thread id,
1222
+ the hash table stores another hash table that contains profiling
1223
+ information for each method called during the threads execution.
1224
+ That hash table is keyed on method name and contains
1225
+ RubyProf::MethodInfo objects. */
1226
+ static VALUE
1227
+ prof_result_threads(VALUE self)
1228
+ {
1229
+ prof_result_t *prof_result = get_prof_result(self);
1230
+ return prof_result->threads;
1231
+ }
1232
+
1233
+
1234
+
1235
+ /* call-seq:
1236
+ measure_mode -> measure_mode
1237
+
1238
+ Returns what ruby-prof is measuring. Valid values include:
1239
+
1240
+ *RubyProf::PROCESS_TIME - Measure process time. This is default. It is implemented using the clock functions in the C Runtime library.
1241
+ *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
1242
+ *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms.
1243
+ *RubyProf::ALLOCATIONS - Measure object allocations. This requires a patched Ruby interpreter.
1244
+ *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.
1245
+ *RubyProf::GC_RUNS - Measure number of garbage collections. This requires a patched Ruby interpreter.
1246
+ *RubyProf::GC_TIME - Measure time spent doing garbage collection. This requires a patched Ruby interpreter.*/
1247
+ static VALUE
1248
+ prof_get_measure_mode(VALUE self)
1249
+ {
1250
+ return INT2NUM(measure_mode);
1251
+ }
1252
+
1253
+ /* call-seq:
1254
+ measure_mode=value -> void
1255
+
1256
+ Specifies what ruby-prof should measure. Valid values include:
1257
+
1258
+ *RubyProf::PROCESS_TIME - Measure process time. This is default. It is implemented using the clock functions in the C Runtime library.
1259
+ *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
1260
+ *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms.
1261
+ *RubyProf::ALLOCATIONS - Measure object allocations. This requires a patched Ruby interpreter.
1262
+ *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.
1263
+ *RubyProf::GC_RUNS - Measure number of garbage collections. This requires a patched Ruby interpreter.
1264
+ *RubyProf::GC_TIME - Measure time spent doing garbage collection. This requires a patched Ruby interpreter.*/
1265
+ static VALUE
1266
+ prof_set_measure_mode(VALUE self, VALUE val)
1267
+ {
1268
+ long mode = NUM2LONG(val);
1269
+
1270
+ if (threads_tbl)
1271
+ {
1272
+ rb_raise(rb_eRuntimeError, "can't set measure_mode while profiling");
1273
+ }
1274
+
1275
+ switch (mode) {
1276
+ case MEASURE_PROCESS_TIME:
1277
+ get_measurement = measure_process_time;
1278
+ convert_measurement = convert_process_time;
1279
+ break;
1280
+
1281
+ case MEASURE_WALL_TIME:
1282
+ get_measurement = measure_wall_time;
1283
+ convert_measurement = convert_wall_time;
1284
+ break;
1285
+
1286
+ #if defined(MEASURE_CPU_TIME)
1287
+ case MEASURE_CPU_TIME:
1288
+ if (cpu_frequency == 0)
1289
+ cpu_frequency = get_cpu_frequency();
1290
+ get_measurement = measure_cpu_time;
1291
+ convert_measurement = convert_cpu_time;
1292
+ break;
1293
+ #endif
1294
+
1295
+ #if defined(MEASURE_ALLOCATIONS)
1296
+ case MEASURE_ALLOCATIONS:
1297
+ get_measurement = measure_allocations;
1298
+ convert_measurement = convert_allocations;
1299
+ break;
1300
+ #endif
1301
+
1302
+ #if defined(MEASURE_MEMORY)
1303
+ case MEASURE_MEMORY:
1304
+ get_measurement = measure_memory;
1305
+ convert_measurement = convert_memory;
1306
+ break;
1307
+ #endif
1308
+
1309
+ #if defined(MEASURE_GC_RUNS)
1310
+ case MEASURE_GC_RUNS:
1311
+ get_measurement = measure_gc_runs;
1312
+ convert_measurement = convert_gc_runs;
1313
+ break;
1314
+ #endif
1315
+
1316
+ #if defined(MEASURE_GC_TIME)
1317
+ case MEASURE_GC_TIME:
1318
+ get_measurement = measure_gc_time;
1319
+ convert_measurement = convert_gc_time;
1320
+ break;
1321
+ #endif
1322
+
1323
+ default:
1324
+ rb_raise(rb_eArgError, "invalid mode: %ld", mode);
1325
+ break;
1326
+ }
1327
+
1328
+ measure_mode = mode;
1329
+ return val;
1330
+ }
1331
+
1332
+ /* call-seq:
1333
+ exclude_threads= -> void
1334
+
1335
+ Specifies what threads ruby-prof should exclude from profiling */
1336
+ static VALUE
1337
+ prof_set_exclude_threads(VALUE self, VALUE threads)
1338
+ {
1339
+ int i;
1340
+
1341
+ if (threads_tbl != NULL)
1342
+ {
1343
+ rb_raise(rb_eRuntimeError, "can't set exclude_threads while profiling");
1344
+ }
1345
+
1346
+ /* Stay simple, first free the old hash table */
1347
+ if (exclude_threads_tbl)
1348
+ {
1349
+ st_free_table(exclude_threads_tbl);
1350
+ exclude_threads_tbl = NULL;
1351
+ }
1352
+
1353
+ /* Now create a new one if the user passed in any threads */
1354
+ if (threads != Qnil)
1355
+ {
1356
+ Check_Type(threads, T_ARRAY);
1357
+ exclude_threads_tbl = st_init_numtable();
1358
+
1359
+ for (i=0; i < RARRAY_LEN(threads); ++i)
1360
+ {
1361
+ VALUE thread = rb_ary_entry(threads, i);
1362
+ st_insert(exclude_threads_tbl, (st_data_t) rb_obj_id(thread), 0);
1363
+ }
1364
+ }
1365
+ return threads;
1366
+ }
1367
+
1368
+
1369
+ /* ========= Profiling ============= */
1370
+ void
1371
+ prof_install_hook()
1372
+ {
1373
+ #ifdef RUBY_VM
1374
+ rb_add_event_hook(prof_event_hook,
1375
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1376
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
1377
+ | RUBY_EVENT_LINE, Qnil);
1378
+ #else
1379
+ rb_add_event_hook(prof_event_hook,
1380
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1381
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
1382
+ | RUBY_EVENT_LINE);
1383
+ #endif
1384
+
1385
+ #if defined(TOGGLE_GC_STATS)
1386
+ rb_gc_enable_stats();
1387
+ #endif
1388
+ }
1389
+
1390
+ void
1391
+ prof_remove_hook()
1392
+ {
1393
+ #if defined(TOGGLE_GC_STATS)
1394
+ rb_gc_disable_stats();
1395
+ #endif
1396
+
1397
+ /* Now unregister from event */
1398
+ rb_remove_event_hook(prof_event_hook);
1399
+ }
1400
+
1401
+
1402
+
1403
+ /* call-seq:
1404
+ running? -> boolean
1405
+
1406
+ Returns whether a profile is currently running.*/
1407
+ static VALUE
1408
+ prof_running(VALUE self)
1409
+ {
1410
+ if (threads_tbl != NULL)
1411
+ return Qtrue;
1412
+ else
1413
+ return Qfalse;
1414
+ }
1415
+
1416
+ /* call-seq:
1417
+ start -> RubyProf
1418
+
1419
+ Starts recording profile data.*/
1420
+ static VALUE
1421
+ prof_start(VALUE self)
1422
+ {
1423
+ if (threads_tbl != NULL)
1424
+ {
1425
+ rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
1426
+ }
1427
+
1428
+ /* Setup globals */
1429
+ last_thread_data = NULL;
1430
+ threads_tbl = threads_table_create();
1431
+
1432
+ prof_install_hook();
1433
+ return self;
1434
+ }
1435
+
1436
+ /* call-seq:
1437
+ pause -> RubyProf
1438
+
1439
+ Pauses collecting profile data. */
1440
+ static VALUE
1441
+ prof_pause(VALUE self)
1442
+ {
1443
+ if (threads_tbl == NULL)
1444
+ {
1445
+ rb_raise(rb_eRuntimeError, "RubyProf is not running.");
1446
+ }
1447
+
1448
+ prof_remove_hook();
1449
+ return self;
1450
+ }
1451
+
1452
+ /* call-seq:
1453
+ resume {block} -> RubyProf
1454
+
1455
+ Resumes recording profile data.*/
1456
+ static VALUE
1457
+ prof_resume(VALUE self)
1458
+ {
1459
+ if (threads_tbl == NULL)
1460
+ {
1461
+ prof_start(self);
1462
+ }
1463
+ else
1464
+ {
1465
+ prof_install_hook();
1466
+ }
1467
+
1468
+ if (rb_block_given_p())
1469
+ {
1470
+ rb_ensure(rb_yield, self, prof_pause, self);
1471
+ }
1472
+
1473
+ return self;
1474
+ }
1475
+
1476
+ /* call-seq:
1477
+ stop -> RubyProf::Result
1478
+
1479
+ Stops collecting profile data and returns a RubyProf::Result object. */
1480
+ static VALUE
1481
+ prof_stop(VALUE self)
1482
+ {
1483
+ VALUE result = Qnil;
1484
+
1485
+ prof_remove_hook();
1486
+
1487
+ prof_pop_threads();
1488
+
1489
+ /* Create the result */
1490
+ result = prof_result_new();
1491
+
1492
+ /* Unset the last_thread_data (very important!)
1493
+ and the threads table */
1494
+ last_thread_data = NULL;
1495
+ threads_table_free(threads_tbl);
1496
+ threads_tbl = NULL;
1497
+
1498
+ return result;
1499
+ }
1500
+
1501
+ /* call-seq:
1502
+ profile {block} -> RubyProf::Result
1503
+
1504
+ Profiles the specified block and returns a RubyProf::Result object. */
1505
+ static VALUE
1506
+ prof_profile(VALUE self)
1507
+ {
1508
+ int result;
1509
+
1510
+ if (!rb_block_given_p())
1511
+ {
1512
+ rb_raise(rb_eArgError, "A block must be provided to the profile method.");
1513
+ }
1514
+
1515
+ prof_start(self);
1516
+ rb_protect(rb_yield, self, &result);
1517
+ return prof_stop(self);
1518
+ }
1519
+
1520
+ /* Get arround annoying limitations in RDOC */
1521
+
1522
+ /* Document-method: measure_process_time
1523
+ call-seq:
1524
+ measure_process_time -> float
1525
+
1526
+ Returns the process time.*/
1527
+
1528
+ /* Document-method: measure_wall_time
1529
+ call-seq:
1530
+ measure_wall_time -> float
1531
+
1532
+ Returns the wall time.*/
1533
+
1534
+ /* Document-method: measure_cpu_time
1535
+ call-seq:
1536
+ measure_cpu_time -> float
1537
+
1538
+ Returns the cpu time.*/
1539
+
1540
+ /* Document-method: get_cpu_frequency
1541
+ call-seq:
1542
+ cpu_frequency -> int
1543
+
1544
+ Returns the cpu's frequency. This value is needed when
1545
+ RubyProf::measure_mode is set to CPU_TIME. */
1546
+
1547
+ /* Document-method: cpu_frequency
1548
+ call-seq:
1549
+ cpu_frequency -> int
1550
+
1551
+ Returns the cpu's frequency. This value is needed when
1552
+ RubyProf::measure_mode is set to CPU_TIME. */
1553
+
1554
+ /* Document-method: cpu_frequency=
1555
+ call-seq:
1556
+ cpu_frequency = frequency
1557
+
1558
+ Sets the cpu's frequency. This value is needed when
1559
+ RubyProf::measure_mode is set to CPU_TIME. */
1560
+
1561
+ /* Document-method: measure_allocations
1562
+ call-seq:
1563
+ measure_allocations -> int
1564
+
1565
+ Returns the total number of object allocations since Ruby started.*/
1566
+
1567
+ /* Document-method: measure_memory
1568
+ call-seq:
1569
+ measure_memory -> int
1570
+
1571
+ Returns total allocated memory in bytes.*/
1572
+
1573
+ /* Document-method: measure_gc_runs
1574
+ call-seq:
1575
+ gc_runs -> Integer
1576
+
1577
+ Returns the total number of garbage collections.*/
1578
+
1579
+ /* Document-method: measure_gc_time
1580
+ call-seq:
1581
+ gc_time -> Integer
1582
+
1583
+ Returns the time spent doing garbage collections in microseconds.*/
1584
+
1585
+
1586
+ #if defined(_WIN32)
1587
+ __declspec(dllexport)
1588
+ #endif
1589
+ void
1590
+
1591
+ Init_ruby_prof()
1592
+ {
1593
+ mProf = rb_define_module("RubyProf");
1594
+ rb_define_const(mProf, "VERSION", rb_str_new2(RUBY_PROF_VERSION));
1595
+ rb_define_module_function(mProf, "start", prof_start, 0);
1596
+ rb_define_module_function(mProf, "stop", prof_stop, 0);
1597
+ rb_define_module_function(mProf, "resume", prof_resume, 0);
1598
+ rb_define_module_function(mProf, "pause", prof_pause, 0);
1599
+ rb_define_module_function(mProf, "running?", prof_running, 0);
1600
+ rb_define_module_function(mProf, "profile", prof_profile, 0);
1601
+
1602
+ rb_define_singleton_method(mProf, "exclude_threads=", prof_set_exclude_threads, 1);
1603
+ rb_define_singleton_method(mProf, "measure_mode", prof_get_measure_mode, 0);
1604
+ rb_define_singleton_method(mProf, "measure_mode=", prof_set_measure_mode, 1);
1605
+
1606
+ rb_define_const(mProf, "CLOCKS_PER_SEC", INT2NUM(CLOCKS_PER_SEC));
1607
+ rb_define_const(mProf, "PROCESS_TIME", INT2NUM(MEASURE_PROCESS_TIME));
1608
+ rb_define_singleton_method(mProf, "measure_process_time", prof_measure_process_time, 0); /* in measure_process_time.h */
1609
+ rb_define_const(mProf, "WALL_TIME", INT2NUM(MEASURE_WALL_TIME));
1610
+ rb_define_singleton_method(mProf, "measure_wall_time", prof_measure_wall_time, 0); /* in measure_wall_time.h */
1611
+
1612
+ #ifndef MEASURE_CPU_TIME
1613
+ rb_define_const(mProf, "CPU_TIME", Qnil);
1614
+ #else
1615
+ rb_define_const(mProf, "CPU_TIME", INT2NUM(MEASURE_CPU_TIME));
1616
+ rb_define_singleton_method(mProf, "measure_cpu_time", prof_measure_cpu_time, 0); /* in measure_cpu_time.h */
1617
+ rb_define_singleton_method(mProf, "cpu_frequency", prof_get_cpu_frequency, 0); /* in measure_cpu_time.h */
1618
+ rb_define_singleton_method(mProf, "cpu_frequency=", prof_set_cpu_frequency, 1); /* in measure_cpu_time.h */
1619
+ #endif
1620
+
1621
+ #ifndef MEASURE_ALLOCATIONS
1622
+ rb_define_const(mProf, "ALLOCATIONS", Qnil);
1623
+ #else
1624
+ rb_define_const(mProf, "ALLOCATIONS", INT2NUM(MEASURE_ALLOCATIONS));
1625
+ rb_define_singleton_method(mProf, "measure_allocations", prof_measure_allocations, 0); /* in measure_allocations.h */
1626
+ #endif
1627
+
1628
+ #ifndef MEASURE_MEMORY
1629
+ rb_define_const(mProf, "MEMORY", Qnil);
1630
+ #else
1631
+ rb_define_const(mProf, "MEMORY", INT2NUM(MEASURE_MEMORY));
1632
+ rb_define_singleton_method(mProf, "measure_memory", prof_measure_memory, 0); /* in measure_memory.h */
1633
+ #endif
1634
+
1635
+ #ifndef MEASURE_GC_RUNS
1636
+ rb_define_const(mProf, "GC_RUNS", Qnil);
1637
+ #else
1638
+ rb_define_const(mProf, "GC_RUNS", INT2NUM(MEASURE_GC_RUNS));
1639
+ rb_define_singleton_method(mProf, "measure_gc_runs", prof_measure_gc_runs, 0); /* in measure_gc_runs.h */
1640
+ #endif
1641
+
1642
+ #ifndef MEASURE_GC_TIME
1643
+ rb_define_const(mProf, "GC_TIME", Qnil);
1644
+ #else
1645
+ rb_define_const(mProf, "GC_TIME", INT2NUM(MEASURE_GC_TIME));
1646
+ rb_define_singleton_method(mProf, "measure_gc_time", prof_measure_gc_time, 0); /* in measure_gc_time.h */
1647
+ #endif
1648
+
1649
+ cResult = rb_define_class_under(mProf, "Result", rb_cObject);
1650
+ rb_undef_method(CLASS_OF(cMethodInfo), "new");
1651
+ rb_define_method(cResult, "threads", prof_result_threads, 0);
1652
+
1653
+ /* MethodInfo */
1654
+ cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
1655
+ rb_undef_method(CLASS_OF(cMethodInfo), "new");
1656
+
1657
+ rb_define_method(cMethodInfo, "klass", prof_method_klass, 0);
1658
+ rb_define_method(cMethodInfo, "klass_name", prof_klass_name, 0);
1659
+ rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
1660
+ rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
1661
+ rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
1662
+
1663
+ rb_define_method(cMethodInfo, "source_file", prof_method_source_file,0);
1664
+ rb_define_method(cMethodInfo, "line", prof_method_line, 0);
1665
+
1666
+ rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
1667
+
1668
+ /* CallInfo */
1669
+ cCallInfo = rb_define_class_under(mProf, "CallInfo", rb_cObject);
1670
+ rb_undef_method(CLASS_OF(cCallInfo), "new");
1671
+ rb_define_method(cCallInfo, "parent", prof_call_info_parent, 0);
1672
+ rb_define_method(cCallInfo, "children", prof_call_info_children, 0);
1673
+ rb_define_method(cCallInfo, "target", prof_call_info_target, 0);
1674
+ rb_define_method(cCallInfo, "called", prof_call_info_called, 0);
1675
+ rb_define_method(cCallInfo, "total_time", prof_call_info_total_time, 0);
1676
+ rb_define_method(cCallInfo, "self_time", prof_call_info_self_time, 0);
1677
+ rb_define_method(cCallInfo, "wait_time", prof_call_info_wait_time, 0);
1678
+ rb_define_method(cCallInfo, "line", prof_call_info_line, 0);
1679
+ }