ruby-prof 0.4.0-mswin32

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 (57) 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/ruby_prof.so +0 -0
  44. data/lib/unprof.rb +8 -0
  45. data/test/basic_test.rb +141 -0
  46. data/test/clock_mode_test.rb +73 -0
  47. data/test/module_test.rb +45 -0
  48. data/test/prime.rb +58 -0
  49. data/test/prime_test.rb +24 -0
  50. data/test/printers_test.rb +28 -0
  51. data/test/recursive_test.rb +55 -0
  52. data/test/test.rb +3 -0
  53. data/test/test_helper.rb +45 -0
  54. data/test/test_suite.rb +9 -0
  55. data/test/thread_test.rb +32 -0
  56. data/test/timing_test.rb +90 -0
  57. metadata +122 -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 : */