acunote-ruby-prof 0.9.2

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