ruby-prof 0.8.1-x86-mingw32

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