ruby-prof 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/CHANGES +17 -0
  2. data/LICENSE +23 -0
  3. data/README +220 -0
  4. data/Rakefile +141 -0
  5. data/bin/ruby-prof +154 -0
  6. data/doc/classes/RubyProf.html +563 -0
  7. data/doc/classes/RubyProf/CallInfo.html +274 -0
  8. data/doc/classes/RubyProf/FlatPrinter.html +207 -0
  9. data/doc/classes/RubyProf/GraphHtmlPrinter.html +538 -0
  10. data/doc/classes/RubyProf/GraphPrinter.html +240 -0
  11. data/doc/classes/RubyProf/MethodInfo.html +556 -0
  12. data/doc/classes/RubyProf/ProfileTask.html +395 -0
  13. data/doc/classes/RubyProf/Result.html +234 -0
  14. data/doc/created.rid +1 -0
  15. data/doc/files/LICENSE.html +142 -0
  16. data/doc/files/README.html +376 -0
  17. data/doc/files/bin/ruby-prof.html +143 -0
  18. data/doc/files/examples/flat_txt.html +187 -0
  19. data/doc/files/examples/graph_html.html +948 -0
  20. data/doc/files/examples/graph_txt.html +305 -0
  21. data/doc/files/ext/ruby_prof_c.html +101 -0
  22. data/doc/files/lib/ruby-prof/flat_printer_rb.html +101 -0
  23. data/doc/files/lib/ruby-prof/graph_html_printer_rb.html +108 -0
  24. data/doc/files/lib/ruby-prof/graph_printer_rb.html +101 -0
  25. data/doc/files/lib/ruby-prof/profiletask_rb.html +109 -0
  26. data/doc/files/lib/ruby-prof_rb.html +111 -0
  27. data/doc/files/lib/unprof_rb.html +108 -0
  28. data/doc/fr_class_index.html +34 -0
  29. data/doc/fr_file_index.html +39 -0
  30. data/doc/fr_method_index.html +67 -0
  31. data/doc/index.html +24 -0
  32. data/doc/rdoc-style.css +208 -0
  33. data/examples/flat.txt +57 -0
  34. data/examples/graph.html +827 -0
  35. data/examples/graph.txt +171 -0
  36. data/ext/extconf.rb +19 -0
  37. data/ext/ruby_prof.c +1433 -0
  38. data/lib/ruby-prof.rb +38 -0
  39. data/lib/ruby-prof/flat_printer.rb +76 -0
  40. data/lib/ruby-prof/graph_html_printer.rb +227 -0
  41. data/lib/ruby-prof/graph_printer.rb +142 -0
  42. data/lib/ruby-prof/profiletask.rb +150 -0
  43. data/lib/unprof.rb +8 -0
  44. data/test/basic_test.rb +141 -0
  45. data/test/clock_mode_test.rb +73 -0
  46. data/test/module_test.rb +45 -0
  47. data/test/prime.rb +58 -0
  48. data/test/prime_test.rb +24 -0
  49. data/test/printers_test.rb +28 -0
  50. data/test/recursive_test.rb +55 -0
  51. data/test/test.rb +3 -0
  52. data/test/test_helper.rb +45 -0
  53. data/test/test_suite.rb +9 -0
  54. data/test/thread_test.rb +32 -0
  55. data/test/timing_test.rb +90 -0
  56. metadata +121 -0
@@ -0,0 +1,171 @@
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 profiles. 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
+ * Object#test - An instance method "test" on the class "Object"
128
+ * <Class:Object>#test - A class method "test" on the class "Object"
129
+ * <Object:Object>#test - A singleton method "test" on a singleton class inherited from "Object"
130
+ * Module.test - An instance method "test" on the module "Module"
131
+
132
+ For example, we see that 99.48% of the time was spent in Integer#upto and its children.
133
+ Of that time, 4.06 seconds was spent in Integer#upto itself and 4.66 in its children.
134
+ Overall, Integer#upto was called 501 times.
135
+
136
+ == Parents
137
+ In each entry, the lines above the primary line are the methods that
138
+ called the current method. If the current method is a root method then
139
+ no parents are shown.
140
+
141
+
142
+ For parent lines, the columns represent:
143
+
144
+ total - The time spent in the current method and it children on behalf of the parent method.
145
+ self - The time spent in this method on behalf of the parent method.
146
+ children - The time spent in this method's children on behalf of the parent.
147
+ calls - The number of times the parent method called this child
148
+
149
+ Looking at Integer#upto again, we see that it was called 500 times from Object#is_prime
150
+ and 1 time from find_largest. Of the 8.72 total seconds spent in Integer#upto, 6.63
151
+ were done for Object#is_prime and 2.09 for Object#find_largest.
152
+
153
+ == Children
154
+ In each entry, the lines below the primary line are the methods that
155
+ the current method called. If the current method is a leaf method then
156
+ no children are shown.
157
+
158
+ For children lines, the columns represent:
159
+
160
+ total - The time spent in the child, and its children, on behalf of the current method
161
+ self - The time spent in the child on behalf of the current method.
162
+ children - The time spent in the child's children (ie, granchildren) in behalf of the current method
163
+ calls - The number of times the child method was called by the current method.
164
+
165
+ Taking our example of Integer#upto, we see that it called five other methods - Array#[],
166
+ Fixnum#>, Kernel.sleep, Fixnum#= and Fixnum#%. Looking at Kernel.sleep, we see that
167
+ its spent 2.09 seconds working for Integer#upto and its children spent 0 time working for
168
+ Integer#upto. To see the overall time Kernel.sleep took we would have to look up its entry
169
+ in the graph table.
170
+
171
+
data/ext/extconf.rb ADDED
@@ -0,0 +1,19 @@
1
+ require "mkmf"
2
+
3
+ if RUBY_VERSION >= "1.9"
4
+ if RUBY_RELEASE_DATE < "2005-03-17"
5
+ STDERR.print("Ruby version is too old\n")
6
+ exit(1)
7
+ end
8
+ elsif RUBY_VERSION >= "1.8"
9
+ if RUBY_RELEASE_DATE < "2005-03-22"
10
+ STDERR.print("Ruby version is too old\n")
11
+ exit(1)
12
+ end
13
+ else
14
+ STDERR.print("Ruby version is too old\n")
15
+ exit(1)
16
+ end
17
+
18
+ have_header("sys/times.h")
19
+ create_makefile("ruby_prof")
data/ext/ruby_prof.c ADDED
@@ -0,0 +1,1433 @@
1
+ /*
2
+ * $Id: prof.c 298 2005-05-11 08:33:37Z shugo $
3
+ * Copyright (C) 2005 Shugo Maeda <shugo@ruby-lang.org>
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
+ #include <stdio.h>
29
+ #include <time.h>
30
+ #ifdef HAVE_SYS_TIMES_H
31
+ #include <sys/times.h>
32
+ #endif
33
+
34
+ #include <ruby.h>
35
+ #include <node.h>
36
+ #include <st.h>
37
+
38
+ #define PROF_VERSION "0.4.0"
39
+
40
+ static VALUE mProf;
41
+ static VALUE cResult;
42
+ static VALUE cMethodInfo;
43
+ static VALUE cCallInfo;
44
+
45
+ #ifdef HAVE_LONG_LONG
46
+ typedef LONG_LONG prof_clock_t;
47
+ #else
48
+ typedef unsigned long prof_clock_t;
49
+ #endif
50
+
51
+ typedef struct {
52
+ VALUE klass;
53
+ ID mid;
54
+ int called;
55
+ prof_clock_t self_time;
56
+ prof_clock_t total_time;
57
+ } prof_call_info_t;
58
+
59
+ typedef struct {
60
+ /* Cache hash value for speed reasons. */
61
+ st_data_t key;
62
+ VALUE klass;
63
+ ID mid;
64
+ int thread_id;
65
+ int called;
66
+ prof_clock_t self_time;
67
+ prof_clock_t total_time;
68
+ st_table *parents;
69
+ st_table *children;
70
+ /* Hack - piggyback a field to keep track of the
71
+ of times the method appears in the current
72
+ stack. Used to detect recursive cycles. This
73
+ works because there is an instance of this struct
74
+ per method per thread. Could have a separate
75
+ hash table...would be cleaner but adds a bit of
76
+ code and 1 extra lookup per event.*/
77
+ int stack_count;
78
+ } prof_method_t;
79
+
80
+ typedef struct {
81
+ /* Cache prof_method_t values to significantly
82
+ increase speed. */
83
+ prof_method_t *method_info;
84
+ prof_clock_t start_time;
85
+ prof_clock_t child_cost;
86
+ } prof_data_t;
87
+
88
+ typedef struct {
89
+ prof_data_t *start;
90
+ prof_data_t *end;
91
+ prof_data_t *ptr;
92
+ } prof_stack_t;
93
+
94
+ typedef struct {
95
+ prof_stack_t* stack;
96
+ st_table* minfo_table;
97
+ int thread_id;
98
+ } thread_data_t;
99
+
100
+ typedef struct {
101
+ VALUE threads;
102
+ } prof_result_t;
103
+
104
+ static VALUE toplevel_id;
105
+ static st_data_t toplevel_key;
106
+ static int clock_mode;
107
+ static st_table *threads_tbl = NULL;
108
+ static VALUE class_tbl = Qnil;
109
+
110
+ #define CLOCK_MODE_PROCESS 0
111
+ #define CLOCK_MODE_WALL 1
112
+ #if defined(_WIN32) || (defined(__GNUC__) && (defined(__i386__) || defined(__powerpc__) || defined(__ppc__)))
113
+ #define CLOCK_MODE_CPU 2
114
+ static double cpu_frequency;
115
+ #endif
116
+
117
+ #define INITIAL_STACK_SIZE 8
118
+
119
+ static prof_clock_t
120
+ clock_get_clock()
121
+ {
122
+ return clock();
123
+ }
124
+
125
+ static double
126
+ clock_clock2sec(prof_clock_t c)
127
+ {
128
+ return (double) c / CLOCKS_PER_SEC;
129
+ }
130
+
131
+ static prof_clock_t
132
+ gettimeofday_get_clock()
133
+ {
134
+ struct timeval tv;
135
+ gettimeofday(&tv, NULL);
136
+ return tv.tv_sec * 1000000 + tv.tv_usec;
137
+ }
138
+
139
+ static double
140
+ gettimeofday_clock2sec(prof_clock_t c)
141
+ {
142
+ return (double) c / 1000000;
143
+ }
144
+
145
+ #ifdef CLOCK_MODE_CPU
146
+
147
+
148
+ #if defined(__GNUC__)
149
+
150
+ static prof_clock_t
151
+ cpu_get_clock()
152
+ {
153
+ #if defined(__i386__)
154
+ unsigned long long x;
155
+ __asm__ __volatile__ ("rdtsc" : "=A" (x));
156
+ return x;
157
+ #elif defined(__powerpc__) || defined(__ppc__)
158
+ unsigned long long x, y;
159
+
160
+ __asm__ __volatile__ ("\n\
161
+ 1: mftbu %1\n\
162
+ mftb %L0\n\
163
+ mftbu %0\n\
164
+ cmpw %0,%1\n\
165
+ bne- 1b"
166
+ : "=r" (x), "=r" (y));
167
+ return x;
168
+ #endif
169
+ }
170
+
171
+ #elif defined(_WIN32)
172
+
173
+ static prof_clock_t
174
+ cpu_get_clock()
175
+ {
176
+ prof_clock_t cycles = 0;
177
+
178
+ __asm
179
+ {
180
+ rdtsc
181
+ mov DWORD PTR cycles, eax
182
+ mov DWORD PTR [cycles + 4], edx
183
+ }
184
+ return cycles;
185
+ }
186
+
187
+ #endif
188
+
189
+
190
+ /* The _WIN32 check is needed for msys (and maybe cygwin?) */
191
+ #if defined(__GNUC__) && !defined(_WIN32)
192
+
193
+ double get_cpu_frequency()
194
+ {
195
+ unsigned long long x, y;
196
+
197
+ struct timespec ts;
198
+ ts.tv_sec = 0;
199
+ ts.tv_nsec = 500000000;
200
+ x = cpu_get_clock();
201
+ nanosleep(&ts, NULL);
202
+ y = cpu_get_clock();
203
+ return (y - x) * 2;
204
+ }
205
+
206
+ #elif defined(_WIN32)
207
+
208
+ double get_cpu_frequency()
209
+ {
210
+ unsigned long long x, y;
211
+ double frequency;
212
+ x = cpu_get_clock();
213
+
214
+ /* Use the windows sleep function, not Ruby's */
215
+ Sleep(500);
216
+ y = cpu_get_clock();
217
+ frequency = 2*(y-x);
218
+ return frequency;
219
+ }
220
+ #endif
221
+
222
+ static double
223
+ cpu_clock2sec(prof_clock_t c)
224
+ {
225
+ return (double) c / cpu_frequency;
226
+ }
227
+
228
+ /* call-seq:
229
+ cpu_frequency -> int
230
+
231
+ Returns the cpu's frequency. This value is needed when using the
232
+ cpu RubyProf::clock_mode. */
233
+ static VALUE
234
+ prof_get_cpu_frequency(VALUE self)
235
+ {
236
+ return rb_float_new(cpu_frequency);
237
+ }
238
+
239
+ /* call-seq:
240
+ cpu_frequency=value -> void
241
+
242
+ Sets the cpu's frequency. This value is needed when using the
243
+ cpu RubyProf::clock_mode. */
244
+ static VALUE
245
+ prof_set_cpu_freqeuncy(VALUE self, VALUE val)
246
+ {
247
+ cpu_frequency = NUM2DBL(val);
248
+ return val;
249
+ }
250
+
251
+ #endif
252
+
253
+ static prof_clock_t (*get_clock)() = clock_get_clock;
254
+ static double (*clock2sec)(prof_clock_t) = clock_clock2sec;
255
+
256
+
257
+ /* Helper method to get the id of a Ruby thread. */
258
+ static inline int
259
+ get_thread_id(VALUE thread)
260
+ {
261
+ return NUM2INT(rb_obj_id(thread));
262
+ }
263
+
264
+ static VALUE
265
+ figure_singleton_name(VALUE klass)
266
+ {
267
+ VALUE result = Qnil;
268
+
269
+ /* We have come across a singleton object. First
270
+ figure out what it is attached to.*/
271
+ VALUE attached = rb_iv_get(klass, "__attached__");
272
+
273
+
274
+ /* Is this a singleton class acting as a metaclass? */
275
+ if (TYPE(attached) == T_CLASS)
276
+ {
277
+ result = rb_str_new2("<Class::");
278
+ rb_str_append(result, rb_inspect(attached));
279
+ rb_str_cat2(result, ">#");
280
+ }
281
+
282
+ /* Is this for singleton methods on a module? */
283
+ else if (TYPE(attached) == T_MODULE)
284
+ {
285
+ result = rb_str_new2("<Module::");
286
+ rb_str_append(result, rb_inspect(attached));
287
+ rb_str_cat2(result, ">#");
288
+ }
289
+
290
+ /* Is it a regular singleton class for an object? */
291
+ else if (TYPE(attached) == T_OBJECT)
292
+ {
293
+ /* Make sure to get the super class so that we don't
294
+ mistakenly grab a T_ICLASS which would lead to
295
+ unknown method errors. */
296
+ VALUE super = rb_class_real(RCLASS(klass)->super);
297
+ result = rb_str_new2("<Object::");
298
+ rb_str_append(result, rb_inspect(super));
299
+ rb_str_cat2(result, ">#");
300
+ }
301
+ else
302
+ {
303
+ /* Should never happen. */
304
+ result = rb_str_new2("<Unknown:");
305
+ rb_str_append(result, rb_inspect(klass));
306
+ rb_str_cat2(result, ">#");
307
+ rb_raise(rb_eRuntimeError, "Unknown singleton class: %i", result);
308
+ }
309
+
310
+ return result;
311
+ }
312
+
313
+ static VALUE
314
+ method_name(VALUE klass, ID mid)
315
+ {
316
+ VALUE result;
317
+ VALUE method_name;
318
+
319
+ if (mid == ID_ALLOCATOR)
320
+ method_name = rb_str_new2("allocate");
321
+ else
322
+ method_name = rb_String(ID2SYM(mid));
323
+
324
+
325
+ if (klass == Qnil)
326
+ result = rb_str_new2("#");
327
+ else if (TYPE(klass) == T_MODULE)
328
+ {
329
+ result = rb_inspect(klass);
330
+ rb_str_cat2(result, "#");
331
+ }
332
+ else if (TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
333
+ {
334
+ result = figure_singleton_name(klass);
335
+ }
336
+ else if (TYPE(klass) == T_CLASS)
337
+ {
338
+ result = rb_inspect(klass);
339
+ rb_str_cat2(result, "#");
340
+ }
341
+ else
342
+ {
343
+ /* Should never happen. */
344
+ result = rb_str_new2("Unknown#");
345
+ rb_str_append(result, rb_inspect(klass));
346
+ rb_str_cat2(result, ">#");
347
+ rb_raise(rb_eRuntimeError, "Unsupported type in method name: %i\n", result);
348
+ }
349
+
350
+ /* Last add in the method name */
351
+ rb_str_append(result, method_name);
352
+
353
+ return result;
354
+ }
355
+
356
+ static inline st_data_t
357
+ method_key(VALUE klass, ID mid)
358
+ {
359
+ return klass ^ mid;
360
+ }
361
+
362
+
363
+ /* -- Stack to track methods call sequence and times ---- */
364
+ static prof_stack_t *
365
+ stack_create()
366
+ {
367
+ prof_stack_t *stack;
368
+
369
+ stack = ALLOC(prof_stack_t);
370
+ stack->start = stack->ptr =
371
+ ALLOC_N(prof_data_t, INITIAL_STACK_SIZE);
372
+ stack->end = stack->start + INITIAL_STACK_SIZE;
373
+ return stack;
374
+ }
375
+
376
+ static void
377
+ stack_free(prof_stack_t *stack)
378
+ {
379
+ xfree(stack->start);
380
+ xfree(stack);
381
+ }
382
+
383
+ static inline prof_data_t *
384
+ stack_push(prof_stack_t *stack)
385
+ {
386
+ if (stack->ptr == stack->end) {
387
+ int len, new_capa;
388
+ len = stack->ptr - stack->start;
389
+ new_capa = (stack->end - stack->start) * 2;
390
+ REALLOC_N(stack->start, prof_data_t, new_capa);
391
+ stack->ptr = stack->start + len;
392
+ stack->end = stack->start + new_capa;
393
+ }
394
+ return stack->ptr++;
395
+ }
396
+
397
+ static inline prof_data_t *
398
+ stack_pop(prof_stack_t *stack)
399
+ {
400
+ if (stack->ptr == stack->start)
401
+ return NULL;
402
+ else
403
+ return --stack->ptr;
404
+ }
405
+
406
+ static inline prof_data_t *
407
+ stack_peek(prof_stack_t *stack)
408
+ {
409
+ if (stack->ptr == stack->start)
410
+ return NULL;
411
+ else
412
+ return stack->ptr - 1;
413
+ }
414
+
415
+
416
+
417
+ /* --- Keeps track of the methods the current method calls */
418
+ static st_table *
419
+ minfo_table_create()
420
+ {
421
+ return st_init_numtable();
422
+ }
423
+
424
+ static inline int
425
+ minfo_table_insert(st_table *table, st_data_t key, prof_method_t *val)
426
+ {
427
+ return st_insert(table, key, (st_data_t) val);
428
+ }
429
+
430
+ static inline prof_method_t *
431
+ minfo_table_lookup(st_table *table, st_data_t key)
432
+ {
433
+ st_data_t val;
434
+ if (st_lookup(table, key, &val)) {
435
+ return (prof_method_t *) val;
436
+ }
437
+ else {
438
+ return NULL;
439
+ }
440
+ }
441
+
442
+ static void
443
+ minfo_table_free(st_table *table)
444
+ {
445
+ st_free_table(table);
446
+ }
447
+
448
+
449
+ /* ---- Hash, keyed on class/method_id, that holds
450
+ child call_info objects ---- */
451
+ static st_table *
452
+ child_table_create()
453
+ {
454
+ return st_init_numtable();
455
+ }
456
+
457
+ static inline int
458
+ child_table_insert(st_table *table, st_data_t key, prof_call_info_t *val)
459
+ {
460
+ return st_insert(table, key, (st_data_t) val);
461
+ }
462
+
463
+ static inline prof_call_info_t *
464
+ child_table_lookup(st_table *table, st_data_t key)
465
+ {
466
+ st_data_t val;
467
+ if (st_lookup(table, key, &val)) {
468
+ return (prof_call_info_t *) val;
469
+ }
470
+ else {
471
+ return NULL;
472
+ }
473
+ }
474
+
475
+ static void
476
+ child_table_free(st_table *table)
477
+ {
478
+ st_free_table(table);
479
+ }
480
+
481
+ /* Document-class: RubyProf::CallInfo
482
+ RubyProf::CallInfo is a helper class used by RubyProf::MethodInfo
483
+ to keep track of which child methods were called and how long
484
+ they took to execute. */
485
+
486
+ /* :nodoc: */
487
+ static prof_call_info_t *
488
+ call_info_create(VALUE klass, ID mid)
489
+ {
490
+ prof_call_info_t *result;
491
+
492
+ result = ALLOC(prof_call_info_t);
493
+ result->klass = klass;
494
+ result->mid = mid;
495
+ result->called = 0;
496
+ result->total_time = 0;
497
+ result->self_time = 0;
498
+ return result;
499
+ }
500
+
501
+ static void
502
+ call_info_free(prof_call_info_t *call_info)
503
+ {
504
+ xfree(call_info);
505
+ }
506
+
507
+ static int
508
+ free_call_infos(st_data_t key, st_data_t value, st_data_t data)
509
+ {
510
+ prof_call_info_t* call_info = (prof_call_info_t*) value;
511
+ call_info_free(call_info);
512
+ return ST_CONTINUE;
513
+ }
514
+
515
+ static VALUE
516
+ call_info_new(prof_call_info_t *result)
517
+ {
518
+ /* We don't want Ruby freeing the underlying C structures, that
519
+ is when the prof_method_t is freed. */
520
+ return Data_Wrap_Struct(cCallInfo, NULL, NULL, result);
521
+ }
522
+
523
+ static prof_call_info_t *
524
+ get_call_info_result(VALUE obj)
525
+ {
526
+ if (TYPE(obj) != T_DATA)
527
+ {
528
+ /* Should never happen */
529
+ rb_raise(rb_eTypeError, "Not a call info object");
530
+ }
531
+ return (prof_call_info_t *) DATA_PTR(obj);
532
+ }
533
+
534
+ /* call-seq:
535
+ called -> int
536
+
537
+ Returns the total amount of time this method was called. */
538
+ static VALUE
539
+ call_info_called(VALUE self)
540
+ {
541
+ prof_call_info_t *result = get_call_info_result(self);
542
+
543
+ return INT2NUM(result->called);
544
+ }
545
+
546
+ /* call-seq:
547
+ total_time -> float
548
+
549
+ Returns the total amount of time spent in this method and its children. */
550
+ static VALUE
551
+ call_info_total_time(VALUE self)
552
+ {
553
+ prof_call_info_t *result = get_call_info_result(self);
554
+
555
+ return rb_float_new(clock2sec(result->total_time));
556
+ }
557
+
558
+ /* call-seq:
559
+ self_time -> float
560
+
561
+ Returns the total amount of time spent in this method. */
562
+ static VALUE
563
+ call_info_self_time(VALUE self)
564
+ {
565
+ prof_call_info_t *result = get_call_info_result(self);
566
+
567
+ return rb_float_new(clock2sec(result->self_time));
568
+ }
569
+
570
+ /* call-seq:
571
+ children_time -> float
572
+
573
+ Returns the total amount of time spent in this method's children. */
574
+ static VALUE
575
+ call_info_children_time(VALUE self)
576
+ {
577
+ prof_call_info_t *result = get_call_info_result(self);
578
+ prof_clock_t children_time = result->total_time - result->self_time;
579
+ return rb_float_new(clock2sec(children_time));
580
+ }
581
+
582
+
583
+ /* Document-class: RubyProf::MethodInfo
584
+ The RubyProf::MethodInfo class stores profiling data for a method.
585
+ One instance of the RubyProf::MethodInfo class is created per method
586
+ called per thread. Thus, if a method is called in two different
587
+ thread then there will be two RubyProf::MethodInfo objects
588
+ created. RubyProf::MethodInfo objects can be accessed via
589
+ the RubyProf::Result object.
590
+ */
591
+
592
+ /* :nodoc: */
593
+ static prof_method_t *
594
+ prof_method_create(VALUE klass, ID mid, VALUE thread)
595
+ {
596
+ prof_method_t *result;
597
+
598
+ /* Store reference to klass so it is not garbage collected */
599
+ rb_hash_aset(class_tbl, klass, Qnil);
600
+
601
+ result = ALLOC(prof_method_t);
602
+ result->key = method_key(klass, mid);
603
+ result->called = 0;
604
+ result->total_time = 0;
605
+ result->self_time = 0;
606
+ result->klass = klass;
607
+ result->mid = mid;
608
+ result->thread_id = get_thread_id(thread);
609
+ result->parents = minfo_table_create();
610
+ result->children = child_table_create();
611
+ result->stack_count = 0;
612
+ return result;
613
+ }
614
+
615
+ static void
616
+ prof_method_mark(prof_method_t *data)
617
+ {
618
+ rb_gc_mark(data->klass);
619
+ }
620
+
621
+ static void
622
+ prof_method_free(prof_method_t *data)
623
+ {
624
+ st_foreach(data->children, free_call_infos, 0);
625
+ minfo_table_free(data->parents);
626
+ child_table_free(data->children);
627
+ xfree(data);
628
+ }
629
+
630
+ static VALUE
631
+ prof_method_new(prof_method_t *result)
632
+ {
633
+ return Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_free,
634
+ result);
635
+ }
636
+
637
+ static prof_method_t *
638
+ get_prof_method(VALUE obj)
639
+ {
640
+ if (TYPE(obj) != T_DATA ||
641
+ RDATA(obj)->dfree != (RUBY_DATA_FUNC) prof_method_free)
642
+ {
643
+ /* Should never happen */
644
+ rb_raise(rb_eTypeError, "wrong profile result");
645
+ }
646
+ return (prof_method_t *) DATA_PTR(obj);
647
+ }
648
+
649
+ /* call-seq:
650
+ called -> int
651
+
652
+ Returns the number of times this method was called. */
653
+ static VALUE
654
+ prof_method_called(VALUE self)
655
+ {
656
+ prof_method_t *result = get_prof_method(self);
657
+
658
+ return INT2NUM(result->called);
659
+ }
660
+
661
+
662
+ /* call-seq:
663
+ total_time -> float
664
+
665
+ Returns the total amount of time spent in this method and its children. */
666
+ static VALUE
667
+ prof_method_total_time(VALUE self)
668
+ {
669
+ prof_method_t *result = get_prof_method(self);
670
+
671
+ return rb_float_new(clock2sec(result->total_time));
672
+ }
673
+
674
+ /* call-seq:
675
+ self_time -> float
676
+
677
+ Returns the total amount of time spent in this method. */
678
+ static VALUE
679
+ prof_method_self_time(VALUE self)
680
+ {
681
+ prof_method_t *result = get_prof_method(self);
682
+
683
+ return rb_float_new(clock2sec(result->self_time));
684
+ }
685
+
686
+ /* call-seq:
687
+ children_time -> float
688
+
689
+ Returns the total amount of time spent in this method's children. */
690
+ static VALUE
691
+ prof_method_children_time(VALUE self)
692
+ {
693
+ prof_method_t *result = get_prof_method(self);
694
+ prof_clock_t children_time = result->total_time - result->self_time;
695
+ return rb_float_new(clock2sec(children_time));
696
+ }
697
+
698
+ /* call-seq:
699
+ thread_id -> id
700
+
701
+ Returns the id of the thread that executed this method.*/
702
+ static VALUE
703
+ prof_thread_id(VALUE self)
704
+ {
705
+ prof_method_t *result = get_prof_method(self);
706
+
707
+ return INT2FIX(result->thread_id);
708
+ }
709
+
710
+ /* call-seq:
711
+ method_class -> klass
712
+
713
+ Returns the Ruby klass that owns this method. */
714
+ static VALUE
715
+ prof_method_class(VALUE self)
716
+ {
717
+ prof_method_t *result = get_prof_method(self);
718
+
719
+ return result->klass;
720
+ }
721
+
722
+ /* call-seq:
723
+ method_id -> ID
724
+
725
+ Returns the id of this method. */
726
+ static VALUE
727
+ prof_method_id(VALUE self)
728
+ {
729
+ prof_method_t *result = get_prof_method(self);
730
+
731
+ return ID2SYM(result->mid);
732
+ }
733
+
734
+ /* call-seq:
735
+ method_name -> string
736
+
737
+ Returns the name of this object. The name may be in the form:
738
+ Object#method
739
+ Module.method
740
+ .method */
741
+ static VALUE
742
+ prof_method_name(VALUE self)
743
+ {
744
+ prof_method_t *method = get_prof_method(self);
745
+ return method_name(method->klass, method->mid);
746
+ }
747
+
748
+ static int
749
+ prof_method_collect_parents(st_data_t key, st_data_t value, st_data_t parents)
750
+ {
751
+ prof_method_t *parent = (prof_method_t *) value;
752
+
753
+ rb_ary_push(parents, INT2FIX((int) parent));
754
+ return ST_CONTINUE;
755
+ }
756
+
757
+
758
+ /* call-seq:
759
+ parents -> hash
760
+
761
+ Returns a hash table that lists all the methods that called this
762
+ method (ie, parents). The hash table is keyed on method name and contains references
763
+ to RubyProf::MethodInfo objects.*/
764
+ static VALUE
765
+ prof_method_parents(VALUE self)
766
+ {
767
+ VALUE result = rb_hash_new();
768
+ VALUE parents = rb_ary_new();
769
+ int len = 0;
770
+ int i = 0;
771
+
772
+ /* Get the list of parents */
773
+ prof_method_t *child = get_prof_method(self);
774
+ st_foreach(child->parents, prof_method_collect_parents, parents);
775
+
776
+ /* Iterate over each parent */
777
+ len = RARRAY(parents)->len;
778
+ for(i = 0; i<len; i++)
779
+ {
780
+ prof_call_info_t *call_info;
781
+
782
+ /* First get the parent */
783
+ VALUE item = rb_ary_entry(parents, i);
784
+ prof_method_t *parent = (prof_method_t *)(FIX2INT(item));
785
+
786
+ /* Now get the call info */
787
+ call_info = child_table_lookup(parent->children, child->key);
788
+
789
+ if (call_info == NULL)
790
+ {
791
+ /* Should never happen */
792
+ rb_raise(rb_eRuntimeError,
793
+ "Could not find parent call info object for %s",
794
+ method_name(child->klass, child->mid));
795
+ }
796
+
797
+ /* Create a new Ruby CallInfo object and store it into the hash
798
+ keyed on the parent's name. We use the parent's name because
799
+ we want to see that printed out for parent records in
800
+ a call graph. */
801
+ rb_hash_aset(result, method_name(parent->klass, parent->mid),
802
+ call_info_new(call_info));
803
+ }
804
+
805
+ return result;
806
+ }
807
+
808
+
809
+ static int
810
+ prof_method_collect_children(st_data_t key, st_data_t value, st_data_t result)
811
+ {
812
+ prof_call_info_t *call_info = (prof_call_info_t *) value;
813
+ VALUE name = method_name(call_info->klass, call_info->mid);
814
+ VALUE hash = (VALUE) result;
815
+
816
+ /* Create a new Ruby CallInfo object and store it into the hash
817
+ keyed on the parent's name. We use the parent's name because
818
+ we want to see that printed out for child records in
819
+ a call graph. */
820
+ rb_hash_aset(hash, name, call_info_new(call_info));
821
+ return ST_CONTINUE;
822
+ }
823
+
824
+ /* call-seq:
825
+ children -> hash
826
+
827
+ Returns a hash table that lists all the methods that this method
828
+ called (ie, children). The hash table is keyed on method name
829
+ and contains references to RubyProf::CallInfo objects.*/
830
+ static VALUE
831
+ prof_method_children(VALUE self)
832
+ {
833
+ /* Returns a hash table, keyed on method name, of call info
834
+ objects for all methods that this method calls (children). */
835
+
836
+ VALUE children = rb_hash_new();
837
+ prof_method_t *result = get_prof_method(self);
838
+ st_foreach(result->children, prof_method_collect_children, children);
839
+ return children;
840
+ }
841
+
842
+ /* :nodoc: */
843
+ static VALUE
844
+ prof_method_cmp(VALUE self, VALUE other)
845
+ {
846
+ /* For call graphs we want to sort methods by
847
+ their total time, not self time. */
848
+ prof_method_t *x = get_prof_method(self);
849
+ prof_method_t *y = get_prof_method(other);
850
+
851
+ /* Want toplevel to always be first */
852
+ if (x->klass == Qnil && x->mid == toplevel_id)
853
+ return INT2FIX(1);
854
+ else if (y->klass == Qnil && y->mid == toplevel_id)
855
+ return INT2FIX(-11);
856
+ else if (x->total_time < y->total_time)
857
+ return INT2FIX(-1);
858
+ else if (x->total_time == y->total_time)
859
+ return INT2FIX(0);
860
+ else
861
+ return INT2FIX(1);
862
+ }
863
+
864
+ static int
865
+ collect_methods(st_data_t key, st_data_t value, st_data_t result)
866
+ {
867
+ prof_method_t *method = (prof_method_t *) value;
868
+ VALUE name = method_name(method->klass, method->mid);
869
+ VALUE hash = (VALUE) result;
870
+
871
+ VALUE existing_value = rb_hash_aref(hash, name);
872
+
873
+ /* Sanity check. If we have generated the same method name for another prof_method
874
+ then we cannot put the current prof_method into the hash table. If we do, we
875
+ overwrite the reference to the other prof_method. That will mean that Ruby
876
+ will garbage collect it wreaking all sorts of havoc! Trust me - this one took
877
+ a long time to track down. */
878
+ if (existing_value != Qnil)
879
+ {
880
+ /* Definitely should never happen! */
881
+ rb_raise(rb_eRuntimeError,
882
+ "The name %s has already been assigned to another method. This is a bug - please report it.",
883
+ name);
884
+ }
885
+ rb_hash_aset(hash, name, prof_method_new(method));
886
+
887
+ return ST_CONTINUE;
888
+ }
889
+
890
+
891
+ /* ---- Keeps track of thread's stack and methods ---- */
892
+ static thread_data_t*
893
+ thread_data_create()
894
+ {
895
+ thread_data_t* result = ALLOC(thread_data_t);
896
+ result->stack = stack_create();
897
+ result->minfo_table = minfo_table_create();
898
+ return result;
899
+ }
900
+
901
+ static void
902
+ thread_data_free(thread_data_t* thread_data)
903
+ {
904
+ stack_free(thread_data->stack);
905
+ minfo_table_free(thread_data->minfo_table);
906
+ xfree(thread_data);
907
+ }
908
+
909
+
910
+ /* ---- Hash, keyed on thread, that stores thread's stack
911
+ and methods---- */
912
+
913
+ static st_table *
914
+ threads_table_create()
915
+ {
916
+ return st_init_numtable();
917
+ }
918
+
919
+ static inline int
920
+ threads_table_insert(st_table *table, VALUE thread, thread_data_t *thread_data)
921
+ {
922
+ /* Its too slow to key on the real thread id so just typecast thread instead. */
923
+ return st_insert(table, (st_data_t ) thread, (st_data_t) thread_data);
924
+ }
925
+
926
+ static inline thread_data_t *
927
+ threads_table_lookup(st_table *table, VALUE thread)
928
+ {
929
+ thread_data_t* result;
930
+ st_data_t val;
931
+
932
+ /* Its too slow to key on the real thread id so just typecast thread instead. */
933
+ if (st_lookup(table, (st_data_t) thread, &val))
934
+ {
935
+ result = (thread_data_t *) val;
936
+ }
937
+ else
938
+ {
939
+ prof_method_t *toplevel;
940
+ result = thread_data_create();
941
+ /* Store the real thread id here so it can be shown in the results. */
942
+ result->thread_id = get_thread_id(thread);
943
+
944
+ /* Add a toplevel method to the thread */
945
+ toplevel = prof_method_create(Qnil, toplevel_id, thread);
946
+ toplevel->called = 1;
947
+ toplevel->total_time = 0;
948
+ toplevel->self_time = 0;
949
+ minfo_table_insert(result->minfo_table, toplevel->key, toplevel);
950
+
951
+ /* Insert the table */
952
+ threads_table_insert(threads_tbl, thread, result);
953
+ }
954
+ return result;
955
+ }
956
+
957
+ static void
958
+ threads_table_free(st_table *table)
959
+ {
960
+ st_free_table(table);
961
+ }
962
+
963
+ static int
964
+ free_thread_data(st_data_t key, st_data_t value, st_data_t dummy)
965
+ {
966
+ thread_data_free((thread_data_t*)value);
967
+ return ST_CONTINUE;
968
+ }
969
+
970
+ static void
971
+ free_threads(st_table* thread_table)
972
+ {
973
+ st_foreach(thread_table, free_thread_data, 0);
974
+ }
975
+
976
+ static int
977
+ collect_threads(st_data_t key, st_data_t value, st_data_t result)
978
+ {
979
+ /* Although threads are keyed on an id, that is actually a
980
+ pointer to the VALUE object of the thread. So its bogus.
981
+ However, in thread_data is the real thread id stored
982
+ as an int. */
983
+ thread_data_t* thread_data = (thread_data_t*) value;
984
+ VALUE threads_hash = (VALUE) result;
985
+ VALUE minfo_hash = rb_hash_new();
986
+ st_foreach(thread_data->minfo_table, collect_methods, minfo_hash);
987
+ rb_hash_aset(threads_hash, INT2NUM(thread_data->thread_id), minfo_hash);
988
+
989
+ return ST_CONTINUE;
990
+ }
991
+
992
+ static void
993
+ update_result(prof_method_t * parent, prof_method_t *child,
994
+ prof_clock_t total_time, prof_clock_t self_time)
995
+ {
996
+ /* Update child information on parent (ie, the method that
997
+ called the current method) */
998
+ prof_call_info_t *parent_call_info = child_table_lookup(parent->children, child->key);
999
+ if (parent_call_info == NULL)
1000
+ {
1001
+ parent_call_info = call_info_create(child->klass, child->mid);
1002
+ child_table_insert(parent->children, child->key, parent_call_info);
1003
+ }
1004
+
1005
+ parent_call_info->called++;
1006
+ parent_call_info->total_time += total_time;
1007
+ parent_call_info->self_time += self_time;
1008
+
1009
+ /* Slight hack here - if the child is the top level method then we want
1010
+ to update its total time */
1011
+ if (parent->key == toplevel_key)
1012
+ parent->total_time += total_time;
1013
+
1014
+ /* Update information about the child (ie, the current method) */
1015
+ child->called++;
1016
+ child->total_time += total_time;
1017
+ child->self_time += self_time;
1018
+
1019
+ /* Store pointer to parent */
1020
+ if (minfo_table_lookup(child->parents, parent->key) == NULL)
1021
+ minfo_table_insert(child->parents, parent->key, parent);
1022
+ }
1023
+
1024
+ static void
1025
+ prof_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
1026
+ {
1027
+ static int profiling = 0;
1028
+ VALUE thread;
1029
+ thread_data_t* thread_data;
1030
+ prof_data_t *data;
1031
+
1032
+ if (profiling) return;
1033
+
1034
+ /* Special case - skip any methods from the mProf
1035
+ module, such as Prof.stop, since they clutter
1036
+ the results but are not important to the results. */
1037
+ if (self == mProf) return;
1038
+
1039
+ /* Set flag showing we have started profiling */
1040
+ profiling++;
1041
+
1042
+ /* Is this an include for a module? If so get the actual
1043
+ module class since we want to combine all profiling
1044
+ results for that module. */
1045
+ if (BUILTIN_TYPE(klass) == T_ICLASS)
1046
+ klass = RBASIC(klass)->klass;
1047
+
1048
+ /* // Debug Code
1049
+ {
1050
+ VALUE class_name = rb_String(klass);
1051
+ char* c_class_name = StringValuePtr(class_name);
1052
+ char* c_method_name = rb_id2name(mid);
1053
+ VALUE generated_name = method_name(klass, mid);
1054
+ char* c_generated_name = StringValuePtr(generated_name);
1055
+ printf("Event: %2d, Method: %s#%s\n", event, c_class_name, c_method_name);
1056
+ }*/
1057
+
1058
+ thread = rb_thread_current();
1059
+ thread_data = threads_table_lookup(threads_tbl, thread);
1060
+
1061
+ switch (event) {
1062
+ case RUBY_EVENT_CALL:
1063
+ case RUBY_EVENT_C_CALL:
1064
+ {
1065
+ st_data_t key = method_key(klass, mid);
1066
+ prof_method_t *child = minfo_table_lookup(thread_data->minfo_table, key);
1067
+
1068
+ if (child == NULL) {
1069
+ child = prof_method_create(klass, mid, thread);
1070
+ minfo_table_insert(thread_data->minfo_table, key, child);
1071
+ }
1072
+
1073
+ /* Increment count of number of times this child has been called on
1074
+ the current stack. */
1075
+ child->stack_count++;
1076
+
1077
+ /* Push the data for this method onto our stack */
1078
+ data = stack_push(thread_data->stack);
1079
+ data->method_info = child;
1080
+ data->start_time = get_clock();
1081
+ data->child_cost = 0;
1082
+
1083
+ break;
1084
+ }
1085
+ case RUBY_EVENT_RETURN:
1086
+ case RUBY_EVENT_C_RETURN:
1087
+ {
1088
+ prof_data_t* caller;
1089
+ prof_method_t *parent;
1090
+ prof_method_t *child;
1091
+ prof_clock_t now = get_clock();
1092
+ prof_clock_t total_time, self_time;
1093
+
1094
+ /* Pop data for this method off the stack. */
1095
+ data = stack_pop(thread_data->stack);
1096
+
1097
+ if (data == NULL)
1098
+ {
1099
+ /* Can happen on exceptions. The stack gets unwound without RubyProf.stop
1100
+ being called. */
1101
+ VALUE name = method_name(klass, mid);
1102
+ VALUE message = rb_str_new2("ruby-prof: An error occured when leaving the method %s.\n");
1103
+ rb_str_cat2(message, " Perhaps an exception occured in the code being profiled?\n" );
1104
+
1105
+ rb_warn(StringValuePtr(message), StringValuePtr(name));
1106
+
1107
+ return;
1108
+ }
1109
+
1110
+ /* Update timing information. */
1111
+ total_time = now - data->start_time;
1112
+ self_time = total_time - data->child_cost;
1113
+
1114
+ /* Okay, get the method that called this method (ie, parent) */
1115
+ caller = stack_peek(thread_data->stack);
1116
+
1117
+ if (caller == NULL)
1118
+ {
1119
+ /* We are at the top of the stack, so grab the toplevel method */
1120
+ parent = minfo_table_lookup(thread_data->minfo_table, toplevel_key);
1121
+ }
1122
+ else
1123
+ {
1124
+ caller->child_cost += total_time;
1125
+ parent = caller->method_info;
1126
+ }
1127
+
1128
+ /* Decrement count of number of times this child has been called on
1129
+ the current stack. */
1130
+ child = data->method_info;
1131
+ child->stack_count--;
1132
+
1133
+ /* If the stack count is greater than zero, then this
1134
+ method has been called recursively. In that case set the total
1135
+ time to zero because it will be correctly set when we unwind
1136
+ the stack up. If we don't do this, then the total time for the
1137
+ method will be double counted per recursive call. */
1138
+ if (child->stack_count != 0)
1139
+ total_time = 0;
1140
+
1141
+ update_result(parent, child, total_time, self_time);
1142
+ break;
1143
+ }
1144
+ }
1145
+ profiling--;
1146
+ }
1147
+
1148
+
1149
+ /* ======== ProfResult ============== */
1150
+
1151
+ /* Document-class: RubyProf::Result
1152
+ The RubyProf::Result class is used to store the results of a
1153
+ profiling run. And instace of the class is returned from
1154
+ the methods RubyProf#stop and RubyProf#profile.
1155
+
1156
+ RubyProf::Result has one field, called threads, which is a hash
1157
+ table keyed on thread ID. For each thread id, the hash table
1158
+ stores another hash table that contains profiling information
1159
+ for each method called during the threads execution. That
1160
+ hash table is keyed on method name and contains
1161
+ RubyProf::MethodInfo objects. */
1162
+
1163
+
1164
+ static void
1165
+ prof_result_mark(prof_result_t *prof_result)
1166
+ {
1167
+ VALUE threads = prof_result->threads;
1168
+ rb_gc_mark(threads);
1169
+ }
1170
+
1171
+ static void
1172
+ prof_result_free(prof_result_t *prof_result)
1173
+ {
1174
+ prof_result->threads = Qnil;
1175
+ xfree(prof_result);
1176
+ }
1177
+
1178
+ static VALUE
1179
+ prof_result_new()
1180
+ {
1181
+ prof_result_t *prof_result = ALLOC(prof_result_t);
1182
+
1183
+ /* Wrap threads in Ruby regular Ruby hash table. */
1184
+ prof_result->threads = rb_hash_new();
1185
+ st_foreach(threads_tbl, collect_threads, prof_result->threads);
1186
+
1187
+ return Data_Wrap_Struct(cResult, prof_result_mark, prof_result_free, prof_result);
1188
+ }
1189
+
1190
+
1191
+ static prof_result_t *
1192
+ get_prof_result(VALUE obj)
1193
+ {
1194
+ if (TYPE(obj) != T_DATA ||
1195
+ RDATA(obj)->dfree != (RUBY_DATA_FUNC) prof_result_free)
1196
+ {
1197
+ /* Should never happen */
1198
+ rb_raise(rb_eTypeError, "wrong result object");
1199
+ }
1200
+ return (prof_result_t *) DATA_PTR(obj);
1201
+ }
1202
+
1203
+ /* call-seq:
1204
+ threads -> Hash
1205
+
1206
+ Returns a hash table keyed on thread ID. For each thread id,
1207
+ the hash table stores another hash table that contains profiling
1208
+ information for each method called during the threads execution.
1209
+ That hash table is keyed on method name and contains
1210
+ RubyProf::MethodInfo objects. */
1211
+ static VALUE
1212
+ prof_result_threads(VALUE self)
1213
+ {
1214
+ prof_result_t *prof_result = get_prof_result(self);
1215
+ return prof_result->threads;
1216
+ }
1217
+
1218
+
1219
+ /* call-seq:
1220
+ thread_id = int
1221
+ toplevel(thread_id) -> RubyProf::MethodInfo
1222
+
1223
+ Returns the RubyProf::MethodInfo object that represents the root
1224
+ calling method for this thread. This method will always
1225
+ be named #toplevel and contains the total amount of time spent
1226
+ executing code in this thread. */
1227
+ static VALUE
1228
+ prof_result_toplevel(VALUE self, VALUE thread_id)
1229
+ {
1230
+ prof_result_t *prof_result = get_prof_result(self);
1231
+ VALUE methods = rb_hash_aref(prof_result->threads, thread_id);
1232
+ VALUE key = method_name(Qnil, toplevel_id);
1233
+ VALUE result = rb_hash_aref(methods, key);
1234
+
1235
+ if (result == Qnil)
1236
+ {
1237
+ /* Should never happen */
1238
+ rb_raise(rb_eRuntimeError, "Could not find toplevel method information");
1239
+ }
1240
+ return result;
1241
+ }
1242
+
1243
+
1244
+ /* call-seq:
1245
+ clock_mode -> clock_mode
1246
+
1247
+ Returns the current clock mode. Valid values include:
1248
+ *RubyProf::PROCESS_TIME - Measure process time. This is default. It is implemented using the clock function in the C Runtime library.
1249
+ *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
1250
+ *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms. */
1251
+ static VALUE
1252
+ prof_get_clock_mode(VALUE self)
1253
+ {
1254
+ return INT2NUM(clock_mode);
1255
+ }
1256
+
1257
+ /* call-seq:
1258
+ clock_mode=value -> void
1259
+
1260
+ Specifies the method ruby-prof uses to measure time. Valid values include:
1261
+ *RubyProf::PROCESS_TIME - Measure process time. This is default. It is implemented using the clock function in the C Runtime library.
1262
+ *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
1263
+ *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms. */
1264
+ static VALUE
1265
+ prof_set_clock_mode(VALUE self, VALUE val)
1266
+ {
1267
+ int mode = NUM2INT(val);
1268
+
1269
+ if (threads_tbl)
1270
+ {
1271
+ rb_raise(rb_eRuntimeError, "can't set clock_mode while profiling");
1272
+ }
1273
+
1274
+ switch (mode) {
1275
+ case CLOCK_MODE_PROCESS:
1276
+ get_clock = clock_get_clock;
1277
+ clock2sec = clock_clock2sec;
1278
+ break;
1279
+ case CLOCK_MODE_WALL:
1280
+ get_clock = gettimeofday_get_clock;
1281
+ clock2sec = gettimeofday_clock2sec;
1282
+ break;
1283
+ #ifdef CLOCK_MODE_CPU
1284
+ case CLOCK_MODE_CPU:
1285
+ if (cpu_frequency == 0)
1286
+ cpu_frequency = get_cpu_frequency();
1287
+ get_clock = cpu_get_clock;
1288
+ clock2sec = cpu_clock2sec;
1289
+ break;
1290
+ #endif
1291
+ default:
1292
+ rb_raise(rb_eArgError, "invalid mode: %d", mode);
1293
+ break;
1294
+ }
1295
+ clock_mode = mode;
1296
+ return val;
1297
+ }
1298
+
1299
+ /* ========= Profiling ============= */
1300
+
1301
+
1302
+ /* call-seq:
1303
+ start -> void
1304
+
1305
+ Starts recording profile data.*/
1306
+ static VALUE
1307
+ prof_start(VALUE self)
1308
+ {
1309
+ toplevel_id = rb_intern("toplevel");
1310
+ toplevel_key = method_key(Qnil, toplevel_id);
1311
+
1312
+ if (threads_tbl != NULL)
1313
+ {
1314
+ rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
1315
+ }
1316
+
1317
+ /* Setup globals */
1318
+ class_tbl = rb_hash_new();
1319
+ threads_tbl = threads_table_create();
1320
+
1321
+ rb_add_event_hook(prof_event_hook,
1322
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1323
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN);
1324
+
1325
+ return Qnil;
1326
+ }
1327
+
1328
+
1329
+ /* call-seq:
1330
+ stop -> RubyProf::Result
1331
+
1332
+ Stops collecting profile data and returns a RubyProf::Result object. */
1333
+ static VALUE
1334
+ prof_stop(VALUE self)
1335
+ {
1336
+ VALUE result = Qnil;
1337
+
1338
+ if (threads_tbl == NULL)
1339
+ {
1340
+ rb_raise(rb_eRuntimeError, "RubyProf.start is not called yet");
1341
+ }
1342
+
1343
+ /* Now unregister from event */
1344
+ rb_remove_event_hook(prof_event_hook);
1345
+
1346
+ /* Create the result */
1347
+ result = prof_result_new();
1348
+
1349
+ /* Free threads table */
1350
+ free_threads(threads_tbl);
1351
+ threads_table_free(threads_tbl);
1352
+ threads_tbl = NULL;
1353
+
1354
+ /* Free reference to class_tbl */
1355
+ class_tbl = Qnil;
1356
+
1357
+ return result;
1358
+ }
1359
+
1360
+
1361
+ /* call-seq:
1362
+ profile {block} -> RubyProf::Result
1363
+
1364
+ Profiles the specified block and returns a RubyProf::Result object. */
1365
+ static VALUE
1366
+ prof_profile(VALUE self)
1367
+ {
1368
+ if (!rb_block_given_p())
1369
+ {
1370
+ rb_raise(rb_eArgError, "A block must be provided to the profile method.");
1371
+ }
1372
+
1373
+ prof_start(self);
1374
+ rb_yield(Qnil);
1375
+ return prof_stop(self);
1376
+ }
1377
+
1378
+
1379
+ #if defined(_WIN32)
1380
+ __declspec(dllexport)
1381
+ #endif
1382
+ void
1383
+
1384
+ Init_ruby_prof()
1385
+ {
1386
+ mProf = rb_define_module("RubyProf");
1387
+ rb_define_const(mProf, "VERSION", rb_str_new2(PROF_VERSION));
1388
+ rb_define_module_function(mProf, "start", prof_start, 0);
1389
+ rb_define_module_function(mProf, "stop", prof_stop, 0);
1390
+ rb_define_module_function(mProf, "profile", prof_profile, 0);
1391
+ rb_define_singleton_method(mProf, "clock_mode", prof_get_clock_mode, 0);
1392
+ rb_define_singleton_method(mProf, "clock_mode=", prof_set_clock_mode, 1);
1393
+ rb_define_const(mProf, "PROCESS_TIME", INT2NUM(CLOCK_MODE_PROCESS));
1394
+ rb_define_const(mProf, "WALL_TIME", INT2NUM(CLOCK_MODE_WALL));
1395
+ #ifdef CLOCK_MODE_CPU
1396
+ rb_define_const(mProf, "CPU_TIME", INT2NUM(CLOCK_MODE_CPU));
1397
+ rb_define_singleton_method(mProf, "cpu_frequency",
1398
+ prof_get_cpu_frequency, 0);
1399
+ rb_define_singleton_method(mProf, "cpu_frequency=",
1400
+ prof_set_cpu_freqeuncy, 1);
1401
+ #endif
1402
+
1403
+ cResult = rb_define_class_under(mProf, "Result", rb_cObject);
1404
+ rb_undef_method(CLASS_OF(cMethodInfo), "new");
1405
+ rb_define_method(cResult, "threads", prof_result_threads, 0);
1406
+ rb_define_method(cResult, "toplevel", prof_result_toplevel, 1);
1407
+
1408
+ cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
1409
+ rb_include_module(cMethodInfo, rb_mComparable);
1410
+ rb_undef_method(CLASS_OF(cMethodInfo), "new");
1411
+ rb_define_method(cMethodInfo, "called", prof_method_called, 0);
1412
+ rb_define_method(cMethodInfo, "total_time", prof_method_total_time, 0);
1413
+ rb_define_method(cMethodInfo, "self_time", prof_method_self_time, 0);
1414
+ rb_define_method(cMethodInfo, "children_time", prof_method_children_time, 0);
1415
+ rb_define_method(cMethodInfo, "name", prof_method_name, 0);
1416
+ rb_define_method(cMethodInfo, "method_class", prof_method_class, 0);
1417
+ rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
1418
+ rb_define_method(cMethodInfo, "thread_id", prof_thread_id, 0);
1419
+ rb_define_method(cMethodInfo, "parents", prof_method_parents, 0);
1420
+ rb_define_method(cMethodInfo, "children", prof_method_children, 0);
1421
+ rb_define_method(cMethodInfo, "<=>", prof_method_cmp, 1);
1422
+
1423
+ cCallInfo = rb_define_class_under(mProf, "CallInfo", rb_cObject);
1424
+ rb_undef_method(CLASS_OF(cCallInfo), "new");
1425
+ rb_define_method(cCallInfo, "called", call_info_called, 0);
1426
+ rb_define_method(cCallInfo, "total_time", call_info_total_time, 0);
1427
+ rb_define_method(cCallInfo, "self_time", call_info_self_time, 0);
1428
+ rb_define_method(cCallInfo, "children_time", call_info_children_time, 0);
1429
+
1430
+ rb_global_variable(&class_tbl);
1431
+ }
1432
+
1433
+ /* vim: set filetype=c ts=8 sw=4 noexpandtab : */