rdp-ruby-prof 0.7.4

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 (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
+ }