ruby-prof-danielhoey 0.8.1

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