adamh-ruby-prof 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ }