ruby-prof 0.8.1-x86-mingw32 → 0.11.0.rc1-x86-mingw32

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