ruby-prof 0.10.8 → 0.11.0.rc1

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 (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
  }