acunote-ruby-prof 0.9.2

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