ruby-prof 0.8.1-x86-mingw32 → 0.11.0.rc1-x86-mingw32
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.
- data/CHANGES +89 -13
- data/LICENSE +4 -3
- data/{README → README.rdoc} +155 -162
- data/Rakefile +50 -123
- data/bin/ruby-prof +86 -47
- data/examples/empty.png +0 -0
- data/examples/graph.dot +106 -0
- data/examples/graph.png +0 -0
- data/examples/minus.png +0 -0
- data/examples/multi.flat.txt +23 -0
- data/examples/multi.graph.html +906 -0
- data/examples/multi.grind.dat +194 -0
- data/examples/multi.stack.html +573 -0
- data/examples/plus.png +0 -0
- data/examples/stack.html +573 -0
- data/ext/ruby_prof/extconf.rb +53 -0
- data/ext/ruby_prof/rp_call_info.c +369 -0
- data/ext/ruby_prof/rp_call_info.h +46 -0
- data/ext/ruby_prof/rp_measure.c +48 -0
- data/ext/ruby_prof/rp_measure.h +45 -0
- data/ext/ruby_prof/rp_measure_allocations.c +86 -0
- data/ext/ruby_prof/rp_measure_cpu_time.c +112 -0
- data/ext/ruby_prof/rp_measure_gc_runs.c +87 -0
- data/ext/ruby_prof/rp_measure_gc_time.c +73 -0
- data/ext/ruby_prof/rp_measure_memory.c +81 -0
- data/ext/ruby_prof/rp_measure_process_time.c +71 -0
- data/ext/ruby_prof/rp_measure_wall_time.c +42 -0
- data/ext/ruby_prof/rp_method.c +363 -0
- data/ext/ruby_prof/rp_method.h +55 -0
- data/ext/ruby_prof/rp_stack.c +61 -0
- data/ext/ruby_prof/rp_stack.h +40 -0
- data/ext/ruby_prof/rp_thread.c +113 -0
- data/ext/ruby_prof/rp_thread.h +20 -0
- data/ext/ruby_prof/ruby_prof.c +332 -1377
- data/ext/ruby_prof/ruby_prof.h +54 -188
- data/ext/ruby_prof/version.h +6 -3
- data/lib/1.8/ruby_prof.so +0 -0
- data/lib/1.9/ruby_prof.exp +0 -0
- data/lib/1.9/ruby_prof.ilk +0 -0
- data/lib/1.9/ruby_prof.lib +0 -0
- data/lib/1.9/ruby_prof.pdb +0 -0
- data/lib/1.9/ruby_prof.so +0 -0
- data/lib/ruby-prof.rb +32 -18
- data/lib/ruby-prof/abstract_printer.rb +15 -5
- data/lib/ruby-prof/aggregate_call_info.rb +11 -3
- data/lib/ruby-prof/call_info.rb +68 -1
- data/lib/ruby-prof/call_stack_printer.rb +775 -0
- data/lib/ruby-prof/call_tree_printer.rb +17 -9
- data/lib/ruby-prof/compatibility.rb +134 -0
- data/lib/ruby-prof/dot_printer.rb +152 -0
- data/lib/ruby-prof/empty.png +0 -0
- data/lib/ruby-prof/flat_printer.rb +23 -24
- data/lib/ruby-prof/flat_printer_with_line_numbers.rb +17 -21
- data/lib/ruby-prof/graph_html_printer.rb +69 -39
- data/lib/ruby-prof/graph_printer.rb +35 -35
- data/lib/ruby-prof/method_info.rb +26 -4
- data/lib/ruby-prof/minus.png +0 -0
- data/lib/ruby-prof/multi_printer.rb +56 -0
- data/lib/ruby-prof/plus.png +0 -0
- data/lib/ruby-prof/profile.rb +72 -0
- data/lib/ruby-prof/rack.rb +31 -0
- data/lib/ruby-prof/symbol_to_proc.rb +3 -1
- data/lib/ruby-prof/task.rb +20 -19
- data/lib/ruby-prof/test.rb +5 -3
- data/lib/ruby_prof.exp +0 -0
- data/lib/ruby_prof.ilk +0 -0
- data/lib/ruby_prof.lib +0 -0
- data/lib/ruby_prof.pdb +0 -0
- data/lib/ruby_prof.so +0 -0
- data/lib/unprof.rb +2 -0
- data/test/aggregate_test.rb +29 -14
- data/test/basic_test.rb +3 -251
- data/test/bug_test.rb +6 -0
- data/test/duplicate_names_test.rb +4 -4
- data/test/dynamic_method_test.rb +61 -0
- data/test/enumerable_test.rb +4 -4
- data/test/exceptions_test.rb +6 -5
- data/test/exclude_threads_test.rb +47 -47
- data/test/exec_test.rb +5 -5
- data/test/line_number_test.rb +16 -16
- data/test/measure_allocations_test.rb +25 -0
- data/test/measure_cpu_time_test.rb +212 -0
- data/test/measure_gc_runs_test.rb +29 -0
- data/test/measure_gc_time_test.rb +29 -0
- data/test/measure_memory_test.rb +36 -0
- data/test/measure_process_time_test.rb +205 -0
- data/test/measure_wall_time_test.rb +209 -0
- data/test/method_elimination_test.rb +74 -0
- data/test/module_test.rb +12 -21
- data/test/multi_printer_test.rb +81 -0
- data/test/no_method_class_test.rb +5 -3
- data/test/prime.rb +7 -10
- data/test/prime_test.rb +3 -3
- data/test/printers_test.rb +180 -54
- data/test/recursive_test.rb +34 -72
- data/test/singleton_test.rb +5 -4
- data/test/stack_printer_test.rb +73 -0
- data/test/stack_test.rb +7 -7
- data/test/start_stop_test.rb +23 -6
- data/test/test_helper.rb +81 -0
- data/test/test_suite.rb +35 -21
- data/test/thread_test.rb +40 -39
- data/test/unique_call_path_test.rb +6 -6
- metadata +106 -51
- data/ext/ruby_prof/measure_allocations.h +0 -58
- data/ext/ruby_prof/measure_cpu_time.h +0 -152
- data/ext/ruby_prof/measure_gc_runs.h +0 -76
- data/ext/ruby_prof/measure_gc_time.h +0 -57
- data/ext/ruby_prof/measure_memory.h +0 -101
- data/ext/ruby_prof/measure_process_time.h +0 -52
- data/ext/ruby_prof/measure_wall_time.h +0 -53
- data/ext/ruby_prof/mingw/Rakefile +0 -23
- data/ext/ruby_prof/mingw/build.rake +0 -38
- data/rails/environment/profile.rb +0 -24
- data/rails/example/example_test.rb +0 -9
- data/rails/profile_test_helper.rb +0 -21
- data/test/current_failures_windows +0 -8
- data/test/measurement_test.rb +0 -121
- 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__
|
data/ext/ruby_prof/ruby_prof.c
CHANGED
@@ -1,858 +1,66 @@
|
|
1
|
-
/*
|
2
|
-
|
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
|
-
|
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
|
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
|
-
|
55
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
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
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
170
|
-
|
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->
|
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
|
-
/*
|
853
|
-
|
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
|
-
|
94
|
+
|
95
|
+
static prof_method_t*
|
888
96
|
#ifdef RUBY_VM
|
889
|
-
get_method(rb_event_flag_t event, VALUE klass, ID mid,
|
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,
|
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
|
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(
|
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
|
-
|
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
|
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
|
-
|
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 =
|
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
|
-
|
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
|
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
|
-
|
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(
|
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(
|
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
|
-
|
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
|
-
|
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
|
1055
|
-
|
223
|
+
/* Get current measurement */
|
224
|
+
profile->measurement = profile->measurer->measure();
|
1056
225
|
|
1057
|
-
|
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
|
-
|
245
|
+
fprintf(trace_file, "\n");
|
1080
246
|
}
|
1081
|
-
|
1082
|
-
|
1083
|
-
(unsigned int) thread_id, (unsigned int)
|
1084
|
-
fflush(
|
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
|
-
|
1088
|
-
|
1089
|
-
|
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
|
-
|
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(
|
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
|
-
|
312
|
+
method = get_method(event, klass, mid, thread_data->method_table);
|
1164
313
|
#else
|
1165
|
-
|
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 =
|
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
|
-
|
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
|
-
/*
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
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
|
-
|
1241
|
-
|
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
|
-
|
1248
|
-
|
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
|
-
|
1255
|
-
|
376
|
+
void
|
377
|
+
prof_remove_hook()
|
1256
378
|
{
|
1257
|
-
|
1258
|
-
|
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
|
-
|
1262
|
-
|
391
|
+
|
392
|
+
static int
|
393
|
+
collect_methods(st_data_t key, st_data_t value, st_data_t result)
|
1263
394
|
{
|
1264
|
-
|
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
|
1267
|
-
|
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
|
404
|
+
return ST_CONTINUE;
|
1271
405
|
}
|
1272
406
|
|
1273
|
-
|
1274
|
-
|
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
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
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
|
-
|
1287
|
-
threads -> Hash
|
417
|
+
VALUE methods = rb_ary_new();
|
1288
418
|
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
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
|
-
/*
|
1304
|
-
|
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
|
-
|
433
|
+
VALUE threads = profile->threads;
|
434
|
+
rb_gc_mark(threads);
|
1319
435
|
}
|
1320
436
|
|
1321
|
-
|
1322
|
-
|
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
|
-
|
1337
|
-
|
1338
|
-
|
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
|
-
|
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
|
-
|
448
|
+
prof_allocate(VALUE klass)
|
1406
449
|
{
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
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
|
-
|
1415
|
-
|
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
|
-
|
1422
|
-
|
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
|
-
|
1425
|
-
exclude_threads_tbl = st_init_numtable();
|
1426
|
-
|
1427
|
-
for (i=0; i < RARRAY_LEN(threads); ++i)
|
478
|
+
case 0:
|
1428
479
|
{
|
1429
|
-
|
1430
|
-
|
480
|
+
measurer = MEASURE_WALL_TIME;
|
481
|
+
exclude_threads = rb_ary_new();
|
482
|
+
break;
|
1431
483
|
}
|
1432
|
-
|
1433
|
-
|
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
|
-
|
1438
|
-
|
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
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
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
|
-
|
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
|
-
|
1479
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 ->
|
604
|
+
stop -> self
|
1546
605
|
|
1547
|
-
Stops collecting profile data
|
606
|
+
Stops collecting profile data.*/
|
1548
607
|
static VALUE
|
1549
608
|
prof_stop(VALUE self)
|
1550
609
|
{
|
1551
|
-
|
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
|
-
|
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
|
-
/*
|
1558
|
-
|
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
|
-
/*
|
1561
|
-
|
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
|
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
|
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(
|
1584
|
-
rb_protect(rb_yield,
|
1585
|
-
return prof_stop(
|
662
|
+
prof_start(profile);
|
663
|
+
rb_protect(rb_yield, profile, &result);
|
664
|
+
return prof_stop(profile);
|
1586
665
|
}
|
1587
666
|
|
1588
|
-
/*
|
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
|
-
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
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
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1736
|
-
|
1737
|
-
|
1738
|
-
|
1739
|
-
rb_define_method(
|
1740
|
-
rb_define_method(
|
1741
|
-
rb_define_method(
|
1742
|
-
rb_define_method(
|
1743
|
-
rb_define_method(
|
1744
|
-
rb_define_method(
|
1745
|
-
rb_define_method(
|
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
|
}
|