ruby-prof 0.6.0-x86-mswin32-60 → 0.7.0-x86-mswin32-60

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 (64) hide show
  1. data/CHANGES +54 -1
  2. data/README +134 -7
  3. data/Rakefile +40 -58
  4. data/bin/ruby-prof +21 -6
  5. data/ext/extconf.rb +13 -0
  6. data/ext/measure_allocations.h +16 -1
  7. data/ext/measure_cpu_time.h +15 -3
  8. data/ext/measure_gc_runs.h +76 -0
  9. data/ext/measure_gc_time.h +57 -0
  10. data/ext/measure_memory.h +61 -2
  11. data/ext/measure_process_time.h +13 -2
  12. data/ext/measure_wall_time.h +12 -1
  13. data/ext/mingw/Rakefile +23 -0
  14. data/ext/mingw/build.rake +38 -0
  15. data/ext/mingw/ruby_prof.so +0 -0
  16. data/ext/ruby_prof.c +685 -633
  17. data/ext/ruby_prof.h +188 -0
  18. data/ext/vc/ruby_prof.sln +20 -0
  19. data/ext/vc/ruby_prof.vcproj +241 -0
  20. data/ext/version.h +4 -0
  21. data/lib/ruby-prof.rb +4 -0
  22. data/lib/ruby-prof/call_info.rb +47 -0
  23. data/lib/ruby-prof/call_tree_printer.rb +9 -1
  24. data/lib/ruby-prof/graph_html_printer.rb +6 -5
  25. data/lib/ruby-prof/graph_printer.rb +3 -2
  26. data/lib/ruby-prof/method_info.rb +85 -0
  27. data/lib/ruby-prof/task.rb +1 -2
  28. data/lib/ruby-prof/test.rb +148 -0
  29. data/rails/environment/profile.rb +24 -0
  30. data/rails/example/example_test.rb +9 -0
  31. data/rails/profile_test_helper.rb +21 -0
  32. data/test/basic_test.rb +173 -80
  33. data/test/duplicate_names_test.rb +2 -3
  34. data/test/exceptions_test.rb +15 -0
  35. data/test/exclude_threads_test.rb +54 -0
  36. data/test/line_number_test.rb +18 -14
  37. data/test/measurement_test.rb +121 -0
  38. data/test/module_test.rb +5 -8
  39. data/test/no_method_class_test.rb +4 -5
  40. data/test/prime.rb +3 -5
  41. data/test/prime_test.rb +1 -12
  42. data/test/printers_test.rb +10 -13
  43. data/test/profile_unit_test.rb +10 -12
  44. data/test/recursive_test.rb +202 -92
  45. data/test/singleton_test.rb +1 -2
  46. data/test/stack_test.rb +138 -0
  47. data/test/start_stop_test.rb +95 -0
  48. data/test/test_suite.rb +7 -3
  49. data/test/thread_test.rb +111 -87
  50. data/test/unique_call_path_test.rb +206 -0
  51. metadata +42 -44
  52. data/ext/extconf.rb.rej +0 -13
  53. data/lib/ruby-prof/call_tree_printer.rb.rej +0 -27
  54. data/lib/ruby-prof/profile_test_case.rb +0 -80
  55. data/lib/ruby_prof.so +0 -0
  56. data/rails_plugin/ruby-prof/init.rb +0 -8
  57. data/rails_plugin/ruby-prof/lib/profiling.rb +0 -57
  58. data/test/measure_mode_test.rb +0 -79
  59. data/test/prime1.rb +0 -17
  60. data/test/prime2.rb +0 -26
  61. data/test/prime3.rb +0 -17
  62. data/test/start_test.rb +0 -24
  63. data/test/test_helper.rb +0 -55
  64. data/test/timing_test.rb +0 -133
@@ -16,6 +16,19 @@ else
16
16
  end
17
17
 
18
18
  have_header("sys/times.h")
19
+
20
+ # Stefan Kaes / Alexander Dymo GC patch
19
21
  have_func("rb_os_allocated_objects")
20
22
  have_func("rb_gc_allocated_size")
23
+ have_func("rb_gc_collections")
24
+ have_func("rb_gc_time")
25
+
26
+ # Lloyd Hilaiel's heap info patch
27
+ have_func("rb_heap_total_mem")
28
+ have_func("rb_gc_heap_info")
29
+
30
+ # Ruby 1.9 unexposed methods
31
+ have_func("rb_gc_malloc_allocations")
32
+ have_func("rb_gc_malloc_allocated_size")
33
+
21
34
  create_makefile("ruby_prof")
@@ -1,5 +1,5 @@
1
1
  /* :nodoc:
2
- * Copyright (C) 2007 Shugo Maeda <shugo@ruby-lang.org>
2
+ * Copyright (C) 2008 Shugo Maeda <shugo@ruby-lang.org>
3
3
  * Charlie Savage <cfis@savagexi.com>
4
4
  * All rights reserved.
5
5
  *
@@ -24,6 +24,7 @@
24
24
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
25
  * SUCH DAMAGE. */
26
26
 
27
+ #include <ruby.h>
27
28
 
28
29
  #if defined(HAVE_RB_OS_ALLOCATED_OBJECTS)
29
30
  #define MEASURE_ALLOCATIONS 3
@@ -40,4 +41,18 @@ convert_allocations(prof_measure_t c)
40
41
  return c;
41
42
  }
42
43
 
44
+ /* Document-method: prof_measure_allocations
45
+ call-seq:
46
+ measure_allocations -> int
47
+
48
+ Returns the total number of object allocations since Ruby started.*/
49
+ static VALUE
50
+ prof_measure_allocations(VALUE self)
51
+ {
52
+ #if defined(HAVE_LONG_LONG)
53
+ return ULL2NUM(rb_os_allocated_objects());
54
+ #else
55
+ return ULONG2NUM(rb_os_allocated_objects());
56
+ #endif
57
+ }
43
58
  #endif
@@ -1,5 +1,5 @@
1
1
  /* :nodoc:
2
- * Copyright (C) 2007 Shugo Maeda <shugo@ruby-lang.org>
2
+ * Copyright (C) 2008 Shugo Maeda <shugo@ruby-lang.org>
3
3
  * Charlie Savage <cfis@savagexi.com>
4
4
  * All rights reserved.
5
5
  *
@@ -24,6 +24,7 @@
24
24
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
25
  * SUCH DAMAGE. */
26
26
 
27
+ #include <ruby.h>
27
28
 
28
29
  #if defined(_WIN32) || (defined(__GNUC__) && (defined(__i386__) || defined(__powerpc__) || defined(__ppc__)))
29
30
  #define MEASURE_CPU_TIME 2
@@ -110,6 +111,17 @@ convert_cpu_time(prof_measure_t c)
110
111
  return (double) c / cpu_frequency;
111
112
  }
112
113
 
114
+ /* Document-method: prof_measure_cpu_time
115
+ call-seq:
116
+ measure_cpu_time -> float
117
+
118
+ Returns the cpu time.*/
119
+ static VALUE
120
+ prof_measure_cpu_time(VALUE self)
121
+ {
122
+ return rb_float_new(convert_cpu_time(measure_cpu_time()));
123
+ }
124
+
113
125
  /* Document-method: prof_get_cpu_frequency
114
126
  call-seq:
115
127
  cpu_frequency -> int
@@ -119,7 +131,7 @@ RubyProf::measure_mode is set to CPU_TIME. */
119
131
  static VALUE
120
132
  prof_get_cpu_frequency(VALUE self)
121
133
  {
122
- return LONG2NUM(cpu_frequency);
134
+ return ULL2NUM(cpu_frequency);
123
135
  }
124
136
 
125
137
  /* Document-method: prof_set_cpu_frequency
@@ -131,7 +143,7 @@ RubyProf::measure_mode is set to CPU_TIME. */
131
143
  static VALUE
132
144
  prof_set_cpu_frequency(VALUE self, VALUE val)
133
145
  {
134
- cpu_frequency = NUM2LONG(val);
146
+ cpu_frequency = NUM2LL(val);
135
147
  return val;
136
148
  }
137
149
 
@@ -0,0 +1,76 @@
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_COLLECTIONS)
28
+ #define MEASURE_GC_RUNS 5
29
+
30
+ static prof_measure_t
31
+ measure_gc_runs()
32
+ {
33
+ return NUM2INT(rb_gc_collections());
34
+ }
35
+
36
+ static double
37
+ convert_gc_runs(prof_measure_t c)
38
+ {
39
+ return c;
40
+ }
41
+
42
+ /* Document-method: prof_measure_gc_runs
43
+ call-seq:
44
+ gc_runs -> Integer
45
+
46
+ Returns the total number of garbage collections.*/
47
+ static VALUE
48
+ prof_measure_gc_runs(VALUE self)
49
+ {
50
+ return rb_gc_collections();
51
+ }
52
+
53
+ #elif defined(HAVE_RB_GC_HEAP_INFO)
54
+ #define MEASURE_GC_RUNS 5
55
+
56
+ static prof_measure_t
57
+ measure_gc_runs()
58
+ {
59
+ VALUE h = rb_gc_heap_info();
60
+ return NUM2UINT(rb_hash_aref(h, rb_str_new2("num_gc_passes")));
61
+ }
62
+
63
+ static double
64
+ convert_gc_runs(prof_measure_t c)
65
+ {
66
+ return c;
67
+ }
68
+
69
+ static VALUE
70
+ prof_measure_gc_runs(VALUE self)
71
+ {
72
+ VALUE h = rb_gc_heap_info();
73
+ return rb_hash_aref(h, rb_str_new2("num_gc_passes"));
74
+ }
75
+
76
+ #endif
@@ -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
@@ -1,5 +1,6 @@
1
1
  /* :nodoc:
2
2
  * Copyright (C) 2008 Alexander Dymo <adymo@pluron.com>
3
+ *
3
4
  * All rights reserved.
4
5
  *
5
6
  * Redistribution and use in source and binary forms, with or without
@@ -26,17 +27,75 @@
26
27
 
27
28
  #if defined(HAVE_RB_GC_ALLOCATED_SIZE)
28
29
  #define MEASURE_MEMORY 4
30
+ #define TOGGLE_GC_STATS 1
29
31
 
30
32
  static prof_measure_t
31
33
  measure_memory()
32
34
  {
33
- return rb_gc_allocated_size();
35
+ #if defined(HAVE_LONG_LONG)
36
+ return NUM2LL(rb_gc_allocated_size());
37
+ #else
38
+ return NUM2ULONG(rb_gc_allocated_size());
39
+ #endif
34
40
  }
35
41
 
36
42
  static double
37
43
  convert_memory(prof_measure_t c)
38
44
  {
39
- return c / 1024;
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());
40
99
  }
41
100
 
42
101
  #endif
@@ -1,5 +1,5 @@
1
- /* :nodoc:
2
- * Copyright (C) 2007 Shugo Maeda <shugo@ruby-lang.org>
1
+ /*
2
+ * Copyright (C) 2008 Shugo Maeda <shugo@ruby-lang.org>
3
3
  * Charlie Savage <cfis@savagexi.com>
4
4
  * All rights reserved.
5
5
  *
@@ -39,3 +39,14 @@ convert_process_time(prof_measure_t c)
39
39
  {
40
40
  return (double) c / CLOCKS_PER_SEC;
41
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
+ }
@@ -1,5 +1,5 @@
1
1
  /* :nodoc:
2
- * Copyright (C) 2007 Shugo Maeda <shugo@ruby-lang.org>
2
+ * Copyright (C) 2008 Shugo Maeda <shugo@ruby-lang.org>
3
3
  * Charlie Savage <cfis@savagexi.com>
4
4
  * All rights reserved.
5
5
  *
@@ -40,3 +40,14 @@ convert_wall_time(prof_measure_t c)
40
40
  {
41
41
  return (double) c / 1000000;
42
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
Binary file
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2007 Shugo Maeda <shugo@ruby-lang.org>
2
+ * Copyright (C) 2008 Shugo Maeda <shugo@ruby-lang.org>
3
3
  * Charlie Savage <cfis@savagexi.com>
4
4
  * All rights reserved.
5
5
  *
@@ -47,129 +47,10 @@
47
47
  hierarchy in ruby - which is very helpful for creating call graphs.
48
48
  */
49
49
 
50
-
51
- #include <stdio.h>
52
-
53
- #include <ruby.h>
54
- #ifndef RUBY_VM
55
- #include <node.h>
56
- #include <st.h>
57
- typedef rb_event_t rb_event_flag_t;
58
- #define rb_sourcefile() (node ? node->nd_file : 0)
59
- #define rb_sourceline() (node ? nd_line(node) : 0)
60
- #endif
61
-
62
-
63
- /* ================ Constants =================*/
64
- #define INITIAL_STACK_SIZE 8
65
- #define PROF_VERSION "0.6.0"
66
-
67
-
68
- /* ================ Measurement =================*/
69
- #ifdef HAVE_LONG_LONG
70
- typedef LONG_LONG prof_measure_t;
71
- #else
72
- typedef unsigned long prof_measure_t;
73
- #endif
74
-
75
- #include "measure_process_time.h"
76
- #include "measure_wall_time.h"
77
- #include "measure_cpu_time.h"
78
- #include "measure_allocations.h"
79
- #include "measure_memory.h"
80
-
81
- static prof_measure_t (*get_measurement)() = measure_process_time;
82
- static double (*convert_measurement)(prof_measure_t) = convert_process_time;
83
-
84
- /* ================ DataTypes =================*/
85
- static VALUE mProf;
86
- static VALUE cResult;
87
- static VALUE cMethodInfo;
88
- static VALUE cCallInfo;
89
-
90
- /* Profiling information for each method. */
91
- typedef struct prof_method_t {
92
- st_data_t key; /* Cache hash value for speed reasons. */
93
- VALUE name; /* Name of the method. */
94
- VALUE klass; /* The method's class. */
95
- ID mid; /* The method id. */
96
- int depth; /* The recursive depth this method was called at.*/
97
- int called; /* Number of times called */
98
- const char* source_file; /* The method's source file */
99
- int line; /* The method's line number. */
100
- prof_measure_t total_time; /* Total time spent in this method and children. */
101
- prof_measure_t self_time; /* Total time spent in this method. */
102
- prof_measure_t wait_time; /* Total time this method spent waiting for other threads. */
103
- st_table *parents; /* The method's callers (prof_call_info_t). */
104
- st_table *children; /* The method's callees (prof_call_info_t). */
105
- int active_frame; /* # of active frames for this method. Used to detect
106
- recursion. Stashed here to avoid extra lookups in
107
- the hook method - so a bit hackey. */
108
- struct prof_method_t *base; /* For recursion - this is the parent method */
109
- } prof_method_t;
110
-
111
-
112
- /* Callers and callee information for a method. */
113
- typedef struct {
114
- prof_method_t *target;
115
- int called;
116
- prof_measure_t total_time;
117
- prof_measure_t self_time;
118
- prof_measure_t wait_time;
119
- int line;
120
- } prof_call_info_t;
121
-
122
-
123
- /* Temporary object that maintains profiling information
124
- for active methods - there is one per method.*/
125
- typedef struct {
126
- /* Caching prof_method_t values significantly
127
- increases performance. */
128
- prof_method_t *method;
129
- prof_measure_t start_time;
130
- prof_measure_t wait_time;
131
- prof_measure_t child_time;
132
- unsigned int line;
133
- } prof_frame_t;
134
-
135
- /* Current stack of active methods.*/
136
- typedef struct {
137
- prof_frame_t *start;
138
- prof_frame_t *end;
139
- prof_frame_t *ptr;
140
- } prof_stack_t;
141
-
142
- /* Profiling information for a thread. */
143
- typedef struct {
144
- unsigned long thread_id; /* Thread id */
145
- st_table* method_info_table; /* All called methods */
146
- prof_stack_t* stack; /* Active methods */
147
- prof_measure_t last_switch; /* Point of last context switch */
148
- } thread_data_t;
149
-
150
- typedef struct {
151
- VALUE threads;
152
- } prof_result_t;
153
-
154
-
155
- /* ================ Variables =================*/
156
- static int measure_mode;
157
- static st_table *threads_tbl = NULL;
158
- /* TODO - If Ruby become multi-threaded this has to turn into
159
- a separate stack since this isn't thread safe! */
160
- static thread_data_t* last_thread_data = NULL;
50
+ #include "ruby_prof.h"
161
51
 
162
52
 
163
53
  /* ================ Helper Functions =================*/
164
- /* Helper method to get the id of a Ruby thread. */
165
- static inline long
166
- get_thread_id(VALUE thread)
167
- {
168
- //return NUM2ULONG(rb_obj_id(thread));
169
- // From line 1997 in gc.c
170
- return (long)thread;
171
- }
172
-
173
54
  static VALUE
174
55
  figure_singleton_name(VALUE klass)
175
56
  {
@@ -285,21 +166,7 @@ full_name(VALUE klass, ID mid, int depth)
285
166
  return result;
286
167
  }
287
168
 
288
-
289
- static inline st_data_t
290
- method_key(VALUE klass, ID mid, int depth)
291
- {
292
- /* No idea if this is a unique key or not. Would be
293
- best to use the method name, but we can't, since
294
- that calls internal ruby functions which would
295
- cause the hook method to recursively call itself.
296
- And that is too much of a bother to deal with.
297
- Plus of course, this is faster. */
298
- return (klass * 100) + (mid * 10) + depth;
299
- }
300
-
301
169
  /* ================ Stack Handling =================*/
302
-
303
170
  /* Creates a stack of prof_frame_t to keep track
304
171
  of timings for active methods. */
305
172
  static prof_stack_t *
@@ -319,7 +186,7 @@ stack_free(prof_stack_t *stack)
319
186
  xfree(stack);
320
187
  }
321
188
 
322
- static inline prof_frame_t *
189
+ static prof_frame_t *
323
190
  stack_push(prof_stack_t *stack)
324
191
  {
325
192
  /* Is there space on the stack? If not, double
@@ -335,7 +202,7 @@ stack_push(prof_stack_t *stack)
335
202
  return stack->ptr++;
336
203
  }
337
204
 
338
- static inline prof_frame_t *
205
+ static prof_frame_t *
339
206
  stack_pop(prof_stack_t *stack)
340
207
  {
341
208
  if (stack->ptr == stack->start)
@@ -344,7 +211,7 @@ stack_pop(prof_stack_t *stack)
344
211
  return --stack->ptr;
345
212
  }
346
213
 
347
- static inline prof_frame_t *
214
+ static prof_frame_t *
348
215
  stack_peek(prof_stack_t *stack)
349
216
  {
350
217
  if (stack->ptr == stack->start)
@@ -353,71 +220,54 @@ stack_peek(prof_stack_t *stack)
353
220
  return stack->ptr - 1;
354
221
  }
355
222
 
356
- static inline size_t
357
- stack_size(prof_stack_t *stack)
223
+ /* ================ Method Key =================*/
224
+ static int
225
+ method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2)
358
226
  {
359
- return stack->ptr - stack->start;
227
+ return (key1->klass != key2->klass) ||
228
+ (key1->mid != key2->mid) ||
229
+ (key1->depth != key2->depth);
360
230
  }
361
231
 
362
- /* ================ Method Info Handling =================*/
363
-
364
- /* --- Keeps track of the methods the current method calls */
365
- static st_table *
366
- method_info_table_create()
232
+ static int
233
+ method_table_hash(prof_method_key_t *key)
367
234
  {
368
- return st_init_numtable();
369
- }
370
-
371
- static inline size_t
372
- method_info_table_insert(st_table *table, st_data_t key, prof_method_t *val)
373
- {
374
- return st_insert(table, key, (st_data_t) val);
375
- }
376
-
377
- static inline prof_method_t *
378
- method_info_table_lookup(st_table *table, st_data_t key)
379
- {
380
- st_data_t val;
381
- if (st_lookup(table, key, &val))
382
- {
383
- return (prof_method_t *) val;
384
- }
385
- else
386
- {
387
- return NULL;
388
- }
235
+ return key->key;
389
236
  }
390
237
 
238
+ static struct st_hash_type type_method_hash = {
239
+ method_table_cmp,
240
+ method_table_hash
241
+ };
391
242
 
392
243
  static void
393
- method_info_table_free(st_table *table)
244
+ method_key(prof_method_key_t* key, VALUE klass, ID mid, int depth)
394
245
  {
395
- /* Don't free the contents since they are wrapped by
396
- Ruby objects! */
397
- st_free_table(table);
246
+ key->klass = klass;
247
+ key->mid = mid;
248
+ key->depth = depth;
249
+ key->key = (klass << 4) + (mid << 2) + depth;
398
250
  }
399
251
 
400
252
 
401
- /* ================ Call Info Handling =================*/
402
-
403
- /* ---- Hash, keyed on class/method_id, that holds call_info objects ---- */
253
+ /* ================ Call Info =================*/
404
254
  static st_table *
405
- caller_table_create()
255
+ call_info_table_create()
406
256
  {
407
- return st_init_numtable();
257
+ return st_init_table(&type_method_hash);
408
258
  }
409
259
 
410
- static inline size_t
411
- caller_table_insert(st_table *table, st_data_t key, prof_call_info_t *val)
260
+ static size_t
261
+ call_info_table_insert(st_table *table, const prof_method_key_t *key, prof_call_info_t *val)
412
262
  {
413
- return st_insert(table, key, (st_data_t) val);
263
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
414
264
  }
415
265
 
416
- static inline prof_call_info_t *
417
- caller_table_lookup(st_table *table, st_data_t key)
266
+ static prof_call_info_t *
267
+ call_info_table_lookup(st_table *table, const prof_method_key_t *key)
418
268
  {
419
269
  st_data_t val;
420
- if (st_lookup(table, key, &val))
270
+ if (st_lookup(table, (st_data_t) key, &val))
421
271
  {
422
272
  return (prof_call_info_t *) val;
423
273
  }
@@ -428,7 +278,7 @@ caller_table_lookup(st_table *table, st_data_t key)
428
278
  }
429
279
 
430
280
  static void
431
- caller_table_free(st_table *table)
281
+ call_info_table_free(st_table *table)
432
282
  {
433
283
  st_free_table(table);
434
284
  }
@@ -440,43 +290,51 @@ they took to execute. */
440
290
 
441
291
  /* :nodoc: */
442
292
  static prof_call_info_t *
443
- call_info_create(prof_method_t* method)
293
+ prof_call_info_create(prof_method_t* method, prof_call_info_t* parent)
444
294
  {
445
- prof_call_info_t *result;
446
-
447
- result = ALLOC(prof_call_info_t);
295
+ prof_call_info_t *result = ALLOC(prof_call_info_t);
296
+ result->object = Qnil;
448
297
  result->target = method;
298
+ result->parent = parent;
299
+ result->call_infos = call_info_table_create();
300
+ result->children = Qnil;
301
+
449
302
  result->called = 0;
450
303
  result->total_time = 0;
451
304
  result->self_time = 0;
452
305
  result->wait_time = 0;
306
+ result->line = 0;
453
307
  return result;
454
308
  }
455
309
 
456
310
  static void
457
- call_info_free(prof_call_info_t *call_info)
311
+ prof_call_info_mark(prof_call_info_t *call_info)
458
312
  {
459
- xfree(call_info);
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));
460
317
  }
461
318
 
462
- static int
463
- free_call_infos(st_data_t key, st_data_t value, st_data_t data)
319
+ static void
320
+ prof_call_info_free(prof_call_info_t *call_info)
464
321
  {
465
- prof_call_info_t* call_info = (prof_call_info_t*) value;
466
- call_info_free(call_info);
467
- return ST_CONTINUE;
322
+ call_info_table_free(call_info->call_infos);
323
+ xfree(call_info);
468
324
  }
469
325
 
470
326
  static VALUE
471
- call_info_new(prof_call_info_t *result)
327
+ prof_call_info_wrap(prof_call_info_t *call_info)
472
328
  {
473
- /* We don't want Ruby freeing the underlying C structures, that
474
- is done when the prof_method_t is freed. */
475
- return Data_Wrap_Struct(cCallInfo, NULL, NULL, result);
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;
476
334
  }
477
335
 
478
336
  static prof_call_info_t *
479
- get_call_info_result(VALUE obj)
337
+ prof_get_call_info_result(VALUE obj)
480
338
  {
481
339
  if (BUILTIN_TYPE(obj) != T_DATA)
482
340
  {
@@ -492,14 +350,14 @@ get_call_info_result(VALUE obj)
492
350
 
493
351
  Returns the target method. */
494
352
  static VALUE
495
- call_info_target(VALUE self)
353
+ prof_call_info_target(VALUE self)
496
354
  {
497
355
  /* Target is a pointer to a method_info - so we have to be careful
498
356
  about the GC. We will wrap the method_info but provide no
499
357
  free method so the underlying object is not freed twice! */
500
358
 
501
- prof_call_info_t *result = get_call_info_result(self);
502
- return Data_Wrap_Struct(cMethodInfo, NULL, NULL, result->target);
359
+ prof_call_info_t *result = prof_get_call_info_result(self);
360
+ return prof_method_wrap(result->target);
503
361
  }
504
362
 
505
363
  /* call-seq:
@@ -507,10 +365,9 @@ call_info_target(VALUE self)
507
365
 
508
366
  Returns the total amount of time this method was called. */
509
367
  static VALUE
510
- call_info_called(VALUE self)
368
+ prof_call_info_called(VALUE self)
511
369
  {
512
- prof_call_info_t *result = get_call_info_result(self);
513
-
370
+ prof_call_info_t *result = prof_get_call_info_result(self);
514
371
  return INT2NUM(result->called);
515
372
  }
516
373
 
@@ -519,9 +376,10 @@ call_info_called(VALUE self)
519
376
 
520
377
  returns the line number of the method */
521
378
  static VALUE
522
- call_info_line(VALUE self)
379
+ prof_call_info_line(VALUE self)
523
380
  {
524
- return rb_int_new(get_call_info_result(self)->line);
381
+ prof_call_info_t *result = prof_get_call_info_result(self);
382
+ return rb_int_new(result->line);
525
383
  }
526
384
 
527
385
  /* call-seq:
@@ -529,10 +387,9 @@ call_info_line(VALUE self)
529
387
 
530
388
  Returns the total amount of time spent in this method and its children. */
531
389
  static VALUE
532
- call_info_total_time(VALUE self)
390
+ prof_call_info_total_time(VALUE self)
533
391
  {
534
- prof_call_info_t *result = get_call_info_result(self);
535
-
392
+ prof_call_info_t *result = prof_get_call_info_result(self);
536
393
  return rb_float_new(convert_measurement(result->total_time));
537
394
  }
538
395
 
@@ -541,9 +398,9 @@ call_info_total_time(VALUE self)
541
398
 
542
399
  Returns the total amount of time spent in this method. */
543
400
  static VALUE
544
- call_info_self_time(VALUE self)
401
+ prof_call_info_self_time(VALUE self)
545
402
  {
546
- prof_call_info_t *result = get_call_info_result(self);
403
+ prof_call_info_t *result = prof_get_call_info_result(self);
547
404
 
548
405
  return rb_float_new(convert_measurement(result->self_time));
549
406
  }
@@ -553,138 +410,177 @@ call_info_self_time(VALUE self)
553
410
 
554
411
  Returns the total amount of time this method waited for other threads. */
555
412
  static VALUE
556
- call_info_wait_time(VALUE self)
413
+ prof_call_info_wait_time(VALUE self)
557
414
  {
558
- prof_call_info_t *result = get_call_info_result(self);
415
+ prof_call_info_t *result = prof_get_call_info_result(self);
559
416
 
560
417
  return rb_float_new(convert_measurement(result->wait_time));
561
418
  }
562
419
 
563
420
  /* call-seq:
564
- children_time -> float
421
+ parent -> call_info
565
422
 
566
- Returns the total amount of time spent in this method's children. */
423
+ Returns the call_infos parent call_info object (the method that called this method).*/
567
424
  static VALUE
568
- call_info_children_time(VALUE self)
425
+ prof_call_info_parent(VALUE self)
569
426
  {
570
- prof_call_info_t *result = get_call_info_result(self);
571
- prof_measure_t children_time = result->total_time - result->self_time - result->wait_time;
572
- return rb_float_new(convert_measurement(children_time));
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;
573
432
  }
574
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
+ }
575
442
 
576
- /* Document-class: RubyProf::MethodInfo
577
- The RubyProf::MethodInfo class stores profiling data for a method.
578
- One instance of the RubyProf::MethodInfo class is created per method
579
- called per thread. Thus, if a method is called in two different
580
- thread then there will be two RubyProf::MethodInfo objects
581
- created. RubyProf::MethodInfo objects can be accessed via
582
- the RubyProf::Result object.
583
- */
443
+ /* call-seq:
444
+ children -> hash
584
445
 
585
- /* :nodoc: */
586
- static prof_method_t *
587
- prof_method_create(st_data_t key, VALUE klass, ID mid, int depth,
588
- const char* source_file, int line)
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)
589
450
  {
590
- prof_method_t *result = ALLOC(prof_method_t);
591
-
592
- result->klass = klass;
593
- result->mid = mid;
594
- result->key = key;
595
- result->depth = depth;
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
+ }
596
459
 
597
- result->called = 0;
598
- result->total_time = 0;
599
- result->self_time = 0;
600
- result->wait_time = 0;
601
- result->parents = caller_table_create();
602
- result->children = caller_table_create();
603
- result->active_frame = 0;
604
- result->base = result;
605
-
606
- result->source_file = source_file;
607
- result->line = line;
608
- return result;
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;
609
470
  }
610
471
 
611
472
  static void
612
- prof_method_mark(prof_method_t *data)
473
+ prof_call_infos_free(prof_call_infos_t *call_infos)
613
474
  {
614
- rb_gc_mark(data->klass);
475
+ xfree(call_infos->start);
476
+ xfree(call_infos);
615
477
  }
616
478
 
617
479
  static void
618
- prof_method_free(prof_method_t *data)
480
+ prof_add_call_info(prof_call_infos_t *call_infos, prof_call_info_t *call_info)
619
481
  {
620
- st_foreach(data->parents, free_call_infos, 0);
621
- caller_table_free(data->parents);
622
-
623
- st_foreach(data->children, free_call_infos, 0);
624
- caller_table_free(data->children);
625
-
626
- xfree(data);
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++;
627
492
  }
628
493
 
629
494
  static VALUE
630
- prof_method_new(prof_method_t *result)
495
+ prof_call_infos_wrap(prof_call_infos_t *call_infos)
631
496
  {
632
- return Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_free, result);
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;
633
508
  }
634
509
 
635
- static prof_method_t *
636
- get_prof_method(VALUE obj)
637
- {
638
- return (prof_method_t *) DATA_PTR(obj);
639
- }
640
510
 
641
- /* call-seq:
642
- called -> int
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
+ */
643
520
 
644
- Returns the number of times this method was called. */
645
- static VALUE
646
- prof_method_called(VALUE self)
521
+ static prof_method_t*
522
+ prof_method_create(prof_method_key_t *key, const char* source_file, int line)
647
523
  {
648
- prof_method_t *result = get_prof_method(self);
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);
649
528
 
650
- return INT2NUM(result->called);
651
- }
529
+ result->call_infos = prof_call_infos_create();
652
530
 
531
+ result->active = 0;
653
532
 
654
- /* call-seq:
655
- total_time -> float
533
+ if (source_file != NULL)
534
+ {
535
+ int len = strlen(source_file) + 1;
536
+ char *buffer = ALLOC_N(char, len);
656
537
 
657
- Returns the total amount of time spent in this method and its children. */
658
- static VALUE
659
- prof_method_total_time(VALUE self)
660
- {
661
- prof_method_t *result = get_prof_method(self);
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;
662
546
 
663
- return rb_float_new(convert_measurement(result->total_time));
547
+ return result;
664
548
  }
665
549
 
666
- /* call-seq:
667
- self_time -> float
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
+ }
668
556
 
669
- Returns the total amount of time spent in this method. */
670
- static VALUE
671
- prof_method_self_time(VALUE self)
557
+ static void
558
+ prof_method_free(prof_method_t *method)
672
559
  {
673
- prof_method_t *result = get_prof_method(self);
560
+ if (method->source_file)
561
+ {
562
+ xfree((char*)method->source_file);
563
+ }
674
564
 
675
- return rb_float_new(convert_measurement(result->self_time));
565
+ prof_call_infos_free(method->call_infos);
566
+ xfree(method->key);
567
+ xfree(method);
676
568
  }
677
569
 
678
- /* call-seq:
679
- wait_time -> float
680
-
681
- Returns the total amount of time this method waited for other threads. */
682
570
  static VALUE
683
- prof_method_wait_time(VALUE self)
571
+ prof_method_wrap(prof_method_t *result)
684
572
  {
685
- prof_method_t *result = get_prof_method(self);
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
+ }
686
579
 
687
- return rb_float_new(convert_measurement(result->wait_time));
580
+ static prof_method_t *
581
+ get_prof_method(VALUE obj)
582
+ {
583
+ return (prof_method_t *) DATA_PTR(obj);
688
584
  }
689
585
 
690
586
  /* call-seq:
@@ -697,18 +593,6 @@ prof_method_line(VALUE self)
697
593
  return rb_int_new(get_prof_method(self)->line);
698
594
  }
699
595
 
700
- /* call-seq:
701
- children_time -> float
702
-
703
- Returns the total amount of time spent in this method's children. */
704
- static VALUE
705
- prof_method_children_time(VALUE self)
706
- {
707
- prof_method_t *result = get_prof_method(self);
708
- prof_measure_t children_time = result->total_time - result->self_time - result->wait_time;
709
- return rb_float_new(convert_measurement(children_time));
710
- }
711
-
712
596
  /* call-seq:
713
597
  source_file => string
714
598
 
@@ -736,8 +620,7 @@ static VALUE
736
620
  prof_method_klass(VALUE self)
737
621
  {
738
622
  prof_method_t *result = get_prof_method(self);
739
-
740
- return result->klass;
623
+ return result->key->klass;
741
624
  }
742
625
 
743
626
  /* call-seq:
@@ -748,8 +631,7 @@ static VALUE
748
631
  prof_method_id(VALUE self)
749
632
  {
750
633
  prof_method_t *result = get_prof_method(self);
751
-
752
- return ID2SYM(result->mid);
634
+ return ID2SYM(result->key->mid);
753
635
  }
754
636
 
755
637
  /* call-seq:
@@ -762,7 +644,7 @@ static VALUE
762
644
  prof_klass_name(VALUE self)
763
645
  {
764
646
  prof_method_t *method = get_prof_method(self);
765
- return klass_name(method->klass);
647
+ return klass_name(method->key->klass);
766
648
  }
767
649
 
768
650
  /* call-seq:
@@ -772,10 +654,10 @@ Returns the name of this method in the format Object#method. Singletons
772
654
  methods will be returned in the format <Object::Object>#method.*/
773
655
 
774
656
  static VALUE
775
- prof_method_name(VALUE self)
657
+ prof_method_name(VALUE self, int depth)
776
658
  {
777
659
  prof_method_t *method = get_prof_method(self);
778
- return method_name(method->mid, method->depth);
660
+ return method_name(method->key->mid, depth);
779
661
  }
780
662
 
781
663
  /* call-seq:
@@ -787,105 +669,70 @@ static VALUE
787
669
  prof_full_name(VALUE self)
788
670
  {
789
671
  prof_method_t *method = get_prof_method(self);
790
- return full_name(method->klass, method->mid, method->depth);
672
+ return full_name(method->key->klass, method->key->mid, method->key->depth);
791
673
  }
792
674
 
793
675
  /* call-seq:
794
- called -> MethodInfo
676
+ call_infos -> Array of call_info
795
677
 
796
- For recursively called methods, returns the base method. Otherwise,
797
- returns self. */
678
+ Returns an array of call info objects that contain profiling information
679
+ about the current method.*/
798
680
  static VALUE
799
- prof_method_base(VALUE self)
681
+ prof_method_call_infos(VALUE self)
800
682
  {
801
683
  prof_method_t *method = get_prof_method(self);
802
-
803
- if (method == method->base)
804
- return self;
805
- else
806
- /* Target is a pointer to a method_info - so we have to be careful
807
- about the GC. We will wrap the method_info but provide no
808
- free method so the underlying object is not freed twice! */
809
- return Data_Wrap_Struct(cMethodInfo, NULL, NULL, method->base);
684
+ return prof_call_infos_wrap(method->call_infos);
810
685
  }
811
686
 
812
687
  static int
813
- prof_method_collect_call_infos(st_data_t key, st_data_t value, st_data_t result)
688
+ collect_methods(st_data_t key, st_data_t value, st_data_t result)
814
689
  {
815
- /* Create a new Ruby CallInfo object and store it into the hash
816
- keyed on the parent's name. We use the parent's name because
817
- we want to see that printed out for child records in
818
- a call graph. */
819
- prof_call_info_t *call_info = (prof_call_info_t *) value;
820
- VALUE arr = (VALUE) result;
821
- rb_ary_push(arr, call_info_new(call_info));
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
+
822
699
  return ST_CONTINUE;
823
700
  }
824
701
 
825
- /* call-seq:
826
- children -> hash
827
-
828
- Returns an array of call info objects of methods that this method
829
- was called by (ie, parents).*/
830
- static VALUE
831
- prof_method_parents(VALUE self)
702
+ /* ================ Method Table =================*/
703
+ static st_table *
704
+ method_table_create()
832
705
  {
833
- /* Returns an array of call info objects for this
834
- method's callers (the methods this method called). */
835
-
836
- VALUE children = rb_ary_new();
837
- prof_method_t *result = get_prof_method(self);
838
- st_foreach(result->parents, prof_method_collect_call_infos, children);
839
- return children;
706
+ return st_init_table(&type_method_hash);
840
707
  }
841
708
 
842
-
843
- /* call-seq:
844
- children -> hash
845
-
846
- Returns an array of call info objects of methods that this method
847
- called (ie, children).*/
848
- static VALUE
849
- prof_method_children(VALUE self)
709
+ static size_t
710
+ method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
850
711
  {
851
- /* Returns an array of call info objects for this
852
- method's callees (the methods this method called). */
853
-
854
- VALUE children = rb_ary_new();
855
- prof_method_t *result = get_prof_method(self);
856
- st_foreach(result->children, prof_method_collect_call_infos, children);
857
- return children;
712
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
858
713
  }
859
714
 
860
- /* :nodoc: */
861
- static VALUE
862
- prof_method_cmp(VALUE self, VALUE other)
863
- {
864
- /* For call graphs we want to sort methods by
865
- their total time, not self time. */
866
- prof_method_t *x = get_prof_method(self);
867
- prof_method_t *y = get_prof_method(other);
868
-
869
- if (x->called == 0 && y->called == 0)
870
- return INT2FIX(0);
871
- else if (x->called == 0)
872
- return INT2FIX(1);
873
- else if (y->called == 0)
874
- return INT2FIX(-1);
875
- else
876
- return rb_dbl_cmp(x->total_time, y->total_time);
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
+ }
877
727
  }
878
728
 
879
- static int
880
- collect_methods(st_data_t key, st_data_t value, st_data_t result)
881
- {
882
- /* Called for each method stored in a thread's method table.
883
- We want to store the method info information into an array.*/
884
- VALUE methods = (VALUE) result;
885
- prof_method_t *method = (prof_method_t *) value;
886
- rb_ary_push(methods, prof_method_new(method));
887
729
 
888
- return ST_CONTINUE;
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);
889
736
  }
890
737
 
891
738
 
@@ -897,20 +744,19 @@ thread_data_create()
897
744
  {
898
745
  thread_data_t* result = ALLOC(thread_data_t);
899
746
  result->stack = stack_create();
900
- result->method_info_table = method_info_table_create();
901
- result->last_switch = 0;
747
+ result->method_table = method_table_create();
748
+ result->last_switch = get_measurement();
902
749
  return result;
903
750
  }
904
751
 
905
752
  static void
906
753
  thread_data_free(thread_data_t* thread_data)
907
754
  {
755
+ method_table_free(thread_data->method_table);
908
756
  stack_free(thread_data->stack);
909
- method_info_table_free(thread_data->method_info_table);
910
757
  xfree(thread_data);
911
758
  }
912
759
 
913
-
914
760
  /* ---- Hash, keyed on thread, that stores thread's stack
915
761
  and methods---- */
916
762
 
@@ -920,15 +766,15 @@ threads_table_create()
920
766
  return st_init_numtable();
921
767
  }
922
768
 
923
- static inline size_t
769
+ static size_t
924
770
  threads_table_insert(st_table *table, VALUE thread, thread_data_t *thread_data)
925
771
  {
926
772
  /* Its too slow to key on the real thread id so just typecast thread instead. */
927
- return st_insert(table, (st_data_t ) thread, (st_data_t) thread_data);
773
+ return st_insert(table, (st_data_t) thread, (st_data_t) thread_data);
928
774
  }
929
775
 
930
- static inline thread_data_t *
931
- threads_table_lookup(st_table *table, long thread_id)
776
+ static thread_data_t *
777
+ threads_table_lookup(st_table *table, VALUE thread_id)
932
778
  {
933
779
  thread_data_t* result;
934
780
  st_data_t val;
@@ -974,14 +820,15 @@ collect_threads(st_data_t key, st_data_t value, st_data_t result)
974
820
  as an int. */
975
821
  thread_data_t* thread_data = (thread_data_t*) value;
976
822
  VALUE threads_hash = (VALUE) result;
977
-
823
+
978
824
  VALUE methods = rb_ary_new();
979
-
825
+
980
826
  /* Now collect an array of all the called methods */
981
- st_foreach(thread_data->method_info_table, collect_methods, methods);
982
-
827
+ st_table* method_table = thread_data->method_table;
828
+ st_foreach(method_table, collect_methods, methods);
829
+
983
830
  /* Store the results in the threads hash keyed on the thread id. */
984
- rb_hash_aset(threads_hash, ULONG2NUM(thread_data->thread_id), methods);
831
+ rb_hash_aset(threads_hash, thread_data->thread_id, methods);
985
832
 
986
833
  return ST_CONTINUE;
987
834
  }
@@ -989,6 +836,7 @@ collect_threads(st_data_t key, st_data_t value, st_data_t result)
989
836
 
990
837
  /* ================ Profiling =================*/
991
838
  /* Copied from eval.c */
839
+ #ifdef DEBUG
992
840
  static char *
993
841
  get_event_name(rb_event_flag_t event)
994
842
  {
@@ -1011,70 +859,143 @@ get_event_name(rb_event_flag_t event)
1011
859
  return "raise";
1012
860
  default:
1013
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);
1014
889
  }
890
+ return method;
1015
891
  }
1016
892
 
1017
893
  static void
1018
- update_result(thread_data_t* thread_data,
1019
- prof_measure_t total_time,
1020
- prof_frame_t *parent_frame, prof_frame_t *child_frame)
1021
- {
1022
- prof_method_t *parent = NULL;
1023
- prof_method_t *child = child_frame->method;
1024
- prof_call_info_t *parent_call_info = NULL;
1025
- prof_call_info_t *child_call_info = NULL;
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;
1026
901
 
1027
- prof_measure_t wait_time = child_frame->wait_time;
1028
- prof_measure_t self_time = total_time - child_frame->child_time - wait_time;
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;
1029
907
 
1030
- /* Update information about the child (ie, the current method) */
1031
- child->called++;
1032
- child->total_time += total_time;
1033
- child->self_time += self_time;
1034
- child->wait_time += wait_time;
908
+ /* Note where the current method was called from */
909
+ if (parent_frame)
910
+ call_info->line = parent_frame->line;
911
+ }
1035
912
 
1036
- if (!parent_frame) return;
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;
1037
918
 
1038
- parent = parent_frame->method;
1039
-
1040
- child_call_info = caller_table_lookup(parent->children, child->key);
1041
- if (child_call_info == NULL)
1042
- {
1043
- child_call_info = call_info_create(child);
1044
- caller_table_insert(parent->children, child->key, child_call_info);
1045
- }
919
+ /* Get new thread information. */
920
+ thread_data_t *thread_data = threads_table_lookup(threads_tbl, thread_id);
1046
921
 
1047
- child_call_info->called++;
1048
- child_call_info->total_time += total_time;
1049
- child_call_info->self_time += self_time;
1050
- child_call_info->wait_time += wait_time;
1051
- child_call_info->line = parent_frame->line;
1052
-
1053
- /* Update child's parent information */
1054
- parent_call_info = caller_table_lookup(child->parents, parent->key);
1055
- if (parent_call_info == NULL)
1056
- {
1057
- parent_call_info = call_info_create(parent);
1058
- caller_table_insert(child->parents, parent->key, parent_call_info);
1059
- }
1060
-
1061
- parent_call_info->called++;
1062
- parent_call_info->total_time += total_time;
1063
- parent_call_info->self_time += self_time;
1064
- parent_call_info->wait_time += wait_time;
1065
- parent_call_info->line = (parent_frame ? parent_frame->line : 0);
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);
1066
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
+ }
1067
969
 
1068
- /* If the caller is the top of the stack, the merge in
1069
- all the child results. We have to do this because
1070
- the top method is never popped since sooner or later
1071
- the user has to call RubyProf::stop.*/
1072
-
1073
- if (stack_size(thread_data->stack) == 1)
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))
1074
987
  {
1075
- parent->total_time += total_time;
1076
- parent->wait_time += wait_time;
1077
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);
1078
999
  }
1079
1000
 
1080
1001
 
@@ -1087,44 +1008,51 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1087
1008
  #endif
1088
1009
  {
1089
1010
 
1090
- VALUE thread;
1011
+ VALUE thread = Qnil;
1012
+ VALUE thread_id = Qnil;
1091
1013
  prof_measure_t now = 0;
1092
1014
  thread_data_t* thread_data = NULL;
1093
- long thread_id = 0;
1094
1015
  prof_frame_t *frame = NULL;
1016
+
1017
+
1095
1018
  #ifdef RUBY_VM
1096
1019
 
1097
- if (event != RUBY_EVENT_C_CALL &&
1098
- event != RUBY_EVENT_C_RETURN) {
1099
- VALUE thread = rb_thread_current();
1100
- rb_frame_method_id_and_class(&mid, &klass);
1020
+ if (event != RUBY_EVENT_C_CALL && event != RUBY_EVENT_C_RETURN) {
1021
+ rb_frame_method_id_and_class(&mid, &klass);
1101
1022
  }
1102
1023
  #endif
1024
+
1025
+ #ifdef DEBUG
1103
1026
  /* This code is here for debug purposes - uncomment it out
1104
1027
  when debugging to see a print out of exactly what the
1105
- profiler is tracing.
1028
+ profiler is tracing. */
1106
1029
  {
1107
- st_data_t key = 0;
1108
- static unsigned long last_thread_id = 0;
1030
+ char* key = 0;
1031
+ static VALUE last_thread_id = Qnil;
1109
1032
 
1110
1033
  VALUE thread = rb_thread_current();
1111
- unsigned long thread_id = get_thread_id(thread);
1112
- char* class_name = rb_obj_classname(klass);
1034
+ VALUE thread_id = rb_obj_id(thread);
1035
+ char* class_name = NULL;
1113
1036
  char* method_name = rb_id2name(mid);
1114
- char* source_file = node ? node->nd_file : 0;
1115
- unsigned int source_line = node ? nd_line(node) : 0;
1037
+ char* source_file = rb_sourcefile();
1038
+ unsigned int source_line = rb_sourceline();
1039
+
1116
1040
  char* event_name = get_event_name(event);
1117
-
1118
- if (last_thread_id != thread_id)
1119
- printf("\n");
1120
-
1041
+
1121
1042
  if (klass != 0)
1122
1043
  klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
1123
- key = method_key(klass, mid, 0);
1124
- printf("%2u: %-8s :%2d %s#%s (%u)\n",
1125
- thread_id, event_name, source_line, class_name, method_name, key);
1044
+
1045
+ class_name = rb_class2name(klass);
1046
+
1047
+ if (last_thread_id != thread_id)
1048
+ printf("\n");
1049
+
1050
+ printf("%2u: %-8s :%2d %s#%s\n",
1051
+ thread_id, event_name, source_line, class_name, method_name);
1052
+ fflush(stdout);
1126
1053
  last_thread_id = thread_id;
1127
- } */
1054
+ }
1055
+ #endif
1128
1056
 
1129
1057
  /* Special case - skip any methods from the mProf
1130
1058
  module, such as Prof.stop, since they clutter
@@ -1136,41 +1064,23 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1136
1064
 
1137
1065
  /* Get the current thread information. */
1138
1066
  thread = rb_thread_current();
1139
- thread_id = get_thread_id(thread);
1067
+ thread_id = rb_obj_id(thread);
1068
+
1069
+ if (exclude_threads_tbl &&
1070
+ st_lookup(exclude_threads_tbl, (st_data_t) thread_id, 0))
1071
+ {
1072
+ return;
1073
+ }
1140
1074
 
1141
1075
  /* Was there a context switch? */
1142
1076
  if (!last_thread_data || last_thread_data->thread_id != thread_id)
1143
- {
1144
- prof_measure_t wait_time = 0;
1145
-
1146
- /* Get new thread information. */
1147
- thread_data = threads_table_lookup(threads_tbl, thread_id);
1148
-
1149
- /* How long has this thread been waiting? */
1150
- wait_time = now - thread_data->last_switch;
1151
- thread_data->last_switch = 0;
1152
-
1153
- /* Get the frame at the top of the stack. This may represent
1154
- the current method (EVENT_LINE, EVENT_RETURN) or the
1155
- previous method (EVENT_CALL).*/
1156
- frame = stack_peek(thread_data->stack);
1157
-
1158
- if (frame)
1159
- frame->wait_time += wait_time;
1160
-
1161
- /* Save on the last thread the time of the context switch
1162
- and reset this thread's last context switch to 0.*/
1163
- if (last_thread_data)
1164
- last_thread_data->last_switch = now;
1165
-
1166
- last_thread_data = thread_data;
1167
- }
1077
+ thread_data = switch_thread(thread_id, now);
1168
1078
  else
1169
- {
1170
1079
  thread_data = last_thread_data;
1171
- frame = stack_peek(thread_data->stack);
1172
- }
1173
1080
 
1081
+ /* Get the current frame for the current thread. */
1082
+ frame = stack_peek(thread_data->stack);
1083
+
1174
1084
  switch (event) {
1175
1085
  case RUBY_EVENT_LINE:
1176
1086
  {
@@ -1179,14 +1089,10 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1179
1089
  called from. */
1180
1090
  if (frame)
1181
1091
  {
1182
- #ifdef RUBY_VM
1183
- frame->line = rb_sourceline();
1184
- #else
1185
- if (node)
1186
- frame->line = nd_line(node);
1187
- #endif
1092
+ frame->line = rb_sourceline();
1188
1093
  break;
1189
1094
  }
1095
+
1190
1096
  /* If we get here there was no frame, which means this is
1191
1097
  the first method seen for this thread, so fall through
1192
1098
  to below to create it. */
@@ -1194,8 +1100,7 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1194
1100
  case RUBY_EVENT_CALL:
1195
1101
  case RUBY_EVENT_C_CALL:
1196
1102
  {
1197
- int depth = 0;
1198
- st_data_t key = 0;
1103
+ prof_call_info_t *call_info = NULL;
1199
1104
  prof_method_t *method = NULL;
1200
1105
 
1201
1106
  /* Is this an include for a module? If so get the actual
@@ -1205,56 +1110,41 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1205
1110
  if (klass != 0)
1206
1111
  klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
1207
1112
 
1208
- key = method_key(klass, mid, 0);
1209
-
1210
- method = method_info_table_lookup(thread_data->method_info_table, key);
1211
-
1212
- if (!method)
1113
+ /* Assume this is the first time we have called this method. */
1114
+ method = get_method(event, node, klass, mid, 0, thread_data->method_table);
1115
+
1116
+ /* Check for a recursive call */
1117
+ if (method->active)
1213
1118
  {
1214
- const char* source_file = rb_sourcefile();
1215
- int line = rb_sourceline();
1216
-
1217
- /* Line numbers are not accurate for c method calls */
1218
- if (event == RUBY_EVENT_C_CALL)
1219
- {
1220
- line = 0;
1221
- source_file = NULL;
1222
- }
1223
-
1224
- method = prof_method_create(key, klass, mid, depth, source_file, line);
1225
- method_info_table_insert(thread_data->method_info_table, key, method);
1119
+ /* Yes, this method is already active */
1120
+ method = get_method(event, node, klass, mid, method->key->depth + 1, thread_data->method_table);
1226
1121
  }
1227
-
1228
- depth = method->active_frame;
1229
- method->active_frame++;
1230
-
1231
- if (depth > 0)
1122
+ else
1232
1123
  {
1233
- prof_method_t *base_method = method;
1234
- key = method_key(klass, mid, depth);
1235
- method = method_info_table_lookup(thread_data->method_info_table, key);
1236
-
1237
- if (!method)
1124
+ /* No, so make it active */
1125
+ method->active = 1;
1126
+ }
1127
+
1128
+ if (!frame)
1129
+ {
1130
+ call_info = prof_call_info_create(method, NULL);
1131
+ prof_add_call_info(method->call_infos, call_info);
1132
+ }
1133
+ else
1134
+ {
1135
+ call_info = call_info_table_lookup(frame->call_info->call_infos, method->key);
1136
+
1137
+ if (!call_info)
1238
1138
  {
1239
- const char* source_file = rb_sourcefile();
1240
- int line = rb_sourceline();
1241
-
1242
- /* Line numbers are not accurate for c method calls */
1243
- if (event == RUBY_EVENT_C_CALL)
1244
- {
1245
- line = 0;
1246
- source_file = NULL;
1247
- }
1248
-
1249
- method = prof_method_create(key, klass, mid, depth, source_file, line);
1250
- method->base = base_method;
1251
- method_info_table_insert(thread_data->method_info_table, key, method);
1139
+ call_info = prof_call_info_create(method, frame->call_info);
1140
+ call_info_table_insert(frame->call_info->call_infos, method->key, call_info);
1141
+ prof_add_call_info(method->call_infos, call_info);
1252
1142
  }
1253
1143
  }
1254
1144
 
1255
1145
  /* Push a new frame onto the stack */
1256
1146
  frame = stack_push(thread_data->stack);
1257
- frame->method = method;
1147
+ frame->call_info = call_info;
1258
1148
  frame->start_time = now;
1259
1149
  frame->wait_time = 0;
1260
1150
  frame->child_time = 0;
@@ -1265,29 +1155,7 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1265
1155
  case RUBY_EVENT_RETURN:
1266
1156
  case RUBY_EVENT_C_RETURN:
1267
1157
  {
1268
- prof_frame_t* caller_frame = NULL;
1269
-
1270
- prof_measure_t total_time;
1271
-
1272
- frame = stack_pop(thread_data->stack);
1273
- caller_frame = stack_peek(thread_data->stack);
1274
-
1275
- /* Frame can be null. This can happen if RubProf.start is called from
1276
- a method that exits. And it can happen if an exception is raised
1277
- in code that is being profiled and the stack unwinds (RubProf is
1278
- not notified of that by the ruby runtime. */
1279
- if (frame == NULL) return;
1280
-
1281
- total_time = now - frame->start_time;
1282
-
1283
- if (caller_frame)
1284
- {
1285
- caller_frame->child_time += total_time;
1286
- }
1287
-
1288
- frame->method->base->active_frame--;
1289
-
1290
- update_result(thread_data, total_time, caller_frame, frame);
1158
+ pop_frame(thread_data, now);
1291
1159
  break;
1292
1160
  }
1293
1161
  }
@@ -1374,7 +1242,9 @@ prof_result_threads(VALUE self)
1374
1242
  *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
1375
1243
  *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms.
1376
1244
  *RubyProf::ALLOCATIONS - Measure object allocations. This requires a patched Ruby interpreter.
1377
- *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.*/
1245
+ *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.
1246
+ *RubyProf::GC_RUNS - Measure number of garbage collections. This requires a patched Ruby interpreter.
1247
+ *RubyProf::GC_TIME - Measure time spent doing garbage collection. This requires a patched Ruby interpreter.*/
1378
1248
  static VALUE
1379
1249
  prof_get_measure_mode(VALUE self)
1380
1250
  {
@@ -1390,7 +1260,9 @@ prof_get_measure_mode(VALUE self)
1390
1260
  *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
1391
1261
  *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms.
1392
1262
  *RubyProf::ALLOCATIONS - Measure object allocations. This requires a patched Ruby interpreter.
1393
- *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.*/
1263
+ *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.
1264
+ *RubyProf::GC_RUNS - Measure number of garbage collections. This requires a patched Ruby interpreter.
1265
+ *RubyProf::GC_TIME - Measure time spent doing garbage collection. This requires a patched Ruby interpreter.*/
1394
1266
  static VALUE
1395
1267
  prof_set_measure_mode(VALUE self, VALUE val)
1396
1268
  {
@@ -1415,7 +1287,7 @@ prof_set_measure_mode(VALUE self, VALUE val)
1415
1287
  #if defined(MEASURE_CPU_TIME)
1416
1288
  case MEASURE_CPU_TIME:
1417
1289
  if (cpu_frequency == 0)
1418
- cpu_frequency = measure_cpu_time();
1290
+ cpu_frequency = get_cpu_frequency();
1419
1291
  get_measurement = measure_cpu_time;
1420
1292
  convert_measurement = convert_cpu_time;
1421
1293
  break;
@@ -1434,9 +1306,23 @@ prof_set_measure_mode(VALUE self, VALUE val)
1434
1306
  convert_measurement = convert_memory;
1435
1307
  break;
1436
1308
  #endif
1437
-
1309
+
1310
+ #if defined(MEASURE_GC_RUNS)
1311
+ case MEASURE_GC_RUNS:
1312
+ get_measurement = measure_gc_runs;
1313
+ convert_measurement = convert_gc_runs;
1314
+ break;
1315
+ #endif
1316
+
1317
+ #if defined(MEASURE_GC_TIME)
1318
+ case MEASURE_GC_TIME:
1319
+ get_measurement = measure_gc_time;
1320
+ convert_measurement = convert_gc_time;
1321
+ break;
1322
+ #endif
1323
+
1438
1324
  default:
1439
- rb_raise(rb_eArgError, "invalid mode: %d", mode);
1325
+ rb_raise(rb_eArgError, "invalid mode: %ld", mode);
1440
1326
  break;
1441
1327
  }
1442
1328
 
@@ -1444,7 +1330,75 @@ prof_set_measure_mode(VALUE self, VALUE val)
1444
1330
  return val;
1445
1331
  }
1446
1332
 
1333
+ /* call-seq:
1334
+ exclude_threads= -> void
1335
+
1336
+ Specifies what threads ruby-prof should exclude from profiling */
1337
+ static VALUE
1338
+ prof_set_exclude_threads(VALUE self, VALUE threads)
1339
+ {
1340
+ int i;
1341
+
1342
+ if (threads_tbl != NULL)
1343
+ {
1344
+ rb_raise(rb_eRuntimeError, "can't set exclude_threads while profiling");
1345
+ }
1346
+
1347
+ /* Stay simple, first free the old hash table */
1348
+ if (exclude_threads_tbl)
1349
+ {
1350
+ st_free_table(exclude_threads_tbl);
1351
+ exclude_threads_tbl = NULL;
1352
+ }
1353
+
1354
+ /* Now create a new one if the user passed in any threads */
1355
+ if (threads != Qnil)
1356
+ {
1357
+ Check_Type(threads, T_ARRAY);
1358
+ exclude_threads_tbl = st_init_numtable();
1359
+
1360
+ for (i=0; i < RARRAY_LEN(threads); ++i)
1361
+ {
1362
+ VALUE thread = rb_ary_entry(threads, i);
1363
+ st_insert(exclude_threads_tbl, (st_data_t) rb_obj_id(thread), 0);
1364
+ }
1365
+ }
1366
+ return threads;
1367
+ }
1368
+
1369
+
1447
1370
  /* ========= Profiling ============= */
1371
+ void
1372
+ prof_install_hook()
1373
+ {
1374
+ #ifdef RUBY_VM
1375
+ rb_add_event_hook(prof_event_hook,
1376
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1377
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
1378
+ | RUBY_EVENT_LINE, Qnil);
1379
+ #else
1380
+ rb_add_event_hook(prof_event_hook,
1381
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1382
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
1383
+ | RUBY_EVENT_LINE);
1384
+ #endif
1385
+
1386
+ #if defined(TOGGLE_GC_STATS)
1387
+ rb_gc_enable_stats();
1388
+ #endif
1389
+ }
1390
+
1391
+ void
1392
+ prof_remove_hook()
1393
+ {
1394
+ #if defined(TOGGLE_GC_STATS)
1395
+ rb_gc_disable_stats();
1396
+ #endif
1397
+
1398
+ /* Now unregister from event */
1399
+ rb_remove_event_hook(prof_event_hook);
1400
+ }
1401
+
1448
1402
 
1449
1403
 
1450
1404
  /* call-seq:
@@ -1461,7 +1415,7 @@ prof_running(VALUE self)
1461
1415
  }
1462
1416
 
1463
1417
  /* call-seq:
1464
- start -> void
1418
+ start -> RubyProf
1465
1419
 
1466
1420
  Starts recording profile data.*/
1467
1421
  static VALUE
@@ -1475,26 +1429,50 @@ prof_start(VALUE self)
1475
1429
  /* Setup globals */
1476
1430
  last_thread_data = NULL;
1477
1431
  threads_tbl = threads_table_create();
1478
-
1479
- #ifdef RUBY_VM
1480
- rb_add_event_hook(prof_event_hook,
1481
- RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1482
- RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
1483
- | RUBY_EVENT_LINE, Qnil);
1484
- #else
1485
- rb_add_event_hook(prof_event_hook,
1486
- RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1487
- RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
1488
- | RUBY_EVENT_LINE);
1489
- #endif
1490
1432
 
1491
- #if defined(MEASURE_MEMORY)
1492
- rb_gc_enable_stats();
1493
- #endif
1433
+ prof_install_hook();
1434
+ return self;
1435
+ }
1436
+
1437
+ /* call-seq:
1438
+ pause -> RubyProf
1439
+
1440
+ Pauses collecting profile data. */
1441
+ static VALUE
1442
+ prof_pause(VALUE self)
1443
+ {
1444
+ if (threads_tbl == NULL)
1445
+ {
1446
+ rb_raise(rb_eRuntimeError, "RubyProf is not running.");
1447
+ }
1494
1448
 
1495
- return Qnil;
1449
+ prof_remove_hook();
1450
+ return self;
1496
1451
  }
1497
1452
 
1453
+ /* call-seq:
1454
+ resume {block} -> RubyProf
1455
+
1456
+ Resumes recording profile data.*/
1457
+ static VALUE
1458
+ prof_resume(VALUE self)
1459
+ {
1460
+ if (threads_tbl == NULL)
1461
+ {
1462
+ prof_start(self);
1463
+ }
1464
+ else
1465
+ {
1466
+ prof_install_hook();
1467
+ }
1468
+
1469
+ if (rb_block_given_p())
1470
+ {
1471
+ rb_ensure(rb_yield, self, prof_pause, self);
1472
+ }
1473
+
1474
+ return self;
1475
+ }
1498
1476
 
1499
1477
  /* call-seq:
1500
1478
  stop -> RubyProf::Result
@@ -1503,19 +1481,11 @@ prof_start(VALUE self)
1503
1481
  static VALUE
1504
1482
  prof_stop(VALUE self)
1505
1483
  {
1506
- #if defined(MEASURE_MEMORY)
1507
- rb_gc_disable_stats();
1508
- #endif
1509
-
1510
1484
  VALUE result = Qnil;
1485
+
1486
+ prof_remove_hook();
1511
1487
 
1512
- if (threads_tbl == NULL)
1513
- {
1514
- rb_raise(rb_eRuntimeError, "RubyProf is not running.");
1515
- }
1516
-
1517
- /* Now unregister from event */
1518
- rb_remove_event_hook(prof_event_hook);
1488
+ prof_pop_threads();
1519
1489
 
1520
1490
  /* Create the result */
1521
1491
  result = prof_result_new();
@@ -1529,7 +1499,6 @@ prof_stop(VALUE self)
1529
1499
  return result;
1530
1500
  }
1531
1501
 
1532
-
1533
1502
  /* call-seq:
1534
1503
  profile {block} -> RubyProf::Result
1535
1504
 
@@ -1537,16 +1506,83 @@ Profiles the specified block and returns a RubyProf::Result object. */
1537
1506
  static VALUE
1538
1507
  prof_profile(VALUE self)
1539
1508
  {
1509
+ int result;
1510
+
1540
1511
  if (!rb_block_given_p())
1541
1512
  {
1542
1513
  rb_raise(rb_eArgError, "A block must be provided to the profile method.");
1543
1514
  }
1544
1515
 
1545
1516
  prof_start(self);
1546
- rb_yield(Qnil);
1517
+ rb_protect(rb_yield, self, &result);
1547
1518
  return prof_stop(self);
1548
1519
  }
1549
1520
 
1521
+ /* Get arround annoying limitations in RDOC */
1522
+
1523
+ /* Document-method: measure_process_time
1524
+ call-seq:
1525
+ measure_process_time -> float
1526
+
1527
+ Returns the process time.*/
1528
+
1529
+ /* Document-method: measure_wall_time
1530
+ call-seq:
1531
+ measure_wall_time -> float
1532
+
1533
+ Returns the wall time.*/
1534
+
1535
+ /* Document-method: measure_cpu_time
1536
+ call-seq:
1537
+ measure_cpu_time -> float
1538
+
1539
+ Returns the cpu time.*/
1540
+
1541
+ /* Document-method: get_cpu_frequency
1542
+ call-seq:
1543
+ cpu_frequency -> int
1544
+
1545
+ Returns the cpu's frequency. This value is needed when
1546
+ RubyProf::measure_mode is set to CPU_TIME. */
1547
+
1548
+ /* Document-method: cpu_frequency
1549
+ call-seq:
1550
+ cpu_frequency -> int
1551
+
1552
+ Returns the cpu's frequency. This value is needed when
1553
+ RubyProf::measure_mode is set to CPU_TIME. */
1554
+
1555
+ /* Document-method: cpu_frequency=
1556
+ call-seq:
1557
+ cpu_frequency = frequency
1558
+
1559
+ Sets the cpu's frequency. This value is needed when
1560
+ RubyProf::measure_mode is set to CPU_TIME. */
1561
+
1562
+ /* Document-method: measure_allocations
1563
+ call-seq:
1564
+ measure_allocations -> int
1565
+
1566
+ Returns the total number of object allocations since Ruby started.*/
1567
+
1568
+ /* Document-method: measure_memory
1569
+ call-seq:
1570
+ measure_memory -> int
1571
+
1572
+ Returns total allocated memory in bytes.*/
1573
+
1574
+ /* Document-method: measure_gc_runs
1575
+ call-seq:
1576
+ gc_runs -> Integer
1577
+
1578
+ Returns the total number of garbage collections.*/
1579
+
1580
+ /* Document-method: measure_gc_time
1581
+ call-seq:
1582
+ gc_time -> Integer
1583
+
1584
+ Returns the time spent doing garbage collections in microseconds.*/
1585
+
1550
1586
 
1551
1587
  #if defined(_WIN32)
1552
1588
  __declspec(dllexport)
@@ -1556,23 +1592,29 @@ void
1556
1592
  Init_ruby_prof()
1557
1593
  {
1558
1594
  mProf = rb_define_module("RubyProf");
1559
- rb_define_const(mProf, "VERSION", rb_str_new2(PROF_VERSION));
1595
+ rb_define_const(mProf, "VERSION", rb_str_new2(RUBY_PROF_VERSION));
1560
1596
  rb_define_module_function(mProf, "start", prof_start, 0);
1561
1597
  rb_define_module_function(mProf, "stop", prof_stop, 0);
1598
+ rb_define_module_function(mProf, "resume", prof_resume, 0);
1599
+ rb_define_module_function(mProf, "pause", prof_pause, 0);
1562
1600
  rb_define_module_function(mProf, "running?", prof_running, 0);
1563
1601
  rb_define_module_function(mProf, "profile", prof_profile, 0);
1564
1602
 
1603
+ rb_define_singleton_method(mProf, "exclude_threads=", prof_set_exclude_threads, 1);
1565
1604
  rb_define_singleton_method(mProf, "measure_mode", prof_get_measure_mode, 0);
1566
1605
  rb_define_singleton_method(mProf, "measure_mode=", prof_set_measure_mode, 1);
1567
1606
 
1568
1607
  rb_define_const(mProf, "CLOCKS_PER_SEC", INT2NUM(CLOCKS_PER_SEC));
1569
1608
  rb_define_const(mProf, "PROCESS_TIME", INT2NUM(MEASURE_PROCESS_TIME));
1609
+ rb_define_singleton_method(mProf, "measure_process_time", prof_measure_process_time, 0); /* in measure_process_time.h */
1570
1610
  rb_define_const(mProf, "WALL_TIME", INT2NUM(MEASURE_WALL_TIME));
1611
+ rb_define_singleton_method(mProf, "measure_wall_time", prof_measure_wall_time, 0); /* in measure_wall_time.h */
1571
1612
 
1572
1613
  #ifndef MEASURE_CPU_TIME
1573
1614
  rb_define_const(mProf, "CPU_TIME", Qnil);
1574
1615
  #else
1575
1616
  rb_define_const(mProf, "CPU_TIME", INT2NUM(MEASURE_CPU_TIME));
1617
+ rb_define_singleton_method(mProf, "measure_cpu_time", prof_measure_cpu_time, 0); /* in measure_cpu_time.h */
1576
1618
  rb_define_singleton_method(mProf, "cpu_frequency", prof_get_cpu_frequency, 0); /* in measure_cpu_time.h */
1577
1619
  rb_define_singleton_method(mProf, "cpu_frequency=", prof_set_cpu_frequency, 1); /* in measure_cpu_time.h */
1578
1620
  #endif
@@ -1581,20 +1623,36 @@ Init_ruby_prof()
1581
1623
  rb_define_const(mProf, "ALLOCATIONS", Qnil);
1582
1624
  #else
1583
1625
  rb_define_const(mProf, "ALLOCATIONS", INT2NUM(MEASURE_ALLOCATIONS));
1626
+ rb_define_singleton_method(mProf, "measure_allocations", prof_measure_allocations, 0); /* in measure_allocations.h */
1584
1627
  #endif
1585
1628
 
1586
1629
  #ifndef MEASURE_MEMORY
1587
1630
  rb_define_const(mProf, "MEMORY", Qnil);
1588
1631
  #else
1589
1632
  rb_define_const(mProf, "MEMORY", INT2NUM(MEASURE_MEMORY));
1633
+ rb_define_singleton_method(mProf, "measure_memory", prof_measure_memory, 0); /* in measure_memory.h */
1590
1634
  #endif
1591
-
1635
+
1636
+ #ifndef MEASURE_GC_RUNS
1637
+ rb_define_const(mProf, "GC_RUNS", Qnil);
1638
+ #else
1639
+ rb_define_const(mProf, "GC_RUNS", INT2NUM(MEASURE_GC_RUNS));
1640
+ rb_define_singleton_method(mProf, "measure_gc_runs", prof_measure_gc_runs, 0); /* in measure_gc_runs.h */
1641
+ #endif
1642
+
1643
+ #ifndef MEASURE_GC_TIME
1644
+ rb_define_const(mProf, "GC_TIME", Qnil);
1645
+ #else
1646
+ rb_define_const(mProf, "GC_TIME", INT2NUM(MEASURE_GC_TIME));
1647
+ rb_define_singleton_method(mProf, "measure_gc_time", prof_measure_gc_time, 0); /* in measure_gc_time.h */
1648
+ #endif
1649
+
1592
1650
  cResult = rb_define_class_under(mProf, "Result", rb_cObject);
1593
1651
  rb_undef_method(CLASS_OF(cMethodInfo), "new");
1594
1652
  rb_define_method(cResult, "threads", prof_result_threads, 0);
1595
1653
 
1654
+ /* MethodInfo */
1596
1655
  cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
1597
- rb_include_module(cMethodInfo, rb_mComparable);
1598
1656
  rb_undef_method(CLASS_OF(cMethodInfo), "new");
1599
1657
 
1600
1658
  rb_define_method(cMethodInfo, "klass", prof_method_klass, 0);
@@ -1602,27 +1660,21 @@ Init_ruby_prof()
1602
1660
  rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
1603
1661
  rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
1604
1662
  rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
1605
- rb_define_method(cMethodInfo, "base", prof_method_base, 0);
1606
1663
 
1607
- rb_define_method(cMethodInfo, "parents", prof_method_parents, 0);
1608
- rb_define_method(cMethodInfo, "children", prof_method_children, 0);
1609
- rb_define_method(cMethodInfo, "<=>", prof_method_cmp, 1);
1610
1664
  rb_define_method(cMethodInfo, "source_file", prof_method_source_file,0);
1611
1665
  rb_define_method(cMethodInfo, "line", prof_method_line, 0);
1612
- rb_define_method(cMethodInfo, "called", prof_method_called, 0);
1613
- rb_define_method(cMethodInfo, "total_time", prof_method_total_time, 0);
1614
- rb_define_method(cMethodInfo, "self_time", prof_method_self_time, 0);
1615
- rb_define_method(cMethodInfo, "wait_time", prof_method_wait_time, 0);
1616
- rb_define_method(cMethodInfo, "children_time", prof_method_children_time, 0);
1617
1666
 
1667
+ rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
1668
+
1669
+ /* CallInfo */
1618
1670
  cCallInfo = rb_define_class_under(mProf, "CallInfo", rb_cObject);
1619
1671
  rb_undef_method(CLASS_OF(cCallInfo), "new");
1620
- rb_define_method(cCallInfo, "target", call_info_target, 0);
1621
- rb_define_method(cCallInfo, "called", call_info_called, 0);
1622
- rb_define_method(cCallInfo, "total_time", call_info_total_time, 0);
1623
- rb_define_method(cCallInfo, "self_time", call_info_self_time, 0);
1624
- rb_define_method(cCallInfo, "wait_time", call_info_wait_time, 0);
1625
- rb_define_method(cCallInfo, "line", call_info_line, 0);
1626
- rb_define_method(cCallInfo, "children_time", call_info_children_time, 0);
1672
+ rb_define_method(cCallInfo, "parent", prof_call_info_parent, 0);
1673
+ rb_define_method(cCallInfo, "children", prof_call_info_children, 0);
1674
+ rb_define_method(cCallInfo, "target", prof_call_info_target, 0);
1675
+ rb_define_method(cCallInfo, "called", prof_call_info_called, 0);
1676
+ rb_define_method(cCallInfo, "total_time", prof_call_info_total_time, 0);
1677
+ rb_define_method(cCallInfo, "self_time", prof_call_info_self_time, 0);
1678
+ rb_define_method(cCallInfo, "wait_time", prof_call_info_wait_time, 0);
1679
+ rb_define_method(cCallInfo, "line", prof_call_info_line, 0);
1627
1680
  }
1628
-