adamh-ruby-prof 0.7.3

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