ruby-prof 0.10.8 → 0.11.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/CHANGES +38 -18
  2. data/LICENSE +4 -3
  3. data/README.rdoc +30 -66
  4. data/Rakefile +47 -54
  5. data/bin/ruby-prof +24 -4
  6. data/ext/ruby_prof/extconf.rb +9 -16
  7. data/ext/ruby_prof/rp_call_info.c +369 -0
  8. data/ext/ruby_prof/rp_call_info.h +46 -0
  9. data/ext/ruby_prof/rp_measure.c +48 -0
  10. data/ext/ruby_prof/rp_measure.h +45 -0
  11. data/ext/ruby_prof/rp_measure_allocations.c +86 -0
  12. data/ext/ruby_prof/rp_measure_cpu_time.c +112 -0
  13. data/ext/ruby_prof/rp_measure_gc_runs.c +87 -0
  14. data/ext/ruby_prof/rp_measure_gc_time.c +73 -0
  15. data/ext/ruby_prof/rp_measure_memory.c +81 -0
  16. data/ext/ruby_prof/rp_measure_process_time.c +71 -0
  17. data/ext/ruby_prof/rp_measure_wall_time.c +42 -0
  18. data/ext/ruby_prof/rp_method.c +363 -0
  19. data/ext/ruby_prof/rp_method.h +55 -0
  20. data/ext/ruby_prof/rp_stack.c +61 -0
  21. data/ext/ruby_prof/rp_stack.h +40 -0
  22. data/ext/ruby_prof/rp_thread.c +113 -0
  23. data/ext/ruby_prof/rp_thread.h +20 -0
  24. data/ext/ruby_prof/ruby_prof.c +259 -1398
  25. data/ext/ruby_prof/ruby_prof.h +54 -190
  26. data/ext/ruby_prof/version.h +6 -3
  27. data/lib/1.8/ruby_prof.so +0 -0
  28. data/lib/1.9/ruby_prof.exp +0 -0
  29. data/lib/1.9/ruby_prof.ilk +0 -0
  30. data/lib/1.9/ruby_prof.lib +0 -0
  31. data/lib/1.9/ruby_prof.pdb +0 -0
  32. data/lib/1.9/ruby_prof.so +0 -0
  33. data/lib/ruby-prof.rb +14 -11
  34. data/lib/ruby-prof/abstract_printer.rb +10 -0
  35. data/lib/ruby-prof/aggregate_call_info.rb +2 -0
  36. data/lib/ruby-prof/call_info.rb +2 -0
  37. data/lib/ruby-prof/call_stack_printer.rb +2 -4
  38. data/lib/ruby-prof/call_tree_printer.rb +1 -0
  39. data/lib/ruby-prof/compatibility.rb +134 -0
  40. data/lib/ruby-prof/dot_printer.rb +7 -7
  41. data/lib/ruby-prof/flat_printer.rb +7 -7
  42. data/lib/ruby-prof/flat_printer_with_line_numbers.rb +2 -5
  43. data/lib/ruby-prof/graph_html_printer.rb +4 -2
  44. data/lib/ruby-prof/graph_printer.rb +4 -3
  45. data/lib/ruby-prof/method_info.rb +2 -0
  46. data/lib/ruby-prof/multi_printer.rb +2 -0
  47. data/lib/ruby-prof/{result.rb → profile.rb} +3 -1
  48. data/lib/ruby-prof/rack.rb +1 -0
  49. data/lib/ruby-prof/symbol_to_proc.rb +2 -0
  50. data/lib/ruby-prof/task.rb +1 -0
  51. data/lib/ruby-prof/test.rb +2 -0
  52. data/lib/ruby_prof.exp +0 -0
  53. data/lib/ruby_prof.ilk +0 -0
  54. data/lib/ruby_prof.lib +0 -0
  55. data/lib/ruby_prof.pdb +0 -0
  56. data/lib/ruby_prof.so +0 -0
  57. data/lib/unprof.rb +2 -0
  58. data/test/aggregate_test.rb +8 -8
  59. data/test/basic_test.rb +3 -251
  60. data/test/bug_test.rb +6 -0
  61. data/test/duplicate_names_test.rb +2 -2
  62. data/test/dynamic_method_test.rb +61 -0
  63. data/test/enumerable_test.rb +2 -2
  64. data/test/exceptions_test.rb +4 -3
  65. data/test/exclude_threads_test.rb +2 -2
  66. data/test/exec_test.rb +3 -3
  67. data/test/line_number_test.rb +5 -5
  68. data/test/measure_allocations_test.rb +25 -0
  69. data/test/measure_cpu_time_test.rb +212 -0
  70. data/test/measure_gc_runs_test.rb +29 -0
  71. data/test/measure_gc_time_test.rb +29 -0
  72. data/test/measure_memory_test.rb +36 -0
  73. data/test/measure_process_time_test.rb +205 -0
  74. data/test/measure_wall_time_test.rb +209 -0
  75. data/test/method_elimination_test.rb +2 -2
  76. data/test/module_test.rb +3 -2
  77. data/test/multi_printer_test.rb +2 -2
  78. data/test/no_method_class_test.rb +3 -1
  79. data/test/prime_test.rb +3 -3
  80. data/test/printers_test.rb +106 -8
  81. data/test/recursive_test.rb +7 -6
  82. data/test/singleton_test.rb +2 -2
  83. data/test/stack_printer_test.rb +2 -3
  84. data/test/stack_test.rb +2 -2
  85. data/test/start_stop_test.rb +2 -2
  86. data/test/test_helper.rb +81 -0
  87. data/test/test_suite.rb +34 -29
  88. data/test/thread_test.rb +24 -23
  89. data/test/unique_call_path_test.rb +2 -2
  90. metadata +101 -69
  91. data/ext/ruby_prof/measure_allocations.h +0 -83
  92. data/ext/ruby_prof/measure_cpu_time.h +0 -152
  93. data/ext/ruby_prof/measure_gc_runs.h +0 -76
  94. data/ext/ruby_prof/measure_gc_time.h +0 -57
  95. data/ext/ruby_prof/measure_memory.h +0 -101
  96. data/ext/ruby_prof/measure_process_time.h +0 -63
  97. data/ext/ruby_prof/measure_wall_time.h +0 -53
  98. data/ext/ruby_prof/mingw/Rakefile +0 -23
  99. data/ext/ruby_prof/mingw/build.rake +0 -38
  100. data/rails/environment/profile.rb +0 -24
  101. data/rails/example/example_test.rb +0 -9
  102. data/rails/profile_test_helper.rb +0 -21
  103. data/test/current_failures_windows +0 -8
  104. data/test/measurement_test.rb +0 -132
  105. data/test/ruby-prof-bin +0 -20
@@ -0,0 +1,55 @@
1
+ /* Copyright (C) 2005-2011 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #ifndef __RP_METHOD_INFO__
5
+ #define __RP_METHOD_INFO__
6
+
7
+ #include <ruby.h>
8
+
9
+ #ifndef RUBY_VM
10
+ #include <st.h>
11
+ typedef st_data_t st_index_t;
12
+ #endif
13
+
14
+ extern VALUE cMethodInfo;
15
+
16
+ /* A key used to identify each method */
17
+ typedef struct
18
+ {
19
+ VALUE klass; /* The method's class. */
20
+ ID mid; /* The method id. */
21
+ int depth; /* The recursion depth. */
22
+ st_index_t key; /* Cache calculated key */
23
+ } prof_method_key_t;
24
+
25
+ /* Forward declaration, see rp_call_info.h */
26
+ struct prof_call_infos_t;
27
+
28
+ /* Profiling information for each method. */
29
+ typedef struct
30
+ {
31
+ prof_method_key_t *key; /* Method key */
32
+ const char *source_file; /* The method's source file */
33
+ int line; /* The method's line number. */
34
+ struct prof_call_infos_t *call_infos; /* Call info objects for this method */
35
+ VALUE object; /* Cached ruby object */
36
+ } prof_method_t;
37
+
38
+ void rp_init_method_info(void);
39
+
40
+ st_table * method_table_create();
41
+ prof_method_t * method_table_lookup(st_table *table, const prof_method_key_t* key);
42
+ size_t method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val);
43
+ void method_table_free(st_table *table);
44
+
45
+ prof_method_t* prof_method_create(prof_method_key_t *key, const char* source_file, int line);
46
+ VALUE prof_method_wrap(prof_method_t *result);
47
+ void prof_method_mark(prof_method_t *method);
48
+
49
+ /* Setup infrastructure to use method keys as hash comparisons */
50
+ int method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2);
51
+ st_index_t method_table_hash(prof_method_key_t *key);
52
+
53
+ extern struct st_hash_type type_method_hash;
54
+
55
+ #endif
@@ -0,0 +1,61 @@
1
+ /* Copyright (C) 2005-2011 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #include "rp_stack.h"
5
+
6
+ #define INITIAL_STACK_SIZE 8
7
+
8
+
9
+ /* Creates a stack of prof_frame_t to keep track
10
+ of timings for active methods. */
11
+ prof_stack_t *
12
+ stack_create()
13
+ {
14
+ prof_stack_t *stack = ALLOC(prof_stack_t);
15
+ stack->start = ALLOC_N(prof_frame_t, INITIAL_STACK_SIZE);
16
+ stack->ptr = stack->start;
17
+ stack->end = stack->start + INITIAL_STACK_SIZE;
18
+
19
+ return stack;
20
+ }
21
+
22
+ void
23
+ stack_free(prof_stack_t *stack)
24
+ {
25
+ xfree(stack->start);
26
+ xfree(stack);
27
+ }
28
+
29
+ prof_frame_t *
30
+ stack_push(prof_stack_t *stack)
31
+ {
32
+ /* Is there space on the stack? If not, double
33
+ its size. */
34
+ if (stack->ptr == stack->end )
35
+ {
36
+ size_t len = stack->ptr - stack->start;
37
+ size_t new_capacity = (stack->end - stack->start) * 2;
38
+ REALLOC_N(stack->start, prof_frame_t, new_capacity);
39
+ stack->ptr = stack->start + len;
40
+ stack->end = stack->start + new_capacity;
41
+ }
42
+ return stack->ptr++;
43
+ }
44
+
45
+ prof_frame_t *
46
+ stack_pop(prof_stack_t *stack)
47
+ {
48
+ if (stack->ptr == stack->start)
49
+ return NULL;
50
+ else
51
+ return --stack->ptr;
52
+ }
53
+
54
+ prof_frame_t *
55
+ stack_peek(prof_stack_t *stack)
56
+ {
57
+ if (stack->ptr == stack->start)
58
+ return NULL;
59
+ else
60
+ return stack->ptr - 1;
61
+ }
@@ -0,0 +1,40 @@
1
+ /* Copyright (C) 2005-2011 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #ifndef __RP_STACK__
5
+ #define __RP_STACK__
6
+
7
+ #include <ruby.h>
8
+
9
+ #include "rp_measure.h"
10
+ #include "rp_call_info.h"
11
+
12
+
13
+ /* Temporary object that maintains profiling information
14
+ for active methods - there is one per method.*/
15
+ typedef struct
16
+ {
17
+ /* Caching prof_method_t values significantly
18
+ increases performance. */
19
+ prof_call_info_t *call_info;
20
+ double start_time;
21
+ double wait_time;
22
+ double child_time;
23
+ unsigned int line;
24
+ } prof_frame_t;
25
+
26
+ /* Current stack of active methods.*/
27
+ typedef struct
28
+ {
29
+ prof_frame_t *start;
30
+ prof_frame_t *end;
31
+ prof_frame_t *ptr;
32
+ } prof_stack_t;
33
+
34
+ prof_stack_t * stack_create();
35
+ void stack_free(prof_stack_t *stack);
36
+ prof_frame_t * stack_push(prof_stack_t *stack);
37
+ prof_frame_t * stack_pop(prof_stack_t *stack);
38
+ prof_frame_t * stack_peek(prof_stack_t *stack);
39
+
40
+ #endif //__RP_STACK__
@@ -0,0 +1,113 @@
1
+ /* Copyright (C) 2005-2011 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #include "ruby_prof.h"
5
+
6
+ /* ====== thread_data_t ====== */
7
+ thread_data_t*
8
+ thread_data_create(double measure)
9
+ {
10
+ thread_data_t* result = ALLOC(thread_data_t);
11
+ result->stack = stack_create();
12
+ result->method_table = method_table_create();
13
+ result->last_switch = measure;
14
+ return result;
15
+ }
16
+
17
+ void
18
+ thread_data_free(thread_data_t* thread_data)
19
+ {
20
+ method_table_free(thread_data->method_table);
21
+ stack_free(thread_data->stack);
22
+ xfree(thread_data);
23
+ }
24
+
25
+
26
+ /* ====== Thread Table ====== */
27
+ /* The thread table is hash keyed on ruby thread_id that stores instances
28
+ of thread_data_t. */
29
+
30
+ st_table *
31
+ threads_table_create()
32
+ {
33
+ return st_init_numtable();
34
+ }
35
+
36
+ size_t
37
+ threads_table_insert(prof_profile_t* profile, VALUE thread, thread_data_t *thread_data)
38
+ {
39
+ /* Its too slow to key on the real thread id so just typecast thread instead. */
40
+ return st_insert(profile->threads_tbl, (st_data_t) thread, (st_data_t) thread_data);
41
+ }
42
+
43
+ thread_data_t *
44
+ threads_table_lookup(prof_profile_t* profile, VALUE thread_id)
45
+ {
46
+ thread_data_t* result;
47
+ st_data_t val;
48
+
49
+ /* Its too slow to key on the real thread id so just typecast thread instead. */
50
+ if (st_lookup(profile->threads_tbl, (st_data_t) thread_id, &val))
51
+ {
52
+ result = (thread_data_t *) val;
53
+ }
54
+ else
55
+ {
56
+ result = thread_data_create(profile->measurer->measure());
57
+ result->thread_id = thread_id;
58
+
59
+ /* Insert the table */
60
+ threads_table_insert(profile, thread_id, result);
61
+ }
62
+ return result;
63
+ }
64
+
65
+ int
66
+ free_thread_data(st_data_t key, st_data_t value, st_data_t dummy)
67
+ {
68
+ thread_data_free((thread_data_t*)value);
69
+ return ST_CONTINUE;
70
+ }
71
+
72
+ void
73
+ threads_table_free(st_table *table)
74
+ {
75
+ st_foreach(table, free_thread_data, 0);
76
+ st_free_table(table);
77
+ }
78
+
79
+ thread_data_t *
80
+ switch_thread(void* prof, VALUE thread_id)
81
+ {
82
+ prof_profile_t* profile = (prof_profile_t*)prof;
83
+ prof_frame_t *frame = NULL;
84
+ double wait_time = 0;
85
+
86
+ /* Get new thread information. */
87
+ thread_data_t *thread_data = threads_table_lookup(profile, thread_id);
88
+
89
+ /* How long has this thread been waiting? */
90
+ wait_time = profile->measurement - thread_data->last_switch;
91
+
92
+ thread_data->last_switch = profile->measurement; // XXXX a test that fails if this is 0
93
+
94
+ /* Get the frame at the top of the stack. This may represent
95
+ the current method (EVENT_LINE, EVENT_RETURN) or the
96
+ previous method (EVENT_CALL).*/
97
+ frame = stack_peek(thread_data->stack);
98
+
99
+ if (frame)
100
+ {
101
+ frame->wait_time += wait_time;
102
+ }
103
+
104
+ /* Save on the last thread the time of the context switch
105
+ and reset this thread's last context switch to 0.*/
106
+ if (profile->last_thread_data)
107
+ {
108
+ profile->last_thread_data->last_switch = profile->measurement;
109
+ }
110
+
111
+ profile->last_thread_data = thread_data;
112
+ return thread_data;
113
+ }
@@ -0,0 +1,20 @@
1
+ /* Copyright (C) 2005-2011 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #ifndef __RP_THREAD__
5
+ #define __RP_THREAD__
6
+
7
+ /* Profiling information for a thread. */
8
+ typedef struct
9
+ {
10
+ VALUE thread_id; /* Thread id */
11
+ st_table* method_table; /* Methods called in the thread */
12
+ prof_stack_t* stack; /* Active methods */
13
+ double last_switch; /* Point of last context switch */
14
+ } thread_data_t;
15
+
16
+ st_table * threads_table_create();
17
+ thread_data_t* switch_thread(void* prof, VALUE thread_id);
18
+ void threads_table_free(st_table *table);
19
+
20
+ #endif //__RP_THREAD__
@@ -1,918 +1,56 @@
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
- */
1
+ /* Copyright (C) 2005-2011 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
27
3
 
28
4
  /* ruby-prof tracks the time spent executing every method in ruby programming.
29
5
  The main players are:
30
6
 
31
- prof_result_t - Its one field, values, contains the overall results
7
+ profile_t - This represents 1 profile.
32
8
  thread_data_t - Stores data about a single thread.
33
9
  prof_stack_t - The method call stack in a particular thread
34
- prof_method_t - Profiling information for each method
10
+ prof_method_t - Profiling information about each method
35
11
  prof_call_info_t - Keeps track a method's callers and callees.
36
12
 
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.
13
+ The final result is an instance of a profile object which has a hash table of
14
+ thread_data_t, keyed on the thread id. Each thread in turn has a hash table
15
+ of prof_method_t, keyed on the method id. A hash table is used for quick
16
+ look up when doing a profile. However, it is exposed to Ruby as an array.
41
17
 
42
18
  Each prof_method_t has two hash tables, parent and children, of prof_call_info_t.
43
- These objects keep track of a method's callers (who called the method) and its
44
- callees (who the method called). These are keyed the method id, but once again,
45
- are exposed to Ruby as arrays. Each prof_call_into_t maintains a pointer to the
46
- caller or callee method, thereby making it easy to navigate through the call
47
- hierarchy in ruby - which is very helpful for creating call graphs.
48
- */
49
-
50
- #include "ruby_prof.h"
51
- #include <stdio.h>
52
- #include <assert.h>
53
-
54
- /* ================ Helper Functions =================*/
55
- static VALUE
56
- figure_singleton_name(VALUE klass)
57
- {
58
- VALUE result = Qnil;
59
-
60
- /* We have come across a singleton object. First
61
- figure out what it is attached to.*/
62
- VALUE attached = rb_iv_get(klass, "__attached__");
63
-
64
- /* Is this a singleton class acting as a metaclass? */
65
- if (BUILTIN_TYPE(attached) == T_CLASS)
66
- {
67
- result = rb_str_new2("<Class::");
68
- rb_str_append(result, rb_inspect(attached));
69
- rb_str_cat2(result, ">");
70
- }
71
-
72
- /* Is this for singleton methods on a module? */
73
- else if (BUILTIN_TYPE(attached) == T_MODULE)
74
- {
75
- result = rb_str_new2("<Module::");
76
- rb_str_append(result, rb_inspect(attached));
77
- rb_str_cat2(result, ">");
78
- }
79
-
80
- /* Is this for singleton methods on an object? */
81
- else if (BUILTIN_TYPE(attached) == T_OBJECT)
82
- {
83
- /* Make sure to get the super class so that we don't
84
- mistakenly grab a T_ICLASS which would lead to
85
- unknown method errors. */
86
- #ifdef HAVE_RB_CLASS_SUPERCLASS
87
- // 1.9.3
88
- VALUE super = rb_class_superclass(klass);
89
- #else
90
- # ifdef RCLASS_SUPER
91
- VALUE super = rb_class_real(RCLASS_SUPER(klass));
92
- # else
93
- VALUE super = rb_class_real(RCLASS(klass)->super);
94
- # endif
95
- #endif
96
- result = rb_str_new2("<Object::");
97
- rb_str_append(result, rb_inspect(super));
98
- rb_str_cat2(result, ">");
99
- }
100
-
101
- /* Ok, this could be other things like an array made put onto
102
- a singleton object (yeah, it happens, see the singleton
103
- objects test case). */
104
- else
105
- {
106
- result = rb_inspect(klass);
107
- }
108
-
109
- return result;
110
- }
111
-
112
- static VALUE
113
- klass_name(VALUE klass)
114
- {
115
- VALUE result = Qnil;
116
-
117
- if (klass == 0 || klass == Qnil)
118
- {
119
- result = rb_str_new2("Global");
120
- }
121
- else if (BUILTIN_TYPE(klass) == T_MODULE)
122
- {
123
- result = rb_inspect(klass);
124
- }
125
- else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
126
- {
127
- result = figure_singleton_name(klass);
128
- }
129
- else if (BUILTIN_TYPE(klass) == T_CLASS)
130
- {
131
- result = rb_inspect(klass);
132
- }
133
- else
134
- {
135
- /* Should never happen. */
136
- result = rb_str_new2("Unknown");
137
- }
138
-
139
- return result;
140
- }
141
-
142
- static VALUE
143
- method_name(ID mid)
144
- {
145
- VALUE result;
146
-
147
- if (mid == ID_ALLOCATOR)
148
- result = rb_str_new2("allocate");
149
- else if (mid == 0)
150
- result = rb_str_new2("[No method]");
151
- else
152
- result = rb_String(ID2SYM(mid));
153
-
154
- return result;
155
- }
156
-
157
- static VALUE
158
- full_name(VALUE klass, ID mid)
159
- {
160
- VALUE result = klass_name(klass);
161
- rb_str_cat2(result, "#");
162
- rb_str_append(result, method_name(mid));
163
-
164
- return result;
165
- }
166
-
167
- /* ================ Stack Handling =================*/
168
- /* Creates a stack of prof_frame_t to keep track
169
- of timings for active methods. */
170
- static prof_stack_t *
171
- stack_create()
172
- {
173
- prof_stack_t *stack = ALLOC(prof_stack_t);
174
- stack->start = ALLOC_N(prof_frame_t, INITIAL_STACK_SIZE);
175
- stack->ptr = stack->start;
176
- stack->end = stack->start + INITIAL_STACK_SIZE;
177
- return stack;
178
- }
179
-
180
- static void
181
- stack_free(prof_stack_t *stack)
182
- {
183
- xfree(stack->start);
184
- xfree(stack);
185
- }
186
-
187
- static prof_frame_t *
188
- stack_push(prof_stack_t *stack)
189
- {
190
- /* Is there space on the stack? If not, double
191
- its size. */
192
- if (stack->ptr == stack->end)
193
- {
194
- size_t len = stack->ptr - stack->start;
195
- size_t new_capacity = (stack->end - stack->start) * 2;
196
- REALLOC_N(stack->start, prof_frame_t, new_capacity);
197
- stack->ptr = stack->start + len;
198
- stack->end = stack->start + new_capacity;
199
- }
200
- return stack->ptr++;
201
- }
202
-
203
- static prof_frame_t *
204
- stack_pop(prof_stack_t *stack)
205
- {
206
- if (stack->ptr == stack->start)
207
- return NULL;
208
- else
209
- return --stack->ptr;
210
- }
211
-
212
- static prof_frame_t *
213
- stack_peek(prof_stack_t *stack)
214
- {
215
- if (stack->ptr == stack->start)
216
- return NULL;
217
- else
218
- return stack->ptr - 1;
219
- }
220
-
221
- /* ================ Method Key =================*/
222
- static int
223
- method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2)
224
- {
225
- return (key1->klass != key2->klass) || (key1->mid != key2->mid);
226
- }
227
-
228
- static st_index_t
229
- method_table_hash(prof_method_key_t *key)
230
- {
231
- return key->key;
232
- }
233
-
234
- static struct st_hash_type type_method_hash = {
235
- method_table_cmp,
236
- method_table_hash
237
- };
238
-
239
- static void
240
- method_key(prof_method_key_t* key, VALUE klass, ID mid)
241
- {
242
- key->klass = klass;
243
- key->mid = mid;
244
- key->key = (klass << 4) + (mid << 2);
245
- }
246
-
247
-
248
- /* ================ Call Info =================*/
249
- static st_table *
250
- call_info_table_create()
251
- {
252
- return st_init_table(&type_method_hash);
253
- }
254
-
255
- static size_t
256
- call_info_table_insert(st_table *table, const prof_method_key_t *key, prof_call_info_t *val)
257
- {
258
- return st_insert(table, (st_data_t) key, (st_data_t) val);
259
- }
260
-
261
- static prof_call_info_t *
262
- call_info_table_lookup(st_table *table, const prof_method_key_t *key)
263
- {
264
- st_data_t val;
265
- if (st_lookup(table, (st_data_t) key, &val))
266
- {
267
- return (prof_call_info_t *) val;
268
- }
269
- else
270
- {
271
- return NULL;
272
- }
273
- }
274
-
275
- static void
276
- call_info_table_free(st_table *table)
277
- {
278
- st_free_table(table);
279
- }
280
-
281
- /* Document-class: RubyProf::CallInfo
282
- RubyProf::CallInfo is a helper class used by RubyProf::MethodInfo
283
- to keep track of which child methods were called and how long
284
- they took to execute. */
285
-
286
- /* :nodoc: */
287
- static prof_call_info_t *
288
- prof_call_info_create(prof_method_t* method, prof_call_info_t* parent)
289
- {
290
- prof_call_info_t *result = ALLOC(prof_call_info_t);
291
- result->object = Qnil;
292
- result->target = method;
293
- result->parent = parent;
294
- result->call_infos = call_info_table_create();
295
- result->children = Qnil;
296
-
297
- result->called = 0;
298
- result->total_time = 0;
299
- result->self_time = 0;
300
- result->wait_time = 0;
301
- result->line = 0;
302
- return result;
303
- }
304
-
305
- static void prof_method_mark(prof_method_t *method);
306
-
307
- static void
308
- prof_call_info_mark(prof_call_info_t *call_info)
309
- {
310
- {
311
- VALUE target = call_info->target->object;
312
- if (NIL_P(target))
313
- prof_method_mark(call_info->target);
314
- else
315
- rb_gc_mark(target);
316
- }
317
- rb_gc_mark(call_info->children);
318
- if (call_info->parent) {
319
- VALUE parent = call_info->parent->object;
320
- if (NIL_P(parent)) {
321
- prof_call_info_mark(call_info->parent);
322
- }
323
- else {
324
- rb_gc_mark(parent);
325
- }
326
- }
327
- }
328
-
329
- static void
330
- prof_call_info_free(prof_call_info_t *call_info)
331
- {
332
- call_info_table_free(call_info->call_infos);
333
- xfree(call_info);
334
- }
335
-
336
- static VALUE
337
- prof_call_info_wrap(prof_call_info_t *call_info)
338
- {
339
- if (call_info->object == Qnil)
340
- {
341
- call_info->object = Data_Wrap_Struct(cCallInfo, prof_call_info_mark, prof_call_info_free, call_info);
342
- }
343
- return call_info->object;
344
- }
345
-
346
- static prof_call_info_t *
347
- prof_get_call_info_result(VALUE obj)
348
- {
349
- if (BUILTIN_TYPE(obj) != T_DATA)
350
- {
351
- /* Should never happen */
352
- rb_raise(rb_eTypeError, "Not a call info object");
353
- }
354
- return (prof_call_info_t *) DATA_PTR(obj);
355
- }
356
-
357
-
358
- /* call-seq:
359
- called -> MethodInfo
360
-
361
- Returns the target method. */
362
- static VALUE
363
- prof_call_info_target(VALUE self)
364
- {
365
- /* Target is a pointer to a method_info - so we have to be careful
366
- about the GC. We will wrap the method_info but provide no
367
- free method so the underlying object is not freed twice! */
368
-
369
- prof_call_info_t *result = prof_get_call_info_result(self);
370
- return prof_method_wrap(result->target);
371
- }
372
-
373
- /* call-seq:
374
- called -> int
375
-
376
- Returns the total amount of times this method was called. */
377
- static VALUE
378
- prof_call_info_called(VALUE self)
379
- {
380
- prof_call_info_t *result = prof_get_call_info_result(self);
381
- return INT2NUM(result->called);
382
- }
383
-
384
- /* call-seq:
385
- called=n -> n
386
-
387
- Sets the call count to n. */
388
- static VALUE
389
- prof_call_info_set_called(VALUE self, VALUE called)
390
- {
391
- prof_call_info_t *result = prof_get_call_info_result(self);
392
- result->called = NUM2INT(called);
393
- return called;
394
- }
395
-
396
- /* call-seq:
397
- line_no -> int
398
-
399
- returns the line number of the method */
400
- static VALUE
401
- prof_call_info_line(VALUE self)
402
- {
403
- prof_call_info_t *result = prof_get_call_info_result(self);
404
- return rb_int_new(result->line);
405
- }
406
-
407
- /* call-seq:
408
- total_time -> float
409
-
410
- Returns the total amount of time spent in this method and its children. */
411
- static VALUE
412
- prof_call_info_total_time(VALUE self)
413
- {
414
- prof_call_info_t *result = prof_get_call_info_result(self);
415
- return rb_float_new(convert_measurement(result->total_time));
416
- }
417
-
418
- /* call-seq:
419
- add_total_time(call_info) -> nil
420
-
421
- adds total time time from call_info to self. */
422
- static VALUE
423
- prof_call_info_add_total_time(VALUE self, VALUE other)
424
- {
425
- prof_call_info_t *result = prof_get_call_info_result(self);
426
- prof_call_info_t *other_info = prof_get_call_info_result(other);
427
-
428
- result->total_time += other_info->total_time;
429
- return Qnil;
430
- }
431
-
432
- /* call-seq:
433
- self_time -> float
434
-
435
- Returns the total amount of time spent in this method. */
436
- static VALUE
437
- prof_call_info_self_time(VALUE self)
438
- {
439
- prof_call_info_t *result = prof_get_call_info_result(self);
440
-
441
- return rb_float_new(convert_measurement(result->self_time));
442
- }
443
-
444
- /* call-seq:
445
- add_self_time(call_info) -> nil
446
-
447
- adds self time from call_info to self. */
448
- static VALUE
449
- prof_call_info_add_self_time(VALUE self, VALUE other)
450
- {
451
- prof_call_info_t *result = prof_get_call_info_result(self);
452
- prof_call_info_t *other_info = prof_get_call_info_result(other);
453
-
454
- result->self_time += other_info->self_time;
455
- return Qnil;
456
- }
457
-
458
- /* call-seq:
459
- wait_time -> float
460
-
461
- Returns the total amount of time this method waited for other threads. */
462
- static VALUE
463
- prof_call_info_wait_time(VALUE self)
464
- {
465
- prof_call_info_t *result = prof_get_call_info_result(self);
466
-
467
- return rb_float_new(convert_measurement(result->wait_time));
468
- }
469
-
470
- /* call-seq:
471
- add_wait_time(call_info) -> nil
472
-
473
- adds wait time from call_info to self. */
474
-
475
- static VALUE
476
- prof_call_info_add_wait_time(VALUE self, VALUE other)
477
- {
478
- prof_call_info_t *result = prof_get_call_info_result(self);
479
- prof_call_info_t *other_info = prof_get_call_info_result(other);
480
-
481
- result->wait_time += other_info->wait_time;
482
- return Qnil;
483
- }
484
-
485
- /* call-seq:
486
- parent -> call_info
487
-
488
- Returns the call_infos parent call_info object (the method that called this method).*/
489
- static VALUE
490
- prof_call_info_parent(VALUE self)
491
- {
492
- prof_call_info_t *result = prof_get_call_info_result(self);
493
- if (result->parent)
494
- return prof_call_info_wrap(result->parent);
495
- else
496
- return Qnil;
497
- }
498
-
499
- /* call-seq:
500
- parent=new_parent -> new_parent
501
-
502
- Changes the parent of self to new_parent and returns it.*/
503
- static VALUE
504
- prof_call_info_set_parent(VALUE self, VALUE new_parent)
505
- {
506
- prof_call_info_t *result = prof_get_call_info_result(self);
507
- if (new_parent == Qnil)
508
- result->parent = NULL;
509
- else
510
- result->parent = prof_get_call_info_result(new_parent);
511
- return prof_call_info_parent(self);
512
- }
513
-
514
- static int
515
- prof_call_info_collect_children(st_data_t key, st_data_t value, st_data_t result)
516
- {
517
- prof_call_info_t *call_info = (prof_call_info_t *) value;
518
- VALUE arr = (VALUE) result;
519
- rb_ary_push(arr, prof_call_info_wrap(call_info));
520
- return ST_CONTINUE;
521
- }
522
-
523
- /* call-seq:
524
- children -> hash
525
-
526
- Returns an array of call info objects of methods that this method
527
- called (ie, children).*/
528
- static VALUE
529
- prof_call_info_children(VALUE self)
530
- {
531
- prof_call_info_t *call_info = prof_get_call_info_result(self);
532
- if (call_info->children == Qnil)
533
- {
534
- call_info->children = rb_ary_new();
535
- st_foreach(call_info->call_infos, prof_call_info_collect_children, call_info->children);
536
- }
537
- return call_info->children;
538
- }
539
-
540
- /* ================ Call Infos =================*/
541
- static prof_call_infos_t*
542
- prof_call_infos_create()
543
- {
544
- prof_call_infos_t *result = ALLOC(prof_call_infos_t);
545
- result->start = ALLOC_N(prof_call_info_t*, INITIAL_CALL_INFOS_SIZE);
546
- result->end = result->start + INITIAL_CALL_INFOS_SIZE;
547
- result->ptr = result->start;
548
- result->object = Qnil;
549
- return result;
550
- }
551
-
552
- static void
553
- prof_call_infos_free(prof_call_infos_t *call_infos)
554
- {
555
- xfree(call_infos->start);
556
- xfree(call_infos);
557
- }
558
-
559
- static void
560
- prof_add_call_info(prof_call_infos_t *call_infos, prof_call_info_t *call_info)
561
- {
562
- if (call_infos->ptr == call_infos->end)
563
- {
564
- size_t len = call_infos->ptr - call_infos->start;
565
- size_t new_capacity = (call_infos->end - call_infos->start) * 2;
566
- REALLOC_N(call_infos->start, prof_call_info_t*, new_capacity);
567
- call_infos->ptr = call_infos->start + len;
568
- call_infos->end = call_infos->start + new_capacity;
569
- }
570
- *call_infos->ptr = call_info;
571
- call_infos->ptr++;
572
- }
573
-
574
- static VALUE
575
- prof_call_infos_wrap(prof_call_infos_t *call_infos)
576
- {
577
- if (call_infos->object == Qnil)
578
- {
579
- prof_call_info_t **i;
580
- call_infos->object = rb_ary_new();
581
- for(i=call_infos->start; i<call_infos->ptr; i++)
582
- {
583
- VALUE call_info = prof_call_info_wrap(*i);
584
- rb_ary_push(call_infos->object, call_info);
585
- }
586
- }
587
- return call_infos->object;
588
- }
589
-
590
-
591
- /* ================ Method Info =================*/
592
- /* Document-class: RubyProf::MethodInfo
593
- The RubyProf::MethodInfo class stores profiling data for a method.
594
- One instance of the RubyProf::MethodInfo class is created per method
595
- called per thread. Thus, if a method is called in two different
596
- thread then there will be two RubyProf::MethodInfo objects
597
- created. RubyProf::MethodInfo objects can be accessed via
598
- the RubyProf::Result object.
599
- */
600
-
601
- static prof_method_t*
602
- prof_method_create(prof_method_key_t *key, const char* source_file, int line)
603
- {
604
- prof_method_t *result = ALLOC(prof_method_t);
605
- result->object = Qnil;
606
- result->key = ALLOC(prof_method_key_t);
607
- method_key(result->key, key->klass, key->mid);
608
-
609
- result->call_infos = prof_call_infos_create();
610
-
611
- if (source_file != NULL)
612
- {
613
- size_t len = strlen(source_file) + 1;
614
- char *buffer = ALLOC_N(char, len);
615
-
616
- MEMCPY(buffer, source_file, char, len);
617
- result->source_file = buffer;
618
- }
619
- else
620
- {
621
- result->source_file = source_file;
622
- }
623
- result->line = line;
624
-
625
- return result;
626
- }
627
-
628
- static void
629
- prof_method_mark(prof_method_t *method)
630
- {
631
- rb_gc_mark(method->call_infos->object);
632
- rb_gc_mark(method->key->klass);
633
- }
634
-
635
- static void
636
- prof_method_free(prof_method_t *method)
637
- {
638
- if (method->source_file)
639
- {
640
- xfree((char*)method->source_file);
641
- }
642
-
643
- prof_call_infos_free(method->call_infos);
644
- xfree(method->key);
645
- xfree(method);
646
- }
647
-
648
- static VALUE
649
- prof_method_wrap(prof_method_t *result)
650
- {
651
- if (result->object == Qnil)
652
- {
653
- result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_free, result);
654
- }
655
- return result->object;
656
- }
657
-
658
- static prof_method_t *
659
- get_prof_method(VALUE obj)
660
- {
661
- return (prof_method_t *) DATA_PTR(obj);
662
- }
663
-
664
- /* call-seq:
665
- line_no -> int
666
-
667
- returns the line number of the method */
668
- static VALUE
669
- prof_method_line(VALUE self)
670
- {
671
- return rb_int_new(get_prof_method(self)->line);
672
- }
673
-
674
- /* call-seq:
675
- source_file => string
676
-
677
- return the source file of the method
678
- */
679
- static VALUE prof_method_source_file(VALUE self)
680
- {
681
- const char* sf = get_prof_method(self)->source_file;
682
- if(!sf)
683
- {
684
- return rb_str_new2("ruby_runtime");
685
- }
686
- else
687
- {
688
- return rb_str_new2(sf);
689
- }
690
- }
691
-
692
-
693
- /* call-seq:
694
- method_class -> klass
695
-
696
- Returns the Ruby klass that owns this method. */
697
- static VALUE
698
- prof_method_klass(VALUE self)
699
- {
700
- prof_method_t *result = get_prof_method(self);
701
- return result->key->klass;
702
- }
703
-
704
- /* call-seq:
705
- method_id -> ID
706
-
707
- Returns the id of this method. */
708
- static VALUE
709
- prof_method_id(VALUE self)
710
- {
711
- prof_method_t *result = get_prof_method(self);
712
- return ID2SYM(result->key->mid);
713
- }
714
-
715
- /* call-seq:
716
- klass_name -> string
717
-
718
- Returns the name of this method's class. Singleton classes
719
- will have the form <Object::Object>. */
720
-
721
- static VALUE
722
- prof_klass_name(VALUE self)
723
- {
724
- prof_method_t *method = get_prof_method(self);
725
- return klass_name(method->key->klass);
726
- }
727
-
728
- /* call-seq:
729
- method_name -> string
730
-
731
- Returns the name of this method in the format Object#method. Singletons
732
- methods will be returned in the format <Object::Object>#method.*/
733
-
734
- static VALUE
735
- prof_method_name(VALUE self)
736
- {
737
- prof_method_t *method = get_prof_method(self);
738
- return method_name(method->key->mid);
739
- }
740
-
741
- /* call-seq:
742
- full_name -> string
743
-
744
- Returns the full name of this method in the format Object#method.*/
745
-
746
- static VALUE
747
- prof_full_name(VALUE self)
748
- {
749
- prof_method_t *method = get_prof_method(self);
750
- return full_name(method->key->klass, method->key->mid);
751
- }
752
-
753
- /* call-seq:
754
- call_infos -> Array of call_info
755
-
756
- Returns an array of call info objects that contain profiling information
757
- about the current method.*/
758
- static VALUE
759
- prof_method_call_infos(VALUE self)
760
- {
761
- prof_method_t *method = get_prof_method(self);
762
- return prof_call_infos_wrap(method->call_infos);
763
- }
764
-
765
- static int
766
- collect_methods(st_data_t key, st_data_t value, st_data_t result)
767
- {
768
- /* Called for each method stored in a thread's method table.
769
- We want to store the method info information into an array.*/
770
- VALUE methods = (VALUE) result;
771
- prof_method_t *method = (prof_method_t *) value;
772
- rb_ary_push(methods, prof_method_wrap(method));
773
-
774
- /* Wrap call info objects */
775
- prof_call_infos_wrap(method->call_infos);
776
-
777
- return ST_CONTINUE;
778
- }
779
-
780
- /* ================ Method Table =================*/
781
- static st_table *
782
- method_table_create()
783
- {
784
- return st_init_table(&type_method_hash);
785
- }
786
-
787
- static size_t
788
- method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
789
- {
790
- return st_insert(table, (st_data_t) key, (st_data_t) val);
791
- }
792
-
793
- static prof_method_t *
794
- method_table_lookup(st_table *table, const prof_method_key_t* key)
795
- {
796
- st_data_t val;
797
- if (st_lookup(table, (st_data_t)key, &val))
798
- {
799
- return (prof_method_t *) val;
800
- }
801
- else
802
- {
803
- return NULL;
804
- }
805
- }
806
-
807
-
808
- static void
809
- method_table_free(st_table *table)
810
- {
811
- /* Don't free the contents since they are wrapped by
812
- Ruby objects! */
813
- st_free_table(table);
814
- }
815
-
816
-
817
- /* ================ Thread Handling =================*/
818
-
819
- /* ---- Keeps track of thread's stack and methods ---- */
820
- static thread_data_t*
821
- thread_data_create()
822
- {
823
- thread_data_t* result = ALLOC(thread_data_t);
824
- result->stack = stack_create();
825
- result->method_table = method_table_create();
826
- result->last_switch = get_measurement();
827
- return result;
828
- }
829
-
830
- static void
831
- thread_data_free(thread_data_t* thread_data)
832
- {
833
- method_table_free(thread_data->method_table);
834
- stack_free(thread_data->stack);
835
- xfree(thread_data);
836
- }
837
-
838
- /* ---- Hash, keyed on thread, that stores thread's stack
839
- and methods---- */
840
-
841
- static st_table *
842
- threads_table_create()
843
- {
844
- return st_init_numtable();
845
- }
846
-
847
- static size_t
848
- threads_table_insert(st_table *table, VALUE thread, thread_data_t *thread_data)
849
- {
850
- /* Its too slow to key on the real thread id so just typecast thread instead. */
851
- return st_insert(table, (st_data_t) thread, (st_data_t) thread_data);
852
- }
853
-
854
- static thread_data_t *
855
- threads_table_lookup(st_table *table, VALUE thread_id)
856
- {
857
- thread_data_t* result;
858
- st_data_t val;
859
-
860
- /* Its too slow to key on the real thread id so just typecast thread instead. */
861
- if (st_lookup(table, (st_data_t) thread_id, &val))
862
- {
863
- result = (thread_data_t *) val;
864
- }
865
- else
866
- {
867
- result = thread_data_create();
868
- result->thread_id = thread_id;
869
-
870
- /* Insert the table */
871
- threads_table_insert(threads_tbl, thread_id, result);
872
- }
873
- return result;
874
- }
875
-
876
- static int
877
- free_thread_data(st_data_t key, st_data_t value, st_data_t dummy)
878
- {
879
- thread_data_free((thread_data_t*)value);
880
- return ST_CONTINUE;
881
- }
882
-
883
-
884
- static void
885
- threads_table_free(st_table *table)
886
- {
887
- st_foreach(table, free_thread_data, 0);
888
- st_free_table(table);
889
- }
890
-
891
-
892
- static int
893
- collect_threads(st_data_t key, st_data_t value, st_data_t result)
894
- {
895
- /* Although threads are keyed on an id, that is actually a
896
- pointer to the VALUE object of the thread. So its bogus.
897
- However, in thread_data is the real thread id stored
898
- as an int. */
899
- thread_data_t* thread_data = (thread_data_t*) value;
900
- VALUE threads_hash = (VALUE) result;
19
+ These objects keep track of a method's callers (who called the method) and its
20
+ callees (who the method called). These are keyed the method id, but once again,
21
+ are exposed to Ruby as arrays. Each prof_call_into_t maintains a pointer to the
22
+ caller or callee method, thereby making it easy to navigate through the call
23
+ hierarchy in ruby - which is very helpful for creating call graphs.
24
+ */
901
25
 
902
- VALUE methods = rb_ary_new();
26
+ #include "ruby_prof.h"
27
+ #include <stdio.h>
28
+ #include <assert.h>
903
29
 
904
- /* Now collect an array of all the called methods */
905
- st_table* method_table = thread_data->method_table;
906
- st_foreach(method_table, collect_methods, methods);
30
+ VALUE mProf;
31
+ VALUE cProfile;
907
32
 
908
- /* Store the results in the threads hash keyed on the thread id. */
909
- rb_hash_aset(threads_hash, thread_data->thread_id, methods);
33
+ #ifndef RUBY_VM
34
+ /* Global variable to hold current profile - needed
35
+ prior to Ruby 1.9 */
36
+ static prof_profile_t* pCurrentProfile;
37
+ #endif
910
38
 
911
- return ST_CONTINUE;
39
+ static prof_profile_t*
40
+ prof_get_profile(VALUE self)
41
+ {
42
+ /* Can't use Data_Get_Struct because that triggers the event hook
43
+ ending up in endless recursion. */
44
+ return (prof_profile_t*)RDATA(self)->data;
912
45
  }
913
46
 
914
-
915
- /* ================ Profiling =================*/
47
+ void
48
+ method_key(prof_method_key_t* key, VALUE klass, ID mid)
49
+ {
50
+ key->klass = klass;
51
+ key->mid = mid;
52
+ key->key = (klass << 4) + (mid << 2);
53
+ }
916
54
 
917
55
  /* support tracing ruby events from ruby-prof. useful for getting at
918
56
  what actually happens inside the ruby interpreter (and ruby-prof).
@@ -986,11 +124,11 @@ static prof_method_t*
986
124
  }
987
125
 
988
126
  static void
989
- update_result(prof_measure_t total_time,
127
+ update_result(double total_time,
990
128
  prof_frame_t *parent_frame,
991
129
  prof_frame_t *frame)
992
130
  {
993
- prof_measure_t self_time = total_time - frame->child_time - frame->wait_time;
131
+ double self_time = total_time - frame->child_time - frame->wait_time;
994
132
  prof_call_info_t *call_info = frame->call_info;
995
133
 
996
134
  /* Update information about the current method */
@@ -1004,44 +142,12 @@ update_result(prof_measure_t total_time,
1004
142
  call_info->line = parent_frame->line;
1005
143
  }
1006
144
 
1007
- static thread_data_t *
1008
- switch_thread(VALUE thread_id, prof_measure_t now)
1009
- {
1010
- prof_frame_t *frame = NULL;
1011
- prof_measure_t wait_time = 0;
1012
- /* Get new thread information. */
1013
- thread_data_t *thread_data = threads_table_lookup(threads_tbl, thread_id);
1014
-
1015
- /* How long has this thread been waiting? */
1016
- wait_time = now - thread_data->last_switch;
1017
-
1018
- thread_data->last_switch = now; // XXXX a test that fails if this is 0
1019
-
1020
- /* Get the frame at the top of the stack. This may represent
1021
- the current method (EVENT_LINE, EVENT_RETURN) or the
1022
- previous method (EVENT_CALL).*/
1023
- frame = stack_peek(thread_data->stack);
1024
-
1025
- if (frame) {
1026
- frame->wait_time += wait_time;
1027
- }
1028
-
1029
- /* Save on the last thread the time of the context switch
1030
- and reset this thread's last context switch to 0.*/
1031
- if (last_thread_data) {
1032
- last_thread_data->last_switch = now;
1033
- }
1034
-
1035
- last_thread_data = thread_data;
1036
- return thread_data;
1037
- }
1038
-
1039
145
  static prof_frame_t*
1040
- pop_frame(thread_data_t *thread_data, prof_measure_t now)
146
+ pop_frame(prof_profile_t* profile, thread_data_t *thread_data)
1041
147
  {
1042
148
  prof_frame_t *frame = NULL;
1043
149
  prof_frame_t* parent_frame = NULL;
1044
- prof_measure_t total_time;
150
+ double total_time;
1045
151
 
1046
152
  frame = stack_pop(thread_data->stack); // only time it's called
1047
153
  /* Frame can be null. This can happen if RubProf.start is called from
@@ -1051,7 +157,7 @@ pop_frame(thread_data_t *thread_data, prof_measure_t now)
1051
157
  if (frame == NULL) return NULL;
1052
158
 
1053
159
  /* Calculate the total time this method took */
1054
- total_time = now - frame->start_time;
160
+ total_time = profile->measurement - frame->start_time;
1055
161
 
1056
162
  parent_frame = stack_peek(thread_data->stack);
1057
163
  if (parent_frame)
@@ -1064,18 +170,18 @@ pop_frame(thread_data_t *thread_data, prof_measure_t now)
1064
170
  }
1065
171
 
1066
172
  static int
1067
- pop_frames(st_data_t key, st_data_t value, st_data_t now_arg)
173
+ pop_frames(st_data_t key, st_data_t value, st_data_t data)
1068
174
  {
1069
175
  VALUE thread_id = (VALUE)key;
1070
176
  thread_data_t* thread_data = (thread_data_t *) value;
1071
- prof_measure_t now = *(prof_measure_t *) now_arg;
177
+ prof_profile_t* profile = (prof_profile_t*) data;
1072
178
 
1073
- if (!last_thread_data || last_thread_data->thread_id != thread_id)
1074
- thread_data = switch_thread(thread_id, now);
179
+ if (!profile->last_thread_data || profile->last_thread_data->thread_id != thread_id)
180
+ thread_data = switch_thread(profile, thread_id);
1075
181
  else
1076
- thread_data = last_thread_data;
182
+ thread_data = profile->last_thread_data;
1077
183
 
1078
- while (pop_frame(thread_data, now))
184
+ while (pop_frame(profile, thread_data))
1079
185
  {
1080
186
  }
1081
187
 
@@ -1083,25 +189,11 @@ pop_frames(st_data_t key, st_data_t value, st_data_t now_arg)
1083
189
  }
1084
190
 
1085
191
  static void
1086
- prof_pop_threads(prof_measure_t now)
192
+ prof_pop_threads(prof_profile_t* profile)
1087
193
  {
1088
- st_foreach(threads_tbl, pop_frames, (st_data_t) &now);
194
+ st_foreach(profile->threads_tbl, pop_frames, (st_data_t) profile);
1089
195
  }
1090
196
 
1091
- #if RUBY_VERSION == 190
1092
- # error 1.9.0 not supported (ask for it if you desire it to be supported)
1093
- #endif
1094
-
1095
- #if RUBY_VERSION >= 191
1096
-
1097
- /* Avoid bugs in 1.9.1 */
1098
-
1099
- 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);
1100
- void prof_install_hook();
1101
- void prof_remove_hook();
1102
-
1103
- #endif
1104
-
1105
197
  #ifdef RUBY_VM
1106
198
  static void
1107
199
  prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
@@ -1110,9 +202,14 @@ static void
1110
202
  prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE klass)
1111
203
  #endif
1112
204
  {
1113
- VALUE thread = Qnil;
205
+ #ifndef RUBY_VM
206
+ prof_profile_t* profile = pCurrentProfile;
207
+ #else
208
+ prof_profile_t* profile = prof_get_profile(data);
209
+ #endif
210
+
211
+ VALUE thread = Qnil;
1114
212
  VALUE thread_id = Qnil;
1115
- prof_measure_t now = 0;
1116
213
  thread_data_t* thread_data = NULL;
1117
214
  prof_frame_t *frame = NULL;
1118
215
 
@@ -1123,8 +220,8 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1123
220
  }
1124
221
  #endif
1125
222
 
1126
- /* Get current timestamp */
1127
- now = get_measurement();
223
+ /* Get current measurement */
224
+ profile->measurement = profile->measurer->measure();
1128
225
 
1129
226
  if (trace_file != NULL)
1130
227
  {
@@ -1149,38 +246,30 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1149
246
  }
1150
247
 
1151
248
  fprintf(trace_file, "%2u:%2ums %-8s %s:%2d %s#%s\n",
1152
- (unsigned int) thread_id, (unsigned int) now, event_name, source_file, source_line, class_name, method_name);
249
+ (unsigned int) thread_id, (unsigned int) profile->measurement, event_name, source_file, source_line, class_name, method_name);
1153
250
  /* fflush(trace_file); */
1154
251
  last_thread_id = thread_id;
1155
252
  }
1156
253
 
1157
254
  /* Special case - skip any methods from the mProf
1158
- module, such as Prof.stop, since they clutter
255
+ module or cProfile class since they clutter
1159
256
  the results but aren't important to them results. */
1160
- if (self == mProf) return;
257
+ if (self == mProf || klass == cProfile) return;
1161
258
 
1162
259
  /* Get the current thread information. */
1163
260
  thread = rb_thread_current();
1164
261
  thread_id = rb_obj_id(thread);
1165
262
 
1166
- # if RUBY_VERSION >= 191 && THREADS_INHERIT_EVENT_FLAGS==0
1167
- /* ensure that new threads are hooked [sigh] (bug in core) */
1168
- prof_remove_hook();
1169
- prof_install_hook();
1170
- # endif
1171
-
1172
- if (exclude_threads_tbl &&
1173
- st_lookup(exclude_threads_tbl, (st_data_t) thread_id, 0))
263
+ if (st_lookup(profile->exclude_threads_tbl, (st_data_t) thread_id, 0))
1174
264
  {
1175
265
  return;
1176
266
  }
1177
267
 
1178
-
1179
268
  /* Was there a context switch? */
1180
- if (!last_thread_data || last_thread_data->thread_id != thread_id)
1181
- thread_data = switch_thread(thread_id, now);
269
+ if (!profile->last_thread_data || profile->last_thread_data->thread_id != thread_id)
270
+ thread_data = switch_thread(profile, thread_id);
1182
271
  else
1183
- thread_data = last_thread_data;
272
+ thread_data = profile->last_thread_data;
1184
273
 
1185
274
 
1186
275
  switch (event) {
@@ -1196,13 +285,6 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1196
285
  if (frame)
1197
286
  {
1198
287
  frame->line = rb_sourceline();
1199
-
1200
- # if RUBY_VERSION >= 191
1201
- // disabled it causes
1202
- // us to lose valuable frame information...maybe mid comes in wrong sometimes?
1203
- // walk_up_until_right_frame(frame, thread_data, mid, klass, now);
1204
- # endif
1205
-
1206
288
  break;
1207
289
  }
1208
290
 
@@ -1252,7 +334,7 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1252
334
  /* Push a new frame onto the stack for a new c-call or ruby call (into a method) */
1253
335
  frame = stack_push(thread_data->stack);
1254
336
  frame->call_info = call_info;
1255
- frame->start_time = now;
337
+ frame->start_time = profile->measurement;
1256
338
  frame->wait_time = 0;
1257
339
  frame->child_time = 0;
1258
340
  frame->line = rb_sourceline();
@@ -1261,269 +343,172 @@ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE kla
1261
343
  case RUBY_EVENT_RETURN:
1262
344
  case RUBY_EVENT_C_RETURN:
1263
345
  {
1264
- frame = pop_frame(thread_data, now);
1265
-
1266
- # if RUBY_VERSION >= 191
1267
- // we need to walk up the stack to find the right one [http://redmine.ruby-lang.org/issues/show/2610] (for now)
1268
- // sometimes frames don't have line and source somehow [like blank]
1269
- // if we hit one there's not much we can do...I guess...
1270
- // or maybe we don't have one because we're at the top or something.
1271
- walk_up_until_right_frame(frame, thread_data, mid, klass, now);
1272
- # endif
1273
-
346
+ frame = pop_frame(profile, thread_data);
1274
347
  break;
1275
348
  }
1276
- }
1277
- }
1278
-
1279
- #if RUBY_VERSION >= 191
1280
-
1281
- 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) {
1282
- // while it doesn't match, pop on up until we have found where we belong...
1283
- 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))){
1284
- frame = pop_frame(thread_data, now);
1285
349
  }
1286
350
  }
1287
- #endif
1288
351
 
1289
- /* ======== ProfResult ============== */
1290
352
 
1291
- /* Document-class: RubyProf::Result
1292
- The RubyProf::Result class is used to store the results of a
1293
- profiling run. And instace of the class is returned from
1294
- the methods RubyProf#stop and RubyProf#profile.
1295
-
1296
- RubyProf::Result has one field, called threads, which is a hash
1297
- table keyed on thread ID. For each thread id, the hash table
1298
- stores another hash table that contains profiling information
1299
- for each method called during the threads execution. That
1300
- hash table is keyed on method name and contains
1301
- RubyProf::MethodInfo objects. */
1302
-
1303
- static void
1304
- prof_result_mark(prof_result_t *prof_result)
353
+ /* =========== Profiling ================= */
354
+ void
355
+ prof_install_hook(VALUE self)
1305
356
  {
1306
- VALUE threads = prof_result->threads;
1307
- rb_gc_mark(threads);
1308
- }
357
+ #ifdef RUBY_VM
358
+ rb_add_event_hook(prof_event_hook,
359
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
360
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
361
+ | RUBY_EVENT_LINE, self); // RUBY_EVENT_SWITCH
362
+ #else
363
+ rb_add_event_hook(prof_event_hook,
364
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
365
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
366
+ | RUBY_EVENT_LINE);
1309
367
 
1310
- static void
1311
- prof_result_free(prof_result_t *prof_result)
1312
- {
1313
- prof_result->threads = Qnil;
1314
- xfree(prof_result);
368
+ pCurrentProfile = prof_get_profile(self);
369
+ #endif
370
+
371
+ #if defined(TOGGLE_GC_STATS)
372
+ rb_gc_enable_stats();
373
+ #endif
1315
374
  }
1316
375
 
1317
- static VALUE
1318
- prof_result_new()
376
+ void
377
+ prof_remove_hook()
1319
378
  {
1320
- prof_result_t *prof_result = ALLOC(prof_result_t);
379
+ #if defined(TOGGLE_GC_STATS)
380
+ rb_gc_disable_stats();
381
+ #endif
1321
382
 
1322
- /* Wrap threads in Ruby regular Ruby hash table. */
1323
- prof_result->threads = rb_hash_new();
1324
- st_foreach(threads_tbl, collect_threads, prof_result->threads);
383
+ #ifndef RUBY_VM
384
+ pCurrentProfile = NULL;
385
+ #endif
1325
386
 
1326
- return Data_Wrap_Struct(cResult, prof_result_mark, prof_result_free, prof_result);
387
+ /* Now unregister from event */
388
+ rb_remove_event_hook(prof_event_hook);
1327
389
  }
1328
390
 
1329
391
 
1330
- static prof_result_t *
1331
- get_prof_result(VALUE obj)
392
+ static int
393
+ collect_methods(st_data_t key, st_data_t value, st_data_t result)
1332
394
  {
1333
- if (BUILTIN_TYPE(obj) != T_DATA ||
1334
- RDATA(obj)->dfree != (RUBY_DATA_FUNC) prof_result_free)
1335
- {
1336
- /* Should never happen */
1337
- rb_raise(rb_eTypeError, "wrong result object (%d %d) ", BUILTIN_TYPE(obj) != T_DATA, RDATA(obj)->dfree != (RUBY_DATA_FUNC) prof_result_free);
1338
- }
1339
- return (prof_result_t *) DATA_PTR(obj);
1340
- }
395
+ /* Called for each method stored in a thread's method table.
396
+ We want to store the method info information into an array.*/
397
+ VALUE methods = (VALUE) result;
398
+ prof_method_t *method = (prof_method_t *) value;
399
+ rb_ary_push(methods, prof_method_wrap(method));
1341
400
 
1342
- /* call-seq:
1343
- threads -> Hash
401
+ /* Wrap call info objects */
402
+ prof_call_infos_wrap(method->call_infos);
1344
403
 
1345
- Returns a hash table keyed on thread ID. For each thread id,
1346
- the hash table stores another hash table that contains profiling
1347
- information for each method called during the threads execution.
1348
- That hash table is keyed on method name and contains
1349
- RubyProf::MethodInfo objects. */
1350
- static VALUE
1351
- prof_result_threads(VALUE self)
1352
- {
1353
- prof_result_t *prof_result = get_prof_result(self);
1354
- return prof_result->threads;
404
+ return ST_CONTINUE;
1355
405
  }
1356
406
 
407
+ static int
408
+ collect_threads(st_data_t key, st_data_t value, st_data_t result)
409
+ {
410
+ /* Although threads are keyed on an id, that is actually a
411
+ pointer to the VALUE object of the thread. So its bogus.
412
+ However, in thread_data is the real thread id stored
413
+ as an int. */
414
+ thread_data_t* thread_data = (thread_data_t*) value;
415
+ VALUE threads_hash = (VALUE) result;
1357
416
 
417
+ VALUE methods = rb_ary_new();
1358
418
 
1359
- /* call-seq:
1360
- measure_mode -> measure_mode
419
+ /* Now collect an array of all the called methods */
420
+ st_table* method_table = thread_data->method_table;
421
+ st_foreach(method_table, collect_methods, methods);
1361
422
 
1362
- Returns what ruby-prof is measuring. Valid values include:
423
+ /* Store the results in the threads hash keyed on the thread id. */
424
+ rb_hash_aset(threads_hash, thread_data->thread_id, methods);
1363
425
 
1364
- *RubyProf::PROCESS_TIME - Measure process time. This is default. It is implemented using the clock functions in the C Runtime library.
1365
- *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
1366
- *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms.
1367
- *RubyProf::ALLOCATIONS - Measure object allocations. This requires a patched Ruby interpreter.
1368
- *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.
1369
- *RubyProf::GC_RUNS - Measure number of garbage collections. This requires a patched Ruby interpreter.
1370
- *RubyProf::GC_TIME - Measure time spent doing garbage collection. This requires a patched Ruby interpreter.*/
1371
- static VALUE
1372
- prof_get_measure_mode(VALUE self)
1373
- {
1374
- return INT2NUM(measure_mode);
426
+ return ST_CONTINUE;
1375
427
  }
1376
428
 
1377
- /* call-seq:
1378
- measure_mode=value -> void
1379
-
1380
- Specifies what ruby-prof should measure. Valid values include:
1381
-
1382
- *RubyProf::PROCESS_TIME - Measure process time. This is default. It is implemented using the clock functions in the C Runtime library.
1383
- *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
1384
- *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms.
1385
- *RubyProf::ALLOCATIONS - Measure object allocations. This requires a patched Ruby interpreter.
1386
- *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.
1387
- *RubyProf::GC_RUNS - Measure number of garbage collections. This requires a patched Ruby interpreter.
1388
- *RubyProf::GC_TIME - Measure time spent doing garbage collection. This requires a patched Ruby interpreter.*/
1389
- static VALUE
1390
- prof_set_measure_mode(VALUE self, VALUE val)
429
+ /* ======== Profile Class ====== */
430
+ static void
431
+ prof_mark(prof_profile_t *profile)
1391
432
  {
1392
- int mode = NUM2INT(val);
1393
-
1394
- if (threads_tbl)
1395
- {
1396
- rb_raise(rb_eRuntimeError, "can't set measure_mode while profiling");
1397
- }
1398
-
1399
- switch (mode) {
1400
- case MEASURE_PROCESS_TIME:
1401
- get_measurement = measure_process_time;
1402
- convert_measurement = convert_process_time;
1403
- break;
1404
-
1405
- case MEASURE_WALL_TIME:
1406
- get_measurement = measure_wall_time;
1407
- convert_measurement = convert_wall_time;
1408
- break;
1409
-
1410
- #if defined(MEASURE_CPU_TIME)
1411
- case MEASURE_CPU_TIME:
1412
- if (cpu_frequency == 0)
1413
- cpu_frequency = get_cpu_frequency();
1414
- get_measurement = measure_cpu_time;
1415
- convert_measurement = convert_cpu_time;
1416
- break;
1417
- #endif
1418
-
1419
- #if defined(MEASURE_ALLOCATIONS)
1420
- case MEASURE_ALLOCATIONS:
1421
- get_measurement = measure_allocations;
1422
- convert_measurement = convert_allocations;
1423
- break;
1424
- #endif
1425
-
1426
- #if defined(MEASURE_MEMORY)
1427
- case MEASURE_MEMORY:
1428
- get_measurement = measure_memory;
1429
- convert_measurement = convert_memory;
1430
- break;
1431
- #endif
1432
-
1433
- #if defined(MEASURE_GC_RUNS)
1434
- case MEASURE_GC_RUNS:
1435
- get_measurement = measure_gc_runs;
1436
- convert_measurement = convert_gc_runs;
1437
- break;
1438
- #endif
1439
-
1440
- #if defined(MEASURE_GC_TIME)
1441
- case MEASURE_GC_TIME:
1442
- get_measurement = measure_gc_time;
1443
- convert_measurement = convert_gc_time;
1444
- break;
1445
- #endif
433
+ VALUE threads = profile->threads;
434
+ rb_gc_mark(threads);
435
+ }
1446
436
 
1447
- default:
1448
- rb_raise(rb_eArgError, "invalid mode: %d", mode);
1449
- break;
1450
- }
437
+ static void
438
+ prof_free(prof_profile_t *profile)
439
+ {
440
+ profile->threads = Qnil;
441
+ st_free_table(profile->exclude_threads_tbl);
442
+ profile->exclude_threads_tbl = NULL;
1451
443
 
1452
- measure_mode = mode;
1453
- return val;
444
+ xfree(profile);
1454
445
  }
1455
446
 
1456
- /* call-seq:
1457
- exclude_threads= -> void
1458
-
1459
- Specifies what threads ruby-prof should exclude from profiling */
1460
447
  static VALUE
1461
- prof_set_exclude_threads(VALUE self, VALUE threads)
448
+ prof_allocate(VALUE klass)
1462
449
  {
1463
- int i;
1464
-
1465
- if (threads_tbl != NULL)
1466
- {
1467
- rb_raise(rb_eRuntimeError, "can't set exclude_threads while profiling");
1468
- }
1469
-
1470
- /* Stay simple, first free the old hash table */
1471
- if (exclude_threads_tbl)
1472
- {
1473
- st_free_table(exclude_threads_tbl);
1474
- exclude_threads_tbl = NULL;
1475
- }
450
+ VALUE result;
451
+ prof_profile_t* profile;
452
+ result = Data_Make_Struct(klass, prof_profile_t, prof_mark, prof_free, profile);
453
+ profile->exclude_threads_tbl = threads_table_create();
454
+ profile->running = Qfalse;
455
+ return result;
456
+ }
1476
457
 
1477
- /* Now create a new one if the user passed in any threads */
1478
- if (threads != Qnil)
458
+ /* call-seq:
459
+ RubyProf::Profile.new(mode, exclude_threads) -> instance
460
+
461
+ Returns a new profiler.
462
+
463
+ == Parameters
464
+ mode:: Measure mode (optional). Specifies the profile measure mode. If not specified, defaults
465
+ to RubyProf::WALL_TIME.
466
+ exclude_threads:: Threads to exclude from the profiling results (optional). */
467
+ static VALUE
468
+ prof_initialize(int argc, VALUE *argv, VALUE self)
469
+ {
470
+ prof_profile_t* profile = prof_get_profile(self);
471
+ VALUE mode;
472
+ prof_measure_mode_t measurer;
473
+ VALUE exclude_threads;
474
+ int i;
475
+
476
+ switch (rb_scan_args(argc, argv, "02", &mode, &exclude_threads))
1479
477
  {
1480
- Check_Type(threads, T_ARRAY);
1481
- exclude_threads_tbl = st_init_numtable();
1482
-
1483
- for (i=0; i < RARRAY_LEN(threads); ++i)
478
+ case 0:
479
+ {
480
+ measurer = MEASURE_WALL_TIME;
481
+ exclude_threads = rb_ary_new();
482
+ break;
483
+ }
484
+ case 1:
1484
485
  {
1485
- VALUE thread = rb_ary_entry(threads, i);
1486
- st_insert(exclude_threads_tbl, (st_data_t) rb_obj_id(thread), 0);
486
+ measurer = (prof_measure_mode_t)NUM2INT(mode);
487
+ exclude_threads = rb_ary_new();
488
+ break;
489
+ }
490
+ case 2:
491
+ {
492
+ Check_Type(exclude_threads, T_ARRAY);
493
+ measurer = (prof_measure_mode_t)NUM2INT(mode);
494
+ break;
1487
495
  }
1488
496
  }
1489
- return threads;
1490
- }
1491
-
1492
497
 
1493
- /* ========= Profiling ============= */
1494
- void
1495
- prof_install_hook()
1496
- {
1497
- #ifdef RUBY_VM
1498
- rb_add_event_hook(prof_event_hook,
1499
- RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1500
- RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
1501
- | RUBY_EVENT_LINE, Qnil); // RUBY_EVENT_SWITCH
1502
- #else
1503
- rb_add_event_hook(prof_event_hook,
1504
- RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1505
- RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
1506
- | RUBY_EVENT_LINE);
1507
- #endif
498
+ profile->measurer = prof_get_measurer(measurer);
499
+ profile->threads = rb_hash_new();
1508
500
 
1509
- #if defined(TOGGLE_GC_STATS)
1510
- rb_gc_enable_stats();
1511
- #endif
1512
- }
1513
501
 
1514
- void
1515
- prof_remove_hook()
1516
- {
1517
- #if defined(TOGGLE_GC_STATS)
1518
- rb_gc_disable_stats();
1519
- #endif
502
+ for (i = 0; i < RARRAY_LEN(exclude_threads); i++)
503
+ {
504
+ VALUE thread = rb_ary_entry(exclude_threads, i);
505
+ VALUE thread_id = rb_obj_id(thread);
506
+ st_insert(profile->exclude_threads_tbl, thread_id, Qtrue);
507
+ }
1520
508
 
1521
- /* Now unregister from event */
1522
- rb_remove_event_hook(prof_event_hook);
509
+ return self;
1523
510
  }
1524
511
 
1525
-
1526
-
1527
512
  /* call-seq:
1528
513
  running? -> boolean
1529
514
 
@@ -1531,10 +516,8 @@ prof_remove_hook()
1531
516
  static VALUE
1532
517
  prof_running(VALUE self)
1533
518
  {
1534
- if (threads_tbl != NULL)
1535
- return Qtrue;
1536
- else
1537
- return Qfalse;
519
+ prof_profile_t* profile = prof_get_profile(self);
520
+ return profile->running;
1538
521
  }
1539
522
 
1540
523
  /* call-seq:
@@ -1544,17 +527,20 @@ prof_running(VALUE self)
1544
527
  static VALUE
1545
528
  prof_start(VALUE self)
1546
529
  {
1547
- if (threads_tbl != NULL)
530
+ char* trace_file_name;
531
+ prof_profile_t* profile = prof_get_profile(self);
532
+
533
+ if (profile->running == Qtrue)
1548
534
  {
1549
535
  rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
1550
536
  }
1551
537
 
1552
- /* Setup globals */
1553
- last_thread_data = NULL;
1554
- threads_tbl = threads_table_create();
538
+ profile->running = Qtrue;
539
+ profile->last_thread_data = NULL;
540
+ profile->threads_tbl = threads_table_create();
1555
541
 
1556
542
  /* open trace file if environment wants it */
1557
- char* trace_file_name = getenv("RUBY_PROF_TRACE");
543
+ trace_file_name = getenv("RUBY_PROF_TRACE");
1558
544
  if (trace_file_name != NULL) {
1559
545
  if (0==strcmp(trace_file_name, "stdout")) {
1560
546
  trace_file = stdout;
@@ -1565,7 +551,7 @@ prof_start(VALUE self)
1565
551
  }
1566
552
  }
1567
553
 
1568
- prof_install_hook();
554
+ prof_install_hook(self);
1569
555
  return self;
1570
556
  }
1571
557
 
@@ -1576,11 +562,14 @@ prof_start(VALUE self)
1576
562
  static VALUE
1577
563
  prof_pause(VALUE self)
1578
564
  {
1579
- if (threads_tbl == NULL)
565
+ prof_profile_t* profile = prof_get_profile(self);
566
+ if (profile->running == Qfalse)
1580
567
  {
1581
568
  rb_raise(rb_eRuntimeError, "RubyProf is not running.");
1582
569
  }
1583
570
 
571
+ profile->running = Qfalse;
572
+
1584
573
  prof_remove_hook();
1585
574
  return self;
1586
575
  }
@@ -1592,13 +581,15 @@ prof_pause(VALUE self)
1592
581
  static VALUE
1593
582
  prof_resume(VALUE self)
1594
583
  {
1595
- if (threads_tbl == NULL)
584
+ prof_profile_t* profile = prof_get_profile(self);
585
+ if (profile->running == Qfalse)
1596
586
  {
1597
587
  prof_start(self);
1598
588
  }
1599
589
  else
1600
590
  {
1601
- prof_install_hook();
591
+ profile->running = Qtrue;
592
+ prof_install_hook(self);
1602
593
  }
1603
594
 
1604
595
  if (rb_block_given_p())
@@ -1610,23 +601,23 @@ prof_resume(VALUE self)
1610
601
  }
1611
602
 
1612
603
  /* call-seq:
1613
- stop -> RubyProf::Result
604
+ stop -> self
1614
605
 
1615
- Stops collecting profile data and returns a RubyProf::Result object. */
606
+ Stops collecting profile data.*/
1616
607
  static VALUE
1617
608
  prof_stop(VALUE self)
1618
609
  {
1619
- /* get 'now' before prof emove hook because it calls GC.disable_stats
610
+ prof_profile_t* profile = prof_get_profile(self);
611
+
612
+ /* get 'now' before prof emove hook because it calls GC.disable_stats
1620
613
  which makes the call within prof_pop_threads of now return 0, which is wrong
1621
614
  */
1622
- prof_measure_t now = get_measurement();
1623
- if (threads_tbl == NULL)
615
+ profile->measurement = profile->measurer->measure();
616
+ if (profile->running == Qfalse)
1624
617
  {
1625
618
  rb_raise(rb_eRuntimeError, "RubyProf.start was not yet called");
1626
619
  }
1627
620
 
1628
- VALUE result = Qnil;
1629
-
1630
621
  /* close trace file if open */
1631
622
  if (trace_file != NULL) {
1632
623
  if (trace_file!=stderr && trace_file!=stdout)
@@ -1635,22 +626,22 @@ prof_stop(VALUE self)
1635
626
  }
1636
627
 
1637
628
  prof_remove_hook();
1638
-
1639
- prof_pop_threads(now);
1640
-
1641
- /* Create the result */
1642
- result = prof_result_new();
629
+ prof_pop_threads(profile);
1643
630
 
1644
631
  /* Unset the last_thread_data (very important!)
1645
632
  and the threads table */
1646
- last_thread_data = NULL;
1647
- threads_table_free(threads_tbl);
1648
- threads_tbl = NULL;
633
+ profile->running = Qfalse;
634
+ profile->last_thread_data = NULL;
635
+
636
+ /* Save the result */
637
+ st_foreach(profile->threads_tbl, collect_threads, profile->threads);
638
+ threads_table_free(profile->threads_tbl);
639
+ profile->threads_tbl = NULL;
1649
640
 
1650
641
  /* compute minimality of call_infos */
1651
- rb_funcall(result, rb_intern("compute_minimality") , 0);
642
+ rb_funcall(self, rb_intern("compute_minimality") , 0);
1652
643
 
1653
- return result;
644
+ return self;
1654
645
  }
1655
646
 
1656
647
  /* call-seq:
@@ -1658,184 +649,54 @@ prof_stop(VALUE self)
1658
649
 
1659
650
  Profiles the specified block and returns a RubyProf::Result object. */
1660
651
  static VALUE
1661
- prof_profile(VALUE self)
652
+ prof_profile(int argc, VALUE *argv, VALUE klass)
1662
653
  {
1663
654
  int result;
655
+ VALUE profile = rb_class_new_instance(argc, argv, cProfile);
1664
656
 
1665
657
  if (!rb_block_given_p())
1666
658
  {
1667
659
  rb_raise(rb_eArgError, "A block must be provided to the profile method.");
1668
660
  }
1669
661
 
1670
- prof_start(self);
1671
- rb_protect(rb_yield, self, &result);
1672
- return prof_stop(self);
662
+ prof_start(profile);
663
+ rb_protect(rb_yield, profile, &result);
664
+ return prof_stop(profile);
1673
665
  }
1674
666
 
1675
- /* Get around annoying limitations in RDOC */
1676
-
1677
- /* Document-method: measure_process_time
1678
- call-seq:
1679
- measure_process_time -> float
1680
-
1681
- Returns the process time.*/
1682
-
1683
- /* Document-method: measure_wall_time
1684
- call-seq:
1685
- measure_wall_time -> float
1686
-
1687
- Returns the wall time.*/
1688
-
1689
- /* Document-method: measure_cpu_time
1690
- call-seq:
1691
- measure_cpu_time -> float
1692
-
1693
- Returns the cpu time.*/
1694
-
1695
- /* Document-method: get_cpu_frequency
1696
- call-seq:
1697
- cpu_frequency -> int
1698
-
1699
- Returns the cpu's frequency. This value is needed when
1700
- RubyProf::measure_mode is set to CPU_TIME. */
1701
-
1702
- /* Document-method: cpu_frequency
1703
- call-seq:
1704
- cpu_frequency -> int
1705
-
1706
- Returns the cpu's frequency. This value is needed when
1707
- RubyProf::measure_mode is set to CPU_TIME. */
1708
-
1709
- /* Document-method: cpu_frequency=
1710
- call-seq:
1711
- cpu_frequency = frequency
1712
-
1713
- Sets the cpu's frequency. This value is needed when
1714
- RubyProf::measure_mode is set to CPU_TIME. */
1715
-
1716
- /* Document-method: measure_allocations
1717
- call-seq:
1718
- measure_allocations -> int
1719
-
1720
- Returns the total number of object allocations since Ruby started.*/
1721
-
1722
- /* Document-method: measure_memory
1723
- call-seq:
1724
- measure_memory -> int
1725
-
1726
- Returns total allocated memory in bytes.*/
1727
-
1728
- /* Document-method: measure_gc_runs
1729
- call-seq:
1730
- gc_runs -> Integer
1731
-
1732
- Returns the total number of garbage collections.*/
1733
-
1734
- /* Document-method: measure_gc_time
1735
- call-seq:
1736
- gc_time -> Integer
1737
-
1738
- Returns the time spent doing garbage collections in microseconds.*/
1739
-
667
+ /* call-seq:
668
+ threads -> Hash
1740
669
 
1741
- #if RUBY_VERSION == 191 // accomodate for this 1.9.1 windows bug: http://redmine.ruby-lang.org/issues/show/3748
1742
- # if defined(_WIN32)
1743
- __declspec(dllexport)
1744
- # endif
1745
- #endif
1746
- void
670
+ Returns a hash table keyed on thread ID. For each thread id,
671
+ the hash table stores another hash table that contains profiling
672
+ information for each method called during the threads execution.
673
+ That hash table is keyed on method name and contains
674
+ RubyProf::MethodInfo objects. */
675
+ static VALUE
676
+ prof_threads(VALUE self)
677
+ {
678
+ prof_profile_t* profile = prof_get_profile(self);
679
+ return profile->threads;
680
+ }
1747
681
 
1748
- Init_ruby_prof_ext()
682
+ void Init_ruby_prof()
1749
683
  {
1750
684
  mProf = rb_define_module("RubyProf");
1751
685
  rb_define_const(mProf, "VERSION", rb_str_new2(RUBY_PROF_VERSION));
1752
- rb_define_module_function(mProf, "start", prof_start, 0);
1753
- rb_define_module_function(mProf, "stop", prof_stop, 0);
1754
- rb_define_module_function(mProf, "resume", prof_resume, 0);
1755
- rb_define_module_function(mProf, "pause", prof_pause, 0);
1756
- rb_define_module_function(mProf, "running?", prof_running, 0);
1757
- rb_define_module_function(mProf, "profile", prof_profile, 0);
1758
-
1759
- rb_define_singleton_method(mProf, "exclude_threads=", prof_set_exclude_threads, 1);
1760
- rb_define_singleton_method(mProf, "measure_mode", prof_get_measure_mode, 0);
1761
- rb_define_singleton_method(mProf, "measure_mode=", prof_set_measure_mode, 1);
1762
-
1763
- rb_define_const(mProf, "CLOCKS_PER_SEC", INT2NUM(CLOCKS_PER_SEC));
1764
- rb_define_const(mProf, "PROCESS_TIME", INT2NUM(MEASURE_PROCESS_TIME));
1765
- rb_define_singleton_method(mProf, "measure_process_time", prof_measure_process_time, 0); /* in measure_process_time.h */
1766
- rb_define_const(mProf, "WALL_TIME", INT2NUM(MEASURE_WALL_TIME));
1767
- rb_define_singleton_method(mProf, "measure_wall_time", prof_measure_wall_time, 0); /* in measure_wall_time.h */
1768
-
1769
- #ifndef MEASURE_CPU_TIME
1770
- rb_define_const(mProf, "CPU_TIME", Qnil);
1771
- #else
1772
- rb_define_const(mProf, "CPU_TIME", INT2NUM(MEASURE_CPU_TIME));
1773
- rb_define_singleton_method(mProf, "measure_cpu_time", prof_measure_cpu_time, 0); /* in measure_cpu_time.h */
1774
- rb_define_singleton_method(mProf, "cpu_frequency", prof_get_cpu_frequency, 0); /* in measure_cpu_time.h */
1775
- rb_define_singleton_method(mProf, "cpu_frequency=", prof_set_cpu_frequency, 1); /* in measure_cpu_time.h */
1776
- #endif
1777
-
1778
- #ifndef MEASURE_ALLOCATIONS
1779
- rb_define_const(mProf, "ALLOCATIONS", Qnil);
1780
- #else
1781
- rb_define_const(mProf, "ALLOCATIONS", INT2NUM(MEASURE_ALLOCATIONS));
1782
- rb_define_singleton_method(mProf, "measure_allocations", prof_measure_allocations, 0); /* in measure_allocations.h */
1783
- #endif
1784
-
1785
- #ifndef MEASURE_MEMORY
1786
- rb_define_const(mProf, "MEMORY", Qnil);
1787
- #else
1788
- rb_define_const(mProf, "MEMORY", INT2NUM(MEASURE_MEMORY));
1789
- rb_define_singleton_method(mProf, "measure_memory", prof_measure_memory, 0); /* in measure_memory.h */
1790
- #endif
1791
-
1792
- #ifndef MEASURE_GC_RUNS
1793
- rb_define_const(mProf, "GC_RUNS", Qnil);
1794
- #else
1795
- rb_define_const(mProf, "GC_RUNS", INT2NUM(MEASURE_GC_RUNS));
1796
- rb_define_singleton_method(mProf, "measure_gc_runs", prof_measure_gc_runs, 0); /* in measure_gc_runs.h */
1797
- #endif
1798
-
1799
- #ifndef MEASURE_GC_TIME
1800
- rb_define_const(mProf, "GC_TIME", Qnil);
1801
- #else
1802
- rb_define_const(mProf, "GC_TIME", INT2NUM(MEASURE_GC_TIME));
1803
- rb_define_singleton_method(mProf, "measure_gc_time", prof_measure_gc_time, 0); /* in measure_gc_time.h */
1804
- #endif
1805
-
1806
- cResult = rb_define_class_under(mProf, "Result", rb_cObject);
1807
- rb_undef_method(CLASS_OF(cMethodInfo), "new");
1808
- rb_define_method(cResult, "threads", prof_result_threads, 0);
1809
-
1810
- /* MethodInfo */
1811
- cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
1812
- rb_undef_method(CLASS_OF(cMethodInfo), "new");
1813
-
1814
- rb_define_method(cMethodInfo, "klass", prof_method_klass, 0);
1815
- rb_define_method(cMethodInfo, "klass_name", prof_klass_name, 0);
1816
- rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
1817
- rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
1818
- rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
1819
-
1820
- rb_define_method(cMethodInfo, "source_file", prof_method_source_file,0);
1821
- rb_define_method(cMethodInfo, "line", prof_method_line, 0);
1822
-
1823
- rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
1824
-
1825
- /* CallInfo */
1826
- cCallInfo = rb_define_class_under(mProf, "CallInfo", rb_cObject);
1827
- rb_undef_method(CLASS_OF(cCallInfo), "new");
1828
- rb_define_method(cCallInfo, "parent", prof_call_info_parent, 0);
1829
- rb_define_method(cCallInfo, "parent=", prof_call_info_set_parent, 1);
1830
- rb_define_method(cCallInfo, "children", prof_call_info_children, 0);
1831
- rb_define_method(cCallInfo, "target", prof_call_info_target, 0);
1832
- rb_define_method(cCallInfo, "called", prof_call_info_called, 0);
1833
- rb_define_method(cCallInfo, "called=", prof_call_info_set_called, 1);
1834
- rb_define_method(cCallInfo, "total_time", prof_call_info_total_time, 0);
1835
- rb_define_method(cCallInfo, "add_total_time", prof_call_info_add_total_time, 1);
1836
- rb_define_method(cCallInfo, "self_time", prof_call_info_self_time, 0);
1837
- rb_define_method(cCallInfo, "add_self_time", prof_call_info_add_self_time, 1);
1838
- rb_define_method(cCallInfo, "wait_time", prof_call_info_wait_time, 0);
1839
- rb_define_method(cCallInfo, "add_wait_time", prof_call_info_add_wait_time, 1);
1840
- rb_define_method(cCallInfo, "line", prof_call_info_line, 0);
686
+
687
+ rp_init_measure();
688
+ rp_init_method_info();
689
+ rp_init_call_info();
690
+
691
+ cProfile = rb_define_class_under(mProf, "Profile", rb_cObject);
692
+ rb_define_singleton_method(cProfile, "profile", prof_profile, -1);
693
+ rb_define_alloc_func (cProfile, prof_allocate);
694
+ rb_define_method(cProfile, "initialize", prof_initialize, -1);
695
+ rb_define_method(cProfile, "start", prof_start, 0);
696
+ rb_define_method(cProfile, "start", prof_start, 0);
697
+ rb_define_method(cProfile, "stop", prof_stop, 0);
698
+ rb_define_method(cProfile, "resume", prof_resume, 0);
699
+ rb_define_method(cProfile, "pause", prof_pause, 0);
700
+ rb_define_method(cProfile, "running?", prof_running, 0);
701
+ rb_define_method(cProfile, "threads", prof_threads, 0);
1841
702
  }