ruby-prof 0.18.0-x64-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.
- checksums.yaml +7 -0
- data/CHANGES +500 -0
- data/LICENSE +25 -0
- data/README.rdoc +487 -0
- data/Rakefile +113 -0
- data/bin/ruby-prof +345 -0
- data/bin/ruby-prof-check-trace +45 -0
- data/examples/flat.txt +50 -0
- data/examples/graph.dot +84 -0
- data/examples/graph.html +823 -0
- data/examples/graph.txt +139 -0
- data/examples/multi.flat.txt +23 -0
- data/examples/multi.graph.html +760 -0
- data/examples/multi.grind.dat +114 -0
- data/examples/multi.stack.html +547 -0
- data/examples/stack.html +547 -0
- data/ext/ruby_prof/extconf.rb +68 -0
- data/ext/ruby_prof/rp_call_info.c +425 -0
- data/ext/ruby_prof/rp_call_info.h +53 -0
- data/ext/ruby_prof/rp_measure.c +40 -0
- data/ext/ruby_prof/rp_measure.h +45 -0
- data/ext/ruby_prof/rp_measure_allocations.c +76 -0
- data/ext/ruby_prof/rp_measure_cpu_time.c +136 -0
- data/ext/ruby_prof/rp_measure_gc_runs.c +73 -0
- data/ext/ruby_prof/rp_measure_gc_time.c +60 -0
- data/ext/ruby_prof/rp_measure_memory.c +77 -0
- data/ext/ruby_prof/rp_measure_process_time.c +71 -0
- data/ext/ruby_prof/rp_measure_wall_time.c +45 -0
- data/ext/ruby_prof/rp_method.c +630 -0
- data/ext/ruby_prof/rp_method.h +75 -0
- data/ext/ruby_prof/rp_stack.c +173 -0
- data/ext/ruby_prof/rp_stack.h +63 -0
- data/ext/ruby_prof/rp_thread.c +277 -0
- data/ext/ruby_prof/rp_thread.h +27 -0
- data/ext/ruby_prof/ruby_prof.c +794 -0
- data/ext/ruby_prof/ruby_prof.h +60 -0
- data/ext/ruby_prof/vc/ruby_prof.sln +31 -0
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +141 -0
- data/lib/2.6.3/ruby_prof.so +0 -0
- data/lib/ruby-prof.rb +68 -0
- data/lib/ruby-prof/aggregate_call_info.rb +76 -0
- data/lib/ruby-prof/assets/call_stack_printer.css.html +117 -0
- data/lib/ruby-prof/assets/call_stack_printer.js.html +385 -0
- data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
- data/lib/ruby-prof/call_info.rb +115 -0
- data/lib/ruby-prof/call_info_visitor.rb +40 -0
- data/lib/ruby-prof/compatibility.rb +179 -0
- data/lib/ruby-prof/method_info.rb +121 -0
- data/lib/ruby-prof/printers/abstract_printer.rb +104 -0
- data/lib/ruby-prof/printers/call_info_printer.rb +41 -0
- data/lib/ruby-prof/printers/call_stack_printer.rb +265 -0
- data/lib/ruby-prof/printers/call_tree_printer.rb +143 -0
- data/lib/ruby-prof/printers/dot_printer.rb +132 -0
- data/lib/ruby-prof/printers/flat_printer.rb +70 -0
- data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +83 -0
- data/lib/ruby-prof/printers/graph_html_printer.rb +249 -0
- data/lib/ruby-prof/printers/graph_printer.rb +116 -0
- data/lib/ruby-prof/printers/multi_printer.rb +84 -0
- data/lib/ruby-prof/profile.rb +26 -0
- data/lib/ruby-prof/profile/exclude_common_methods.rb +207 -0
- data/lib/ruby-prof/profile/legacy_method_elimination.rb +50 -0
- data/lib/ruby-prof/rack.rb +174 -0
- data/lib/ruby-prof/task.rb +147 -0
- data/lib/ruby-prof/thread.rb +35 -0
- data/lib/ruby-prof/version.rb +3 -0
- data/lib/unprof.rb +10 -0
- data/ruby-prof.gemspec +58 -0
- data/test/abstract_printer_test.rb +53 -0
- data/test/aggregate_test.rb +136 -0
- data/test/basic_test.rb +128 -0
- data/test/block_test.rb +74 -0
- data/test/call_info_test.rb +78 -0
- data/test/call_info_visitor_test.rb +31 -0
- data/test/duplicate_names_test.rb +32 -0
- data/test/dynamic_method_test.rb +55 -0
- data/test/enumerable_test.rb +21 -0
- data/test/exceptions_test.rb +24 -0
- data/test/exclude_methods_test.rb +146 -0
- data/test/exclude_threads_test.rb +53 -0
- data/test/fiber_test.rb +79 -0
- data/test/issue137_test.rb +63 -0
- data/test/line_number_test.rb +80 -0
- data/test/measure_allocations_test.rb +26 -0
- data/test/measure_cpu_time_test.rb +212 -0
- data/test/measure_gc_runs_test.rb +32 -0
- data/test/measure_gc_time_test.rb +36 -0
- data/test/measure_memory_test.rb +33 -0
- data/test/measure_process_time_test.rb +61 -0
- data/test/measure_wall_time_test.rb +255 -0
- data/test/method_elimination_test.rb +84 -0
- data/test/module_test.rb +45 -0
- data/test/multi_printer_test.rb +104 -0
- data/test/no_method_class_test.rb +15 -0
- data/test/pause_resume_test.rb +166 -0
- data/test/prime.rb +54 -0
- data/test/printers_test.rb +275 -0
- data/test/printing_recursive_graph_test.rb +127 -0
- data/test/rack_test.rb +157 -0
- data/test/recursive_test.rb +215 -0
- data/test/singleton_test.rb +38 -0
- data/test/stack_printer_test.rb +77 -0
- data/test/stack_test.rb +138 -0
- data/test/start_stop_test.rb +112 -0
- data/test/test_helper.rb +267 -0
- data/test/thread_test.rb +187 -0
- data/test/unique_call_path_test.rb +202 -0
- data/test/yarv_test.rb +55 -0
- metadata +199 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
/* Copyright (C) 2005-2019 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
|
+
/* :nodoc: */
|
5
|
+
|
6
|
+
#include "ruby_prof.h"
|
7
|
+
|
8
|
+
static VALUE cMeasureMemory;
|
9
|
+
|
10
|
+
|
11
|
+
#if defined(HAVE_RB_GC_ALLOCATED_SIZE)
|
12
|
+
VALUE rb_gc_allocated_size();
|
13
|
+
#endif
|
14
|
+
|
15
|
+
#if defined(HAVE_RB_GC_MALLOC_ALLOCATED_SIZE)
|
16
|
+
size_t rb_gc_malloc_allocated_size();
|
17
|
+
#endif
|
18
|
+
|
19
|
+
#if defined(HAVE_RB_HEAP_TOTAL_MEM)
|
20
|
+
//FIXME: did not find the patch to check prototype, assuming it to return size_t
|
21
|
+
size_t rb_heap_total_mem();
|
22
|
+
#endif
|
23
|
+
|
24
|
+
static double
|
25
|
+
measure_memory()
|
26
|
+
{
|
27
|
+
#if defined(HAVE_RB_GC_ALLOCATED_SIZE)
|
28
|
+
#define MEASURE_MEMORY_ENABLED Qtrue
|
29
|
+
#if defined(HAVE_LONG_LONG)
|
30
|
+
return NUM2LL(rb_gc_allocated_size()) / 1024.0;
|
31
|
+
#else
|
32
|
+
return NUM2ULONG(rb_gc_allocated_size()) / 1024.0;
|
33
|
+
#endif
|
34
|
+
|
35
|
+
#elif defined(HAVE_RB_GC_MALLOC_ALLOCATED_SIZE)
|
36
|
+
#define MEASURE_MEMORY_ENABLED Qtrue
|
37
|
+
return rb_gc_malloc_allocated_size() / 1024.0;
|
38
|
+
|
39
|
+
#elif defined(HAVE_RB_GC_TOTAL_MALLOCED_BYTES)
|
40
|
+
#define MEASURE_MEMORY_ENABLED Qtrue
|
41
|
+
return rb_gc_total_malloced_bytes() / 1024.0;
|
42
|
+
|
43
|
+
#elif defined(HAVE_RB_HEAP_TOTAL_MEM)
|
44
|
+
#define MEASURE_MEMORY_ENABLED Qtrue
|
45
|
+
return rb_heap_total_mem() / 1024.0;
|
46
|
+
|
47
|
+
#else
|
48
|
+
#define MEASURE_MEMORY_ENABLED Qfalse
|
49
|
+
return 0;
|
50
|
+
#endif
|
51
|
+
}
|
52
|
+
|
53
|
+
prof_measurer_t* prof_measurer_memory()
|
54
|
+
{
|
55
|
+
prof_measurer_t* measure = ALLOC(prof_measurer_t);
|
56
|
+
measure->measure = measure_memory;
|
57
|
+
return measure;
|
58
|
+
}
|
59
|
+
|
60
|
+
/* call-seq:
|
61
|
+
measure_process_time -> float
|
62
|
+
|
63
|
+
Returns the process time.*/
|
64
|
+
static VALUE
|
65
|
+
prof_measure_memory(VALUE self)
|
66
|
+
{
|
67
|
+
return rb_float_new(measure_memory());
|
68
|
+
}
|
69
|
+
|
70
|
+
void rp_init_measure_memory()
|
71
|
+
{
|
72
|
+
rb_define_const(mProf, "MEMORY", INT2NUM(MEASURE_MEMORY));
|
73
|
+
rb_define_const(mProf, "MEMORY_ENABLED", MEASURE_MEMORY_ENABLED);
|
74
|
+
|
75
|
+
cMeasureMemory = rb_define_class_under(mMeasure, "Memory", rb_cObject);
|
76
|
+
rb_define_singleton_method(cMeasureMemory, "measure", prof_measure_memory, 0);
|
77
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
/* Copyright (C) 2005-2013 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
|
+
#include <time.h>
|
6
|
+
|
7
|
+
static VALUE cMeasureProcessTime;
|
8
|
+
|
9
|
+
static double
|
10
|
+
measure_process_time()
|
11
|
+
{
|
12
|
+
#if defined(__linux__)
|
13
|
+
struct timespec clock;
|
14
|
+
clock_gettime(CLOCK_PROCESS_CPUTIME_ID , &clock);
|
15
|
+
return clock.tv_sec + (clock.tv_nsec/1000000000.0);
|
16
|
+
#elif defined(_win32)
|
17
|
+
FILETIME createTime;
|
18
|
+
FILETIME exitTime;
|
19
|
+
FILETIME sysTime;
|
20
|
+
FILETIME cpuTime;
|
21
|
+
|
22
|
+
ULARGE_INTEGER sysTimeInt;
|
23
|
+
ULARGE_INTEGER cpuTimeInt;
|
24
|
+
ULONGLONG totalTime;
|
25
|
+
|
26
|
+
GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &sysTime, &cpuTime);
|
27
|
+
|
28
|
+
/* Doing this based on MSFT's recommendation in the FILETIME structure documentation at
|
29
|
+
http://msdn.microsoft.com/en-us/library/ms724284%28VS.85%29.aspx*/
|
30
|
+
|
31
|
+
sysTimeInt.LowPart = sysTime.dwLowDateTime;
|
32
|
+
sysTimeInt.HighPart = sysTime.dwHighDateTime;
|
33
|
+
cpuTimeInt.LowPart = cpuTime.dwLowDateTime;
|
34
|
+
cpuTimeInt.HighPart = cpuTime.dwHighDateTime;
|
35
|
+
|
36
|
+
totalTime = sysTimeInt.QuadPart + cpuTimeInt.QuadPart;
|
37
|
+
|
38
|
+
// Times are in 100-nanosecond time units. So instead of 10-9 use 10-7
|
39
|
+
return totalTime / 10000000.0;
|
40
|
+
#else
|
41
|
+
return ((double)clock()) / CLOCKS_PER_SEC;
|
42
|
+
#endif
|
43
|
+
}
|
44
|
+
|
45
|
+
/* call-seq:
|
46
|
+
measure_process_time -> float
|
47
|
+
|
48
|
+
Returns the process time.*/
|
49
|
+
static VALUE
|
50
|
+
prof_measure_process_time(VALUE self)
|
51
|
+
{
|
52
|
+
return rb_float_new(measure_process_time());
|
53
|
+
}
|
54
|
+
|
55
|
+
prof_measurer_t* prof_measurer_process_time()
|
56
|
+
{
|
57
|
+
prof_measurer_t* measure = ALLOC(prof_measurer_t);
|
58
|
+
measure->measure = measure_process_time;
|
59
|
+
return measure;
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
void rp_init_measure_process_time()
|
64
|
+
{
|
65
|
+
rb_define_const(mProf, "CLOCKS_PER_SEC", INT2NUM(CLOCKS_PER_SEC));
|
66
|
+
rb_define_const(mProf, "PROCESS_TIME", INT2NUM(MEASURE_PROCESS_TIME));
|
67
|
+
rb_define_const(mProf, "PROCESS_TIME_ENABLED", Qtrue);
|
68
|
+
|
69
|
+
cMeasureProcessTime = rb_define_class_under(mMeasure, "ProcessTime", rb_cObject);
|
70
|
+
rb_define_singleton_method(cMeasureProcessTime, "measure", prof_measure_process_time, 0);
|
71
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
/* Copyright (C) 2005-2019 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
|
+
/* :nodoc: */
|
5
|
+
#include "ruby_prof.h"
|
6
|
+
#if HAVE_GETTIMEOFDAY && !defined(_WIN32)
|
7
|
+
#include <sys/time.h>
|
8
|
+
#endif
|
9
|
+
|
10
|
+
static VALUE cMeasureWallTime;
|
11
|
+
|
12
|
+
static double
|
13
|
+
measure_wall_time()
|
14
|
+
{
|
15
|
+
struct timeval tv;
|
16
|
+
gettimeofday(&tv, NULL);
|
17
|
+
return tv.tv_sec + (tv.tv_usec/1000000.0);
|
18
|
+
}
|
19
|
+
|
20
|
+
prof_measurer_t* prof_measurer_wall_time()
|
21
|
+
{
|
22
|
+
prof_measurer_t* measure = ALLOC(prof_measurer_t);
|
23
|
+
measure->measure = measure_wall_time;
|
24
|
+
return measure;
|
25
|
+
}
|
26
|
+
|
27
|
+
/* Document-method: prof_measure_wall_time
|
28
|
+
call-seq:
|
29
|
+
measure_wall_time -> float
|
30
|
+
|
31
|
+
Returns the wall time.*/
|
32
|
+
static VALUE
|
33
|
+
prof_measure_wall_time(VALUE self)
|
34
|
+
{
|
35
|
+
return rb_float_new(measure_wall_time());
|
36
|
+
}
|
37
|
+
|
38
|
+
void rp_init_measure_wall_time()
|
39
|
+
{
|
40
|
+
rb_define_const(mProf, "WALL_TIME", INT2NUM(MEASURE_WALL_TIME));
|
41
|
+
rb_define_const(mProf, "WALL_TIME_ENABLED", Qtrue);
|
42
|
+
|
43
|
+
cMeasureWallTime = rb_define_class_under(mMeasure, "WallTime", rb_cObject);
|
44
|
+
rb_define_singleton_method(cMeasureWallTime, "measure", prof_measure_wall_time, 0);
|
45
|
+
}
|
@@ -0,0 +1,630 @@
|
|
1
|
+
/* Copyright (C) 2005-2019 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
|
+
#define RP_REL_GET(r, off) ((r) & (1 << (off)))
|
7
|
+
#define RP_REL_SET(r, off) \
|
8
|
+
do { \
|
9
|
+
r |= (1 << (off)); \
|
10
|
+
} while (0)
|
11
|
+
|
12
|
+
VALUE cMethodInfo;
|
13
|
+
|
14
|
+
/* ================ Helper Functions =================*/
|
15
|
+
static VALUE
|
16
|
+
figure_singleton_name(VALUE klass)
|
17
|
+
{
|
18
|
+
volatile VALUE attached, super;
|
19
|
+
volatile VALUE attached_str, super_str;
|
20
|
+
volatile VALUE result = Qnil;
|
21
|
+
|
22
|
+
/* We have come across a singleton object. First
|
23
|
+
figure out what it is attached to.*/
|
24
|
+
attached = rb_iv_get(klass, "__attached__");
|
25
|
+
|
26
|
+
/* Is this a singleton class acting as a metaclass? */
|
27
|
+
if (BUILTIN_TYPE(attached) == T_CLASS)
|
28
|
+
{
|
29
|
+
attached_str = rb_class_name(attached);
|
30
|
+
result = rb_str_new2("<Class::");
|
31
|
+
rb_str_append(result, attached_str);
|
32
|
+
rb_str_cat2(result, ">");
|
33
|
+
}
|
34
|
+
|
35
|
+
/* Is this for singleton methods on a module? */
|
36
|
+
else if (BUILTIN_TYPE(attached) == T_MODULE)
|
37
|
+
{
|
38
|
+
attached_str = rb_class_name(attached);
|
39
|
+
result = rb_str_new2("<Module::");
|
40
|
+
rb_str_append(result, attached_str);
|
41
|
+
rb_str_cat2(result, ">");
|
42
|
+
}
|
43
|
+
|
44
|
+
/* Is this for singleton methods on an object? */
|
45
|
+
else if (BUILTIN_TYPE(attached) == T_OBJECT)
|
46
|
+
{
|
47
|
+
/* Make sure to get the super class so that we don't
|
48
|
+
mistakenly grab a T_ICLASS which would lead to
|
49
|
+
unknown method errors. */
|
50
|
+
super = rb_class_superclass(klass);
|
51
|
+
super_str = rb_class_name(super);
|
52
|
+
result = rb_str_new2("<Object::");
|
53
|
+
rb_str_append(result, super_str);
|
54
|
+
rb_str_cat2(result, ">");
|
55
|
+
}
|
56
|
+
|
57
|
+
/* Ok, this could be other things like an array made put onto
|
58
|
+
a singleton object (yeah, it happens, see the singleton
|
59
|
+
objects test case). */
|
60
|
+
else
|
61
|
+
{
|
62
|
+
result = rb_any_to_s(klass);
|
63
|
+
}
|
64
|
+
|
65
|
+
return result;
|
66
|
+
}
|
67
|
+
|
68
|
+
static VALUE
|
69
|
+
klass_name(VALUE klass)
|
70
|
+
{
|
71
|
+
volatile VALUE result = Qnil;
|
72
|
+
|
73
|
+
if (klass == 0 || klass == Qnil)
|
74
|
+
{
|
75
|
+
result = rb_str_new2("[global]");
|
76
|
+
}
|
77
|
+
else if (BUILTIN_TYPE(klass) == T_MODULE)
|
78
|
+
{
|
79
|
+
result = rb_class_name(klass);
|
80
|
+
}
|
81
|
+
else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
|
82
|
+
{
|
83
|
+
result = figure_singleton_name(klass);
|
84
|
+
}
|
85
|
+
else if (BUILTIN_TYPE(klass) == T_CLASS)
|
86
|
+
{
|
87
|
+
result = rb_class_name(klass);
|
88
|
+
}
|
89
|
+
else
|
90
|
+
{
|
91
|
+
/* Should never happen. */
|
92
|
+
result = rb_str_new2("[unknown]");
|
93
|
+
}
|
94
|
+
|
95
|
+
return result;
|
96
|
+
}
|
97
|
+
|
98
|
+
static VALUE
|
99
|
+
method_name(ID mid)
|
100
|
+
{
|
101
|
+
volatile VALUE name = Qnil;
|
102
|
+
|
103
|
+
if (RTEST(mid)) {
|
104
|
+
name = rb_id2str(mid);
|
105
|
+
return rb_str_dup(name);
|
106
|
+
} else {
|
107
|
+
return rb_str_new2("[no method]");
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
static VALUE
|
112
|
+
full_name(VALUE klass, ID mid)
|
113
|
+
{
|
114
|
+
volatile VALUE klass_str, method_str;
|
115
|
+
volatile VALUE result = Qnil;
|
116
|
+
|
117
|
+
klass_str = klass_name(klass);
|
118
|
+
method_str = method_name(mid);
|
119
|
+
|
120
|
+
result = rb_str_dup(klass_str);
|
121
|
+
rb_str_cat2(result, "#");
|
122
|
+
rb_str_append(result, method_str);
|
123
|
+
|
124
|
+
return result;
|
125
|
+
}
|
126
|
+
|
127
|
+
static VALUE
|
128
|
+
source_klass_name(VALUE source_klass)
|
129
|
+
{
|
130
|
+
volatile VALUE klass_str;
|
131
|
+
volatile VALUE result = Qnil;
|
132
|
+
|
133
|
+
if (RTEST(source_klass)) {
|
134
|
+
klass_str = rb_class_name(source_klass);
|
135
|
+
result = rb_str_dup(klass_str);
|
136
|
+
} else {
|
137
|
+
result = rb_str_new2("[global]");
|
138
|
+
}
|
139
|
+
|
140
|
+
return result;
|
141
|
+
}
|
142
|
+
|
143
|
+
static VALUE
|
144
|
+
calltree_name(VALUE source_klass, int relation, ID mid)
|
145
|
+
{
|
146
|
+
volatile VALUE klass_str, klass_path, joiner;
|
147
|
+
volatile VALUE method_str;
|
148
|
+
volatile VALUE result = Qnil;
|
149
|
+
|
150
|
+
klass_str = source_klass_name(source_klass);
|
151
|
+
method_str = method_name(mid);
|
152
|
+
|
153
|
+
klass_path = rb_str_split(klass_str, "::");
|
154
|
+
joiner = rb_str_new2("/");
|
155
|
+
result = rb_ary_join(klass_path, joiner);
|
156
|
+
|
157
|
+
rb_str_cat2(result, "::");
|
158
|
+
|
159
|
+
if (RP_REL_GET(relation, kObjectSingleton)) {
|
160
|
+
rb_str_cat2(result, "*");
|
161
|
+
}
|
162
|
+
|
163
|
+
if (RP_REL_GET(relation, kModuleSingleton)) {
|
164
|
+
rb_str_cat2(result, "^");
|
165
|
+
}
|
166
|
+
|
167
|
+
rb_str_append(result, method_str);
|
168
|
+
|
169
|
+
return result;
|
170
|
+
}
|
171
|
+
|
172
|
+
void
|
173
|
+
method_key(prof_method_key_t* key, VALUE klass, ID mid)
|
174
|
+
{
|
175
|
+
/* Is this an include for a module? If so get the actual
|
176
|
+
module class since we want to combine all profiling
|
177
|
+
results for that module. */
|
178
|
+
if (klass != 0 && BUILTIN_TYPE(klass) == T_ICLASS) {
|
179
|
+
klass = RBASIC(klass)->klass;
|
180
|
+
}
|
181
|
+
|
182
|
+
key->klass = klass;
|
183
|
+
key->mid = mid;
|
184
|
+
key->key = (klass << 4) + (mid << 2);
|
185
|
+
}
|
186
|
+
|
187
|
+
/* ================ prof_method_t =================*/
|
188
|
+
|
189
|
+
prof_method_t*
|
190
|
+
prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
|
191
|
+
{
|
192
|
+
prof_method_t *result = ALLOC(prof_method_t);
|
193
|
+
|
194
|
+
result->key = ALLOC(prof_method_key_t);
|
195
|
+
method_key(result->key, klass, mid);
|
196
|
+
|
197
|
+
result->excluded = 0;
|
198
|
+
result->recursive = 0;
|
199
|
+
|
200
|
+
result->call_infos = prof_call_infos_create();
|
201
|
+
result->visits = 0;
|
202
|
+
|
203
|
+
result->object = Qnil;
|
204
|
+
|
205
|
+
if (source_file != NULL)
|
206
|
+
{
|
207
|
+
size_t len = strlen(source_file) + 1;
|
208
|
+
char *buffer = ALLOC_N(char, len);
|
209
|
+
|
210
|
+
MEMCPY(buffer, source_file, char, len);
|
211
|
+
result->source_file = buffer;
|
212
|
+
} else {
|
213
|
+
result->source_file = source_file;
|
214
|
+
}
|
215
|
+
|
216
|
+
result->source_klass = Qnil;
|
217
|
+
result->line = line;
|
218
|
+
|
219
|
+
result->resolved = 0;
|
220
|
+
result->relation = 0;
|
221
|
+
|
222
|
+
return result;
|
223
|
+
}
|
224
|
+
|
225
|
+
prof_method_t*
|
226
|
+
prof_method_create_excluded(VALUE klass, ID mid)
|
227
|
+
{
|
228
|
+
prof_method_t *result = ALLOC(prof_method_t);
|
229
|
+
|
230
|
+
result->key = ALLOC(prof_method_key_t);
|
231
|
+
method_key(result->key, klass, mid);
|
232
|
+
|
233
|
+
/* Invisible with this flag set. */
|
234
|
+
result->excluded = 1;
|
235
|
+
result->recursive = 0;
|
236
|
+
|
237
|
+
result->call_infos = 0;
|
238
|
+
result->visits = 0;
|
239
|
+
|
240
|
+
result->object = Qnil;
|
241
|
+
result->source_klass = Qnil;
|
242
|
+
result->line = 0;
|
243
|
+
|
244
|
+
result->resolved = 0;
|
245
|
+
result->relation = 0;
|
246
|
+
|
247
|
+
return result;
|
248
|
+
}
|
249
|
+
|
250
|
+
/* The underlying c structures are freed when the parent profile is freed.
|
251
|
+
However, on shutdown the Ruby GC frees objects in any will-nilly order.
|
252
|
+
That means the ruby thread object wrapping the c thread struct may
|
253
|
+
be freed before the parent profile. Thus we add in a free function
|
254
|
+
for the garbage collector so that if it does get called will nil
|
255
|
+
out our Ruby object reference.*/
|
256
|
+
static void
|
257
|
+
prof_method_ruby_gc_free(prof_method_t* method)
|
258
|
+
{
|
259
|
+
/* Has this thread object been accessed by Ruby? If
|
260
|
+
yes clean it up so to avoid a segmentation fault. */
|
261
|
+
if (method->object != Qnil)
|
262
|
+
{
|
263
|
+
RDATA(method->object)->data = NULL;
|
264
|
+
RDATA(method->object)->dfree = NULL;
|
265
|
+
RDATA(method->object)->dmark = NULL;
|
266
|
+
}
|
267
|
+
method->object = Qnil;
|
268
|
+
}
|
269
|
+
|
270
|
+
static void
|
271
|
+
prof_method_free(prof_method_t* method)
|
272
|
+
{
|
273
|
+
prof_method_ruby_gc_free(method);
|
274
|
+
|
275
|
+
if (method->call_infos) {
|
276
|
+
prof_call_infos_free(method->call_infos);
|
277
|
+
xfree(method->call_infos);
|
278
|
+
}
|
279
|
+
|
280
|
+
xfree(method->key);
|
281
|
+
|
282
|
+
method->key = NULL;
|
283
|
+
method->source_klass = Qnil;
|
284
|
+
|
285
|
+
xfree(method);
|
286
|
+
}
|
287
|
+
|
288
|
+
void
|
289
|
+
prof_method_mark(prof_method_t *method)
|
290
|
+
{
|
291
|
+
if (method->key->klass) {
|
292
|
+
rb_gc_mark(method->key->klass);
|
293
|
+
}
|
294
|
+
|
295
|
+
if (method->source_klass) {
|
296
|
+
rb_gc_mark(method->source_klass);
|
297
|
+
}
|
298
|
+
|
299
|
+
if (method->object) {
|
300
|
+
rb_gc_mark(method->object);
|
301
|
+
}
|
302
|
+
|
303
|
+
if (method->call_infos) {
|
304
|
+
prof_call_infos_mark(method->call_infos);
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
308
|
+
static VALUE
|
309
|
+
resolve_source_klass(prof_method_t* method)
|
310
|
+
{
|
311
|
+
volatile VALUE klass, next_klass;
|
312
|
+
volatile VALUE attached;
|
313
|
+
unsigned int relation;
|
314
|
+
|
315
|
+
/* We want to group methods according to their source-level
|
316
|
+
definitions, not their implementation class. Follow module
|
317
|
+
inclusions and singleton classes back to a meaningful root
|
318
|
+
while keeping track of these relationships. */
|
319
|
+
|
320
|
+
if (method->resolved) {
|
321
|
+
return method->source_klass;
|
322
|
+
}
|
323
|
+
|
324
|
+
klass = method->key->klass;
|
325
|
+
relation = 0;
|
326
|
+
|
327
|
+
while (1)
|
328
|
+
{
|
329
|
+
/* This is a global/unknown class */
|
330
|
+
if (klass == 0 || klass == Qnil)
|
331
|
+
break;
|
332
|
+
|
333
|
+
/* Is this a singleton class? (most common case) */
|
334
|
+
if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
|
335
|
+
{
|
336
|
+
/* We have come across a singleton object. First
|
337
|
+
figure out what it is attached to.*/
|
338
|
+
attached = rb_iv_get(klass, "__attached__");
|
339
|
+
|
340
|
+
/* Is this a singleton class acting as a metaclass?
|
341
|
+
Or for singleton methods on a module? */
|
342
|
+
if (BUILTIN_TYPE(attached) == T_CLASS ||
|
343
|
+
BUILTIN_TYPE(attached) == T_MODULE)
|
344
|
+
{
|
345
|
+
RP_REL_SET(relation, kModuleSingleton);
|
346
|
+
klass = attached;
|
347
|
+
}
|
348
|
+
/* Is this for singleton methods on an object? */
|
349
|
+
else if (BUILTIN_TYPE(attached) == T_OBJECT)
|
350
|
+
{
|
351
|
+
RP_REL_SET(relation, kObjectSingleton);
|
352
|
+
next_klass = rb_class_superclass(klass);
|
353
|
+
klass = next_klass;
|
354
|
+
}
|
355
|
+
/* This is a singleton of an instance of a builtin type. */
|
356
|
+
else
|
357
|
+
{
|
358
|
+
RP_REL_SET(relation, kObjectSingleton);
|
359
|
+
next_klass = rb_class_superclass(klass);
|
360
|
+
klass = next_klass;
|
361
|
+
}
|
362
|
+
}
|
363
|
+
/* Is this an include for a module? If so get the actual
|
364
|
+
module class since we want to combine all profiling
|
365
|
+
results for that module. */
|
366
|
+
else if (BUILTIN_TYPE(klass) == T_ICLASS)
|
367
|
+
{
|
368
|
+
RP_REL_SET(relation, kModuleIncludee);
|
369
|
+
next_klass = RBASIC(klass)->klass;
|
370
|
+
klass = next_klass;
|
371
|
+
}
|
372
|
+
/* No transformations apply; so bail. */
|
373
|
+
else
|
374
|
+
{
|
375
|
+
break;
|
376
|
+
}
|
377
|
+
}
|
378
|
+
|
379
|
+
method->resolved = 1;
|
380
|
+
method->relation = relation;
|
381
|
+
method->source_klass = klass;
|
382
|
+
|
383
|
+
return klass;
|
384
|
+
}
|
385
|
+
|
386
|
+
VALUE
|
387
|
+
prof_method_wrap(prof_method_t *result)
|
388
|
+
{
|
389
|
+
if (result->object == Qnil) {
|
390
|
+
result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_ruby_gc_free, result);
|
391
|
+
}
|
392
|
+
return result->object;
|
393
|
+
}
|
394
|
+
|
395
|
+
static prof_method_t *
|
396
|
+
get_prof_method(VALUE self)
|
397
|
+
{
|
398
|
+
/* Can't use Data_Get_Struct because that triggers the event hook
|
399
|
+
ending up in endless recursion. */
|
400
|
+
prof_method_t* result = DATA_PTR(self);
|
401
|
+
|
402
|
+
if (!result) {
|
403
|
+
rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
|
404
|
+
}
|
405
|
+
|
406
|
+
return result;
|
407
|
+
}
|
408
|
+
|
409
|
+
/* ================ Method Table =================*/
|
410
|
+
int
|
411
|
+
method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2)
|
412
|
+
{
|
413
|
+
return (key1->klass != key2->klass) || (key1->mid != key2->mid);
|
414
|
+
}
|
415
|
+
|
416
|
+
st_index_t
|
417
|
+
method_table_hash(prof_method_key_t *key)
|
418
|
+
{
|
419
|
+
return key->key;
|
420
|
+
}
|
421
|
+
|
422
|
+
struct st_hash_type type_method_hash = {
|
423
|
+
method_table_cmp,
|
424
|
+
method_table_hash
|
425
|
+
};
|
426
|
+
|
427
|
+
st_table *
|
428
|
+
method_table_create()
|
429
|
+
{
|
430
|
+
return st_init_table(&type_method_hash);
|
431
|
+
}
|
432
|
+
|
433
|
+
static int
|
434
|
+
method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
|
435
|
+
{
|
436
|
+
prof_method_free((prof_method_t *)value);
|
437
|
+
return ST_CONTINUE;
|
438
|
+
}
|
439
|
+
|
440
|
+
void
|
441
|
+
method_table_free(st_table *table)
|
442
|
+
{
|
443
|
+
st_foreach(table, method_table_free_iterator, 0);
|
444
|
+
st_free_table(table);
|
445
|
+
}
|
446
|
+
|
447
|
+
size_t
|
448
|
+
method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
|
449
|
+
{
|
450
|
+
return st_insert(table, (st_data_t) key, (st_data_t) val);
|
451
|
+
}
|
452
|
+
|
453
|
+
prof_method_t *
|
454
|
+
method_table_lookup(st_table *table, const prof_method_key_t* key)
|
455
|
+
{
|
456
|
+
st_data_t val;
|
457
|
+
if (st_lookup(table, (st_data_t)key, &val)) {
|
458
|
+
return (prof_method_t *) val;
|
459
|
+
} else {
|
460
|
+
return NULL;
|
461
|
+
}
|
462
|
+
}
|
463
|
+
|
464
|
+
/* ================ Method Info =================*/
|
465
|
+
/* Document-class: RubyProf::MethodInfo
|
466
|
+
The RubyProf::MethodInfo class stores profiling data for a method.
|
467
|
+
One instance of the RubyProf::MethodInfo class is created per method
|
468
|
+
called per thread. Thus, if a method is called in two different
|
469
|
+
thread then there will be two RubyProf::MethodInfo objects
|
470
|
+
created. RubyProf::MethodInfo objects can be accessed via
|
471
|
+
the RubyProf::Profile object.
|
472
|
+
*/
|
473
|
+
|
474
|
+
/* call-seq:
|
475
|
+
line_no -> int
|
476
|
+
|
477
|
+
returns the line number of the method */
|
478
|
+
static VALUE
|
479
|
+
prof_method_line(VALUE self)
|
480
|
+
{
|
481
|
+
int line = get_prof_method(self)->line;
|
482
|
+
return rb_int_new(line);
|
483
|
+
}
|
484
|
+
|
485
|
+
/* call-seq:
|
486
|
+
source_file => string
|
487
|
+
|
488
|
+
return the source file of the method
|
489
|
+
*/
|
490
|
+
static VALUE prof_method_source_file(VALUE self)
|
491
|
+
{
|
492
|
+
const char* sf = get_prof_method(self)->source_file;
|
493
|
+
if(!sf) {
|
494
|
+
return rb_str_new2("ruby_runtime");
|
495
|
+
} else {
|
496
|
+
return rb_str_new2(sf);
|
497
|
+
}
|
498
|
+
}
|
499
|
+
|
500
|
+
|
501
|
+
/* call-seq:
|
502
|
+
method_class -> klass
|
503
|
+
|
504
|
+
Returns the Ruby klass that owns this method. */
|
505
|
+
static VALUE
|
506
|
+
prof_method_klass(VALUE self)
|
507
|
+
{
|
508
|
+
prof_method_t *result = get_prof_method(self);
|
509
|
+
return result->key->klass;
|
510
|
+
}
|
511
|
+
|
512
|
+
/* call-seq:
|
513
|
+
method_id -> ID
|
514
|
+
|
515
|
+
Returns the id of this method. */
|
516
|
+
static VALUE
|
517
|
+
prof_method_id(VALUE self)
|
518
|
+
{
|
519
|
+
prof_method_t *result = get_prof_method(self);
|
520
|
+
return ID2SYM(result->key->mid);
|
521
|
+
}
|
522
|
+
|
523
|
+
/* call-seq:
|
524
|
+
klass_name -> string
|
525
|
+
|
526
|
+
Returns the name of this method's class. Singleton classes
|
527
|
+
will have the form <Object::Object>. */
|
528
|
+
|
529
|
+
static VALUE
|
530
|
+
prof_klass_name(VALUE self)
|
531
|
+
{
|
532
|
+
prof_method_t *method = get_prof_method(self);
|
533
|
+
return klass_name(method->key->klass);
|
534
|
+
}
|
535
|
+
|
536
|
+
/* call-seq:
|
537
|
+
method_name -> string
|
538
|
+
|
539
|
+
Returns the name of this method in the format Object#method. Singletons
|
540
|
+
methods will be returned in the format <Object::Object>#method.*/
|
541
|
+
|
542
|
+
static VALUE
|
543
|
+
prof_method_name(VALUE self)
|
544
|
+
{
|
545
|
+
prof_method_t *method = get_prof_method(self);
|
546
|
+
return method_name(method->key->mid);
|
547
|
+
}
|
548
|
+
|
549
|
+
/* call-seq:
|
550
|
+
full_name -> string
|
551
|
+
|
552
|
+
Returns the full name of this method in the format Object#method.*/
|
553
|
+
|
554
|
+
static VALUE
|
555
|
+
prof_full_name(VALUE self)
|
556
|
+
{
|
557
|
+
prof_method_t *method = get_prof_method(self);
|
558
|
+
return full_name(method->key->klass, method->key->mid);
|
559
|
+
}
|
560
|
+
|
561
|
+
/* call-seq:
|
562
|
+
call_infos -> Array of call_info
|
563
|
+
|
564
|
+
Returns an array of call info objects that contain profiling information
|
565
|
+
about the current method.*/
|
566
|
+
static VALUE
|
567
|
+
prof_method_call_infos(VALUE self)
|
568
|
+
{
|
569
|
+
prof_method_t *method = get_prof_method(self);
|
570
|
+
if (method->call_infos->object == Qnil) {
|
571
|
+
method->call_infos->object = prof_call_infos_wrap(method->call_infos);
|
572
|
+
}
|
573
|
+
return method->call_infos->object;
|
574
|
+
}
|
575
|
+
|
576
|
+
/* call-seq:
|
577
|
+
recursive? -> boolean
|
578
|
+
|
579
|
+
Returns the true if this method is recursive */
|
580
|
+
static VALUE
|
581
|
+
prof_method_recursive(VALUE self)
|
582
|
+
{
|
583
|
+
prof_method_t *method = get_prof_method(self);
|
584
|
+
return method->recursive ? Qtrue : Qfalse;
|
585
|
+
}
|
586
|
+
|
587
|
+
/* call-seq:
|
588
|
+
source_klass -> klass
|
589
|
+
|
590
|
+
Returns the Ruby klass of the natural source-level definition. */
|
591
|
+
static VALUE
|
592
|
+
prof_source_klass(VALUE self)
|
593
|
+
{
|
594
|
+
prof_method_t *method = get_prof_method(self);
|
595
|
+
return resolve_source_klass(method);
|
596
|
+
}
|
597
|
+
|
598
|
+
/* call-seq:
|
599
|
+
calltree_name -> string
|
600
|
+
|
601
|
+
Returns the full name of this method in the calltree format.*/
|
602
|
+
|
603
|
+
static VALUE
|
604
|
+
prof_calltree_name(VALUE self)
|
605
|
+
{
|
606
|
+
prof_method_t *method = get_prof_method(self);
|
607
|
+
volatile VALUE source_klass = resolve_source_klass(method);
|
608
|
+
return calltree_name(source_klass, method->relation, method->key->mid);
|
609
|
+
}
|
610
|
+
|
611
|
+
void rp_init_method_info()
|
612
|
+
{
|
613
|
+
/* MethodInfo */
|
614
|
+
cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
|
615
|
+
rb_undef_method(CLASS_OF(cMethodInfo), "new");
|
616
|
+
|
617
|
+
rb_define_method(cMethodInfo, "klass", prof_method_klass, 0);
|
618
|
+
rb_define_method(cMethodInfo, "klass_name", prof_klass_name, 0);
|
619
|
+
rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
|
620
|
+
rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
|
621
|
+
rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
|
622
|
+
|
623
|
+
rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
|
624
|
+
rb_define_method(cMethodInfo, "source_klass", prof_source_klass, 0);
|
625
|
+
rb_define_method(cMethodInfo, "source_file", prof_method_source_file, 0);
|
626
|
+
rb_define_method(cMethodInfo, "line", prof_method_line, 0);
|
627
|
+
|
628
|
+
rb_define_method(cMethodInfo, "recursive?", prof_method_recursive, 0);
|
629
|
+
rb_define_method(cMethodInfo, "calltree_name", prof_calltree_name, 0);
|
630
|
+
}
|