ruby-prof 1.0.0
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 +523 -0
- data/LICENSE +25 -0
- data/README.rdoc +5 -0
- data/Rakefile +110 -0
- data/bin/ruby-prof +380 -0
- data/bin/ruby-prof-check-trace +45 -0
- data/ext/ruby_prof/extconf.rb +36 -0
- data/ext/ruby_prof/rp_allocation.c +292 -0
- data/ext/ruby_prof/rp_allocation.h +31 -0
- data/ext/ruby_prof/rp_call_info.c +283 -0
- data/ext/ruby_prof/rp_call_info.h +35 -0
- data/ext/ruby_prof/rp_measure_allocations.c +52 -0
- data/ext/ruby_prof/rp_measure_memory.c +42 -0
- data/ext/ruby_prof/rp_measure_process_time.c +63 -0
- data/ext/ruby_prof/rp_measure_wall_time.c +62 -0
- data/ext/ruby_prof/rp_measurement.c +236 -0
- data/ext/ruby_prof/rp_measurement.h +49 -0
- data/ext/ruby_prof/rp_method.c +642 -0
- data/ext/ruby_prof/rp_method.h +70 -0
- data/ext/ruby_prof/rp_profile.c +881 -0
- data/ext/ruby_prof/rp_profile.h +36 -0
- data/ext/ruby_prof/rp_stack.c +196 -0
- data/ext/ruby_prof/rp_stack.h +56 -0
- data/ext/ruby_prof/rp_thread.c +338 -0
- data/ext/ruby_prof/rp_thread.h +36 -0
- data/ext/ruby_prof/ruby_prof.c +48 -0
- data/ext/ruby_prof/ruby_prof.h +17 -0
- data/ext/ruby_prof/vc/ruby_prof.sln +31 -0
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +143 -0
- data/lib/ruby-prof.rb +53 -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/assets/graph_printer.html.erb +356 -0
- data/lib/ruby-prof/call_info.rb +57 -0
- data/lib/ruby-prof/call_info_visitor.rb +38 -0
- data/lib/ruby-prof/compatibility.rb +109 -0
- data/lib/ruby-prof/exclude_common_methods.rb +198 -0
- data/lib/ruby-prof/measurement.rb +14 -0
- data/lib/ruby-prof/method_info.rb +90 -0
- data/lib/ruby-prof/printers/abstract_printer.rb +118 -0
- data/lib/ruby-prof/printers/call_info_printer.rb +51 -0
- data/lib/ruby-prof/printers/call_stack_printer.rb +269 -0
- data/lib/ruby-prof/printers/call_tree_printer.rb +151 -0
- data/lib/ruby-prof/printers/dot_printer.rb +132 -0
- data/lib/ruby-prof/printers/flat_printer.rb +52 -0
- data/lib/ruby-prof/printers/graph_html_printer.rb +64 -0
- data/lib/ruby-prof/printers/graph_printer.rb +114 -0
- data/lib/ruby-prof/printers/multi_printer.rb +127 -0
- data/lib/ruby-prof/profile.rb +33 -0
- data/lib/ruby-prof/rack.rb +171 -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 +26 -0
- data/test/alias_test.rb +129 -0
- data/test/basic_test.rb +129 -0
- data/test/call_info_visitor_test.rb +31 -0
- data/test/duplicate_names_test.rb +32 -0
- data/test/dynamic_method_test.rb +53 -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/line_number_test.rb +161 -0
- data/test/marshal_test.rb +119 -0
- data/test/measure_allocations.rb +30 -0
- data/test/measure_allocations_test.rb +385 -0
- data/test/measure_allocations_trace_test.rb +385 -0
- data/test/measure_memory_trace_test.rb +756 -0
- data/test/measure_process_time_test.rb +849 -0
- data/test/measure_times.rb +54 -0
- data/test/measure_wall_time_test.rb +459 -0
- data/test/multi_printer_test.rb +71 -0
- data/test/no_method_class_test.rb +15 -0
- data/test/parser_timings.rb +24 -0
- data/test/pause_resume_test.rb +166 -0
- data/test/prime.rb +56 -0
- data/test/printer_call_tree_test.rb +31 -0
- data/test/printer_flat_test.rb +68 -0
- data/test/printer_graph_html_test.rb +60 -0
- data/test/printer_graph_test.rb +41 -0
- data/test/printers_test.rb +141 -0
- data/test/printing_recursive_graph_test.rb +81 -0
- data/test/rack_test.rb +157 -0
- data/test/recursive_test.rb +210 -0
- data/test/singleton_test.rb +38 -0
- data/test/stack_printer_test.rb +64 -0
- data/test/start_stop_test.rb +109 -0
- data/test/test_helper.rb +24 -0
- data/test/thread_test.rb +144 -0
- data/test/unique_call_path_test.rb +190 -0
- data/test/yarv_test.rb +56 -0
- metadata +189 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
stacks = Hash.new{|h,k| h[k] = Hash.new{|h,k| h[k] = []}}
|
|
4
|
+
i = 0
|
|
5
|
+
File.open(ARGV[0]).each_line do |l|
|
|
6
|
+
i += 1
|
|
7
|
+
unless l =~ /^(\d+):(\d+): *\d+ms *([^ ]+) *(.*): *(\d+) *(.+)$/
|
|
8
|
+
next if l =~/^ *$/
|
|
9
|
+
puts "line doesn't match: #{l}"
|
|
10
|
+
next
|
|
11
|
+
end
|
|
12
|
+
details = $1.to_i, $2.to_i, $3, $4, $5.to_i, $6
|
|
13
|
+
thread, fiber, event, file, line, method = *details
|
|
14
|
+
# puts method
|
|
15
|
+
stack = stacks[thread][fiber]
|
|
16
|
+
case event
|
|
17
|
+
when 'call', 'c-call'
|
|
18
|
+
stack << method
|
|
19
|
+
when 'return', 'c-return'
|
|
20
|
+
last_method = stack.pop
|
|
21
|
+
if last_method != method
|
|
22
|
+
puts "LINE #{i}: return event without call: #{method}"
|
|
23
|
+
puts "STACK: #{stack.inspect}"
|
|
24
|
+
if stack.find(method)
|
|
25
|
+
puts "fixing stack"
|
|
26
|
+
while (popped = stack.pop) && (popped != method)
|
|
27
|
+
puts "popped #{popped}"
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
raise "stack unfixable"
|
|
31
|
+
end
|
|
32
|
+
# stack << last_method
|
|
33
|
+
end
|
|
34
|
+
when 'line'
|
|
35
|
+
last_method = stack[-1]
|
|
36
|
+
if last_method != method
|
|
37
|
+
unless stack.find(method)
|
|
38
|
+
raise "LINE #{i}: line event without call: #{method}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
puts "unkown event"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
puts stacks.inspect
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require "mkmf"
|
|
2
|
+
|
|
3
|
+
if RUBY_ENGINE != "ruby"
|
|
4
|
+
STDERR.puts("\n\n***** This gem is MRI-specific. It does not support #{RUBY_ENGINE}. *****\n\n")
|
|
5
|
+
exit(1)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.4.0')
|
|
9
|
+
STDERR.puts("\n\n***** Ruby version #{RUBY_VERSION} is no longer supported. Please upgrade to 2.3 or higher. *****\n\n")
|
|
10
|
+
exit(1)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# For the love of bitfields...
|
|
14
|
+
$CFLAGS += ' -std=c99'
|
|
15
|
+
|
|
16
|
+
# And since we are using C99
|
|
17
|
+
CONFIG['warnflags'].gsub!('-Wdeclaration-after-statement', '')
|
|
18
|
+
|
|
19
|
+
def add_define(name, value = nil)
|
|
20
|
+
if value
|
|
21
|
+
$defs.push("-D#{name}=#{value}")
|
|
22
|
+
else
|
|
23
|
+
$defs.push("-D#{name}")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def windows?
|
|
28
|
+
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
add_define("RUBY_PROF_RUBY_VERSION", RUBY_VERSION.split('.')[0..2].inject(0){|v,d| v*100+d.to_i})
|
|
32
|
+
|
|
33
|
+
# This function was added in Ruby 2.5, so once Ruby 2.4 is no longer supported this can be removed
|
|
34
|
+
have_func('rb_tracearg_callee_id', ["ruby.h"])
|
|
35
|
+
|
|
36
|
+
create_makefile("ruby_prof")
|
|
@@ -0,0 +1,292 @@
|
|
|
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 "rp_allocation.h"
|
|
5
|
+
|
|
6
|
+
VALUE cRpAllocation;
|
|
7
|
+
|
|
8
|
+
prof_allocation_t*
|
|
9
|
+
allocations_table_lookup(st_table *table, st_data_t key)
|
|
10
|
+
{
|
|
11
|
+
prof_allocation_t* result = NULL;
|
|
12
|
+
st_data_t value;
|
|
13
|
+
if (st_lookup(table, key, &value))
|
|
14
|
+
{
|
|
15
|
+
result = (prof_allocation_t*)value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
void
|
|
22
|
+
allocations_table_insert(st_table *table, st_data_t key, prof_allocation_t * allocation)
|
|
23
|
+
{
|
|
24
|
+
st_insert(table, (st_data_t)key, (st_data_t)allocation);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
st_data_t
|
|
28
|
+
allocations_key(VALUE klass, int source_line)
|
|
29
|
+
{
|
|
30
|
+
return (klass << 4) + source_line;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* ====== prof_allocation_t ====== */
|
|
34
|
+
prof_allocation_t*
|
|
35
|
+
prof_allocation_create(void)
|
|
36
|
+
{
|
|
37
|
+
prof_allocation_t *result = ALLOC(prof_allocation_t);
|
|
38
|
+
result->count = 0;
|
|
39
|
+
result->klass = Qnil;
|
|
40
|
+
result->klass_name = Qnil;
|
|
41
|
+
result->object = Qnil;
|
|
42
|
+
result->memory = 0;
|
|
43
|
+
result->source_line = 0;
|
|
44
|
+
result->source_file = Qnil;
|
|
45
|
+
result->key = 0;
|
|
46
|
+
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
prof_allocation_t*
|
|
51
|
+
prof_allocate_increment(prof_method_t* method, rb_trace_arg_t* trace_arg)
|
|
52
|
+
{
|
|
53
|
+
VALUE object = rb_tracearg_object(trace_arg);
|
|
54
|
+
if (BUILTIN_TYPE(object) == T_IMEMO)
|
|
55
|
+
return NULL;
|
|
56
|
+
|
|
57
|
+
VALUE klass = rb_obj_class(object);
|
|
58
|
+
|
|
59
|
+
int source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
|
|
60
|
+
st_data_t key = allocations_key(klass, source_line);
|
|
61
|
+
|
|
62
|
+
prof_allocation_t* allocation = allocations_table_lookup(method->allocations_table, key);
|
|
63
|
+
if (!allocation)
|
|
64
|
+
{
|
|
65
|
+
allocation = prof_allocation_create();
|
|
66
|
+
allocation->source_line = source_line;
|
|
67
|
+
allocation->source_file = rb_tracearg_path(trace_arg);
|
|
68
|
+
allocation->klass_flags = 0;
|
|
69
|
+
allocation->klass = resolve_klass(klass, &allocation->klass_flags);
|
|
70
|
+
|
|
71
|
+
allocation->key = key;
|
|
72
|
+
allocations_table_insert(method->allocations_table, key, allocation);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
allocation->count++;
|
|
76
|
+
allocation->memory += rb_obj_memsize_of(object);
|
|
77
|
+
|
|
78
|
+
return allocation;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static void
|
|
82
|
+
prof_allocation_ruby_gc_free(void *data)
|
|
83
|
+
{
|
|
84
|
+
prof_allocation_t* allocation = (prof_allocation_t*)data;
|
|
85
|
+
|
|
86
|
+
/* Has this thread object been accessed by Ruby? If
|
|
87
|
+
yes clean it up so to avoid a segmentation fault. */
|
|
88
|
+
if (allocation->object != Qnil)
|
|
89
|
+
{
|
|
90
|
+
RDATA(allocation->object)->data = NULL;
|
|
91
|
+
RDATA(allocation->object)->dfree = NULL;
|
|
92
|
+
RDATA(allocation->object)->dmark = NULL;
|
|
93
|
+
}
|
|
94
|
+
allocation->object = Qnil;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
void
|
|
98
|
+
prof_allocation_free(prof_allocation_t* allocation)
|
|
99
|
+
{
|
|
100
|
+
prof_allocation_ruby_gc_free(allocation);
|
|
101
|
+
xfree(allocation);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
size_t
|
|
105
|
+
prof_allocation_size(const void* data)
|
|
106
|
+
{
|
|
107
|
+
return sizeof(prof_allocation_t);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void
|
|
111
|
+
prof_allocation_mark(void *data)
|
|
112
|
+
{
|
|
113
|
+
prof_allocation_t* allocation = (prof_allocation_t*)data;
|
|
114
|
+
if (allocation->klass != Qnil)
|
|
115
|
+
rb_gc_mark(allocation->klass);
|
|
116
|
+
|
|
117
|
+
if (allocation->klass_name != Qnil)
|
|
118
|
+
rb_gc_mark(allocation->klass_name);
|
|
119
|
+
|
|
120
|
+
if (allocation->object != Qnil)
|
|
121
|
+
rb_gc_mark(allocation->object);
|
|
122
|
+
|
|
123
|
+
if (allocation->source_file != Qnil)
|
|
124
|
+
rb_gc_mark(allocation->source_file);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static const rb_data_type_t allocation_type =
|
|
128
|
+
{
|
|
129
|
+
.wrap_struct_name = "Allocation",
|
|
130
|
+
.function =
|
|
131
|
+
{
|
|
132
|
+
.dmark = prof_allocation_mark,
|
|
133
|
+
.dfree = prof_allocation_ruby_gc_free,
|
|
134
|
+
.dsize = prof_allocation_size,
|
|
135
|
+
},
|
|
136
|
+
.data = NULL,
|
|
137
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
VALUE
|
|
141
|
+
prof_allocation_wrap(prof_allocation_t *allocation)
|
|
142
|
+
{
|
|
143
|
+
if (allocation->object == Qnil)
|
|
144
|
+
{
|
|
145
|
+
allocation->object = TypedData_Wrap_Struct(cRpAllocation, &allocation_type, allocation);
|
|
146
|
+
}
|
|
147
|
+
return allocation->object;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static VALUE
|
|
151
|
+
prof_allocation_allocate(VALUE klass)
|
|
152
|
+
{
|
|
153
|
+
prof_allocation_t* allocation = prof_allocation_create();
|
|
154
|
+
allocation->object = prof_allocation_wrap(allocation);
|
|
155
|
+
return allocation->object;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
prof_allocation_t*
|
|
159
|
+
prof_allocation_get(VALUE self)
|
|
160
|
+
{
|
|
161
|
+
/* Can't use Data_Get_Struct because that triggers the event hook
|
|
162
|
+
ending up in endless recursion. */
|
|
163
|
+
prof_allocation_t* result = DATA_PTR(self);
|
|
164
|
+
if (!result)
|
|
165
|
+
rb_raise(rb_eRuntimeError, "This RubyProf::Allocation instance has already been freed, likely because its profile has been freed.");
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* call-seq:
|
|
171
|
+
klass -> Class
|
|
172
|
+
|
|
173
|
+
Returns the type of Class being allocated. */
|
|
174
|
+
static VALUE
|
|
175
|
+
prof_allocation_klass_name(VALUE self)
|
|
176
|
+
{
|
|
177
|
+
prof_allocation_t* allocation = prof_allocation_get(self);
|
|
178
|
+
|
|
179
|
+
if (allocation->klass_name == Qnil)
|
|
180
|
+
allocation->klass_name = resolve_klass_name(allocation->klass, &allocation->klass_flags);
|
|
181
|
+
|
|
182
|
+
return allocation->klass_name;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* call-seq:
|
|
186
|
+
klass_flags -> integer
|
|
187
|
+
|
|
188
|
+
Returns the klass flags */
|
|
189
|
+
|
|
190
|
+
static VALUE
|
|
191
|
+
prof_allocation_klass_flags(VALUE self)
|
|
192
|
+
{
|
|
193
|
+
prof_allocation_t* allocation = prof_allocation_get(self);
|
|
194
|
+
return INT2FIX(allocation->klass_flags);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* call-seq:
|
|
198
|
+
source_file -> string
|
|
199
|
+
|
|
200
|
+
Returns the the line number where objects were allocated. */
|
|
201
|
+
static VALUE
|
|
202
|
+
prof_allocation_source_file(VALUE self)
|
|
203
|
+
{
|
|
204
|
+
prof_allocation_t* allocation = prof_allocation_get(self);
|
|
205
|
+
return allocation->source_file;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* call-seq:
|
|
209
|
+
line -> number
|
|
210
|
+
|
|
211
|
+
Returns the the line number where objects were allocated. */
|
|
212
|
+
static VALUE
|
|
213
|
+
prof_allocation_source_line(VALUE self)
|
|
214
|
+
{
|
|
215
|
+
prof_allocation_t* allocation = prof_allocation_get(self);
|
|
216
|
+
return INT2FIX(allocation->source_line);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* call-seq:
|
|
220
|
+
count -> number
|
|
221
|
+
|
|
222
|
+
Returns the number of times this class has been allocated. */
|
|
223
|
+
static VALUE
|
|
224
|
+
prof_allocation_count(VALUE self)
|
|
225
|
+
{
|
|
226
|
+
prof_allocation_t* allocation = prof_allocation_get(self);
|
|
227
|
+
return INT2FIX(allocation->count);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* call-seq:
|
|
231
|
+
memory -> number
|
|
232
|
+
|
|
233
|
+
Returns the amount of memory allocated. */
|
|
234
|
+
static VALUE
|
|
235
|
+
prof_allocation_memory(VALUE self)
|
|
236
|
+
{
|
|
237
|
+
prof_allocation_t* allocation = prof_allocation_get(self);
|
|
238
|
+
return ULL2NUM(allocation->memory);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* :nodoc: */
|
|
242
|
+
static VALUE
|
|
243
|
+
prof_allocation_dump(VALUE self)
|
|
244
|
+
{
|
|
245
|
+
prof_allocation_t* allocation = DATA_PTR(self);
|
|
246
|
+
|
|
247
|
+
VALUE result = rb_hash_new();
|
|
248
|
+
|
|
249
|
+
rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(allocation->key));
|
|
250
|
+
rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_allocation_klass_name(self));
|
|
251
|
+
rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(allocation->klass_flags));
|
|
252
|
+
rb_hash_aset(result, ID2SYM(rb_intern("source_file")), allocation->source_file);
|
|
253
|
+
rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(allocation->source_line));
|
|
254
|
+
rb_hash_aset(result, ID2SYM(rb_intern("count")), INT2FIX(allocation->count));
|
|
255
|
+
rb_hash_aset(result, ID2SYM(rb_intern("memory")), LONG2FIX(allocation->memory));
|
|
256
|
+
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* :nodoc: */
|
|
261
|
+
static VALUE
|
|
262
|
+
prof_allocation_load(VALUE self, VALUE data)
|
|
263
|
+
{
|
|
264
|
+
prof_allocation_t* allocation = DATA_PTR(self);
|
|
265
|
+
allocation->object = self;
|
|
266
|
+
|
|
267
|
+
allocation->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
|
|
268
|
+
allocation->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
|
|
269
|
+
allocation->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
|
|
270
|
+
allocation->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
|
|
271
|
+
allocation->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
|
|
272
|
+
allocation->count = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("count"))));
|
|
273
|
+
allocation->memory = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("memory"))));
|
|
274
|
+
|
|
275
|
+
return data;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
void rp_init_allocation(void)
|
|
279
|
+
{
|
|
280
|
+
cRpAllocation = rb_define_class_under(mProf, "Allocation", rb_cData);
|
|
281
|
+
rb_undef_method(CLASS_OF(cRpAllocation), "new");
|
|
282
|
+
rb_define_alloc_func(cRpAllocation, prof_allocation_allocate);
|
|
283
|
+
|
|
284
|
+
rb_define_method(cRpAllocation, "klass_name", prof_allocation_klass_name, 0);
|
|
285
|
+
rb_define_method(cRpAllocation, "klass_flags", prof_allocation_klass_flags, 0);
|
|
286
|
+
rb_define_method(cRpAllocation, "source_file", prof_allocation_source_file, 0);
|
|
287
|
+
rb_define_method(cRpAllocation, "line", prof_allocation_source_line, 0);
|
|
288
|
+
rb_define_method(cRpAllocation, "count", prof_allocation_count, 0);
|
|
289
|
+
rb_define_method(cRpAllocation, "memory", prof_allocation_memory, 0);
|
|
290
|
+
rb_define_method(cRpAllocation, "_dump_data", prof_allocation_dump, 0);
|
|
291
|
+
rb_define_method(cRpAllocation, "_load_data", prof_allocation_load, 1);
|
|
292
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
#ifndef _RP_ALLOCATION_
|
|
5
|
+
#define _RP_ALLOCATION_
|
|
6
|
+
|
|
7
|
+
#include "ruby_prof.h"
|
|
8
|
+
#include "rp_method.h"
|
|
9
|
+
|
|
10
|
+
typedef struct
|
|
11
|
+
{
|
|
12
|
+
st_data_t key; /* Key in hash table */
|
|
13
|
+
unsigned int klass_flags; /* Information about the type of class */
|
|
14
|
+
VALUE klass; /* Klass that was created */
|
|
15
|
+
VALUE klass_name; /* Name of the class that was created */
|
|
16
|
+
VALUE source_file; /* Line number where allocation happens */
|
|
17
|
+
int source_line; /* Line number where allocation happens */
|
|
18
|
+
int count; /* Number of allocations */
|
|
19
|
+
size_t memory; /* Amount of allocated memory */
|
|
20
|
+
VALUE object; /* Cache to wrapped object */
|
|
21
|
+
} prof_allocation_t;
|
|
22
|
+
|
|
23
|
+
void rp_init_allocation(void);
|
|
24
|
+
void prof_allocation_free(prof_allocation_t* allocation);
|
|
25
|
+
void prof_allocation_mark(void *data);
|
|
26
|
+
VALUE prof_allocation_wrap(prof_allocation_t* allocation);
|
|
27
|
+
prof_allocation_t* prof_allocation_get(VALUE self);
|
|
28
|
+
prof_allocation_t* prof_allocate_increment(prof_method_t *method, rb_trace_arg_t *trace_arg);
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
#endif //_RP_ALLOCATION_
|
|
@@ -0,0 +1,283 @@
|
|
|
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 "rp_call_info.h"
|
|
5
|
+
|
|
6
|
+
#define INITIAL_CALL_INFOS_SIZE 2
|
|
7
|
+
|
|
8
|
+
VALUE cRpCallnfo;
|
|
9
|
+
|
|
10
|
+
/* ======= prof_call_info_t ========*/
|
|
11
|
+
prof_call_info_t *
|
|
12
|
+
prof_call_info_create(prof_method_t *method, prof_method_t *parent, VALUE source_file, int source_line)
|
|
13
|
+
{
|
|
14
|
+
prof_call_info_t *result = ALLOC(prof_call_info_t);
|
|
15
|
+
result->method = method;
|
|
16
|
+
result->parent = parent;
|
|
17
|
+
result->object = Qnil;
|
|
18
|
+
result->measurement = prof_measurement_create();
|
|
19
|
+
|
|
20
|
+
result->visits = 0;
|
|
21
|
+
|
|
22
|
+
result->depth = 0;
|
|
23
|
+
result->source_line = source_line;
|
|
24
|
+
result->source_file = source_file;
|
|
25
|
+
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static void
|
|
30
|
+
prof_call_info_ruby_gc_free(void *data)
|
|
31
|
+
{
|
|
32
|
+
prof_call_info_t *call_info = (prof_call_info_t*)data;
|
|
33
|
+
|
|
34
|
+
/* Has this thread object been accessed by Ruby? If
|
|
35
|
+
yes clean it up so to avoid a segmentation fault. */
|
|
36
|
+
if (call_info->object != Qnil)
|
|
37
|
+
{
|
|
38
|
+
RDATA(call_info->object)->data = NULL;
|
|
39
|
+
RDATA(call_info->object)->dfree = NULL;
|
|
40
|
+
RDATA(call_info->object)->dmark = NULL;
|
|
41
|
+
}
|
|
42
|
+
call_info->object = Qnil;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
void
|
|
46
|
+
prof_call_info_free(prof_call_info_t *call_info)
|
|
47
|
+
{
|
|
48
|
+
prof_call_info_ruby_gc_free(call_info);
|
|
49
|
+
xfree(call_info);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
size_t
|
|
53
|
+
prof_call_info_size(const void *data)
|
|
54
|
+
{
|
|
55
|
+
return sizeof(prof_call_info_t);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
void
|
|
59
|
+
prof_call_info_mark(void *data)
|
|
60
|
+
{
|
|
61
|
+
prof_call_info_t *call_info = (prof_call_info_t*)data;
|
|
62
|
+
|
|
63
|
+
if (call_info->source_file != Qnil)
|
|
64
|
+
rb_gc_mark(call_info->source_file);
|
|
65
|
+
|
|
66
|
+
if (call_info->object != Qnil)
|
|
67
|
+
rb_gc_mark(call_info->object);
|
|
68
|
+
|
|
69
|
+
if (call_info->method && call_info->method->object != Qnil)
|
|
70
|
+
rb_gc_mark(call_info->method->object);
|
|
71
|
+
|
|
72
|
+
if (call_info->parent && call_info->parent->object != Qnil)
|
|
73
|
+
rb_gc_mark(call_info->parent->object);
|
|
74
|
+
|
|
75
|
+
prof_measurement_mark(call_info->measurement);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static const rb_data_type_t call_info_type =
|
|
79
|
+
{
|
|
80
|
+
.wrap_struct_name = "CallInfo",
|
|
81
|
+
.function =
|
|
82
|
+
{
|
|
83
|
+
.dmark = prof_call_info_mark,
|
|
84
|
+
.dfree = prof_call_info_ruby_gc_free,
|
|
85
|
+
.dsize = prof_call_info_size,
|
|
86
|
+
},
|
|
87
|
+
.data = NULL,
|
|
88
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
VALUE
|
|
92
|
+
prof_call_info_wrap(prof_call_info_t *call_info)
|
|
93
|
+
{
|
|
94
|
+
if (call_info->object == Qnil)
|
|
95
|
+
{
|
|
96
|
+
call_info->object = TypedData_Wrap_Struct(cRpCallnfo, &call_info_type, call_info);
|
|
97
|
+
}
|
|
98
|
+
return call_info->object;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static VALUE
|
|
102
|
+
prof_call_info_allocate(VALUE klass)
|
|
103
|
+
{
|
|
104
|
+
prof_call_info_t* call_info = prof_call_info_create(NULL, NULL, Qnil, 0);
|
|
105
|
+
call_info->object = prof_call_info_wrap(call_info);
|
|
106
|
+
return call_info->object;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
prof_call_info_t *
|
|
110
|
+
prof_get_call_info(VALUE self)
|
|
111
|
+
{
|
|
112
|
+
/* Can't use Data_Get_Struct because that triggers the event hook
|
|
113
|
+
ending up in endless recursion. */
|
|
114
|
+
prof_call_info_t* result = DATA_PTR(self);
|
|
115
|
+
|
|
116
|
+
if (!result)
|
|
117
|
+
rb_raise(rb_eRuntimeError, "This RubyProf::CallInfo instance has already been freed, likely because its profile has been freed.");
|
|
118
|
+
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* ======= Call Info Table ========*/
|
|
123
|
+
st_table *
|
|
124
|
+
call_info_table_create()
|
|
125
|
+
{
|
|
126
|
+
return st_init_numtable();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
size_t
|
|
130
|
+
call_info_table_insert(st_table *table, st_data_t key, prof_call_info_t *val)
|
|
131
|
+
{
|
|
132
|
+
return st_insert(table, (st_data_t) key, (st_data_t) val);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
prof_call_info_t *
|
|
136
|
+
call_info_table_lookup(st_table *table, st_data_t key)
|
|
137
|
+
{
|
|
138
|
+
st_data_t val;
|
|
139
|
+
if (st_lookup(table, (st_data_t) key, &val))
|
|
140
|
+
{
|
|
141
|
+
return (prof_call_info_t *) val;
|
|
142
|
+
}
|
|
143
|
+
else
|
|
144
|
+
{
|
|
145
|
+
return NULL;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* ======= RubyProf::CallInfo ========*/
|
|
150
|
+
|
|
151
|
+
/* call-seq:
|
|
152
|
+
parent -> call_info
|
|
153
|
+
|
|
154
|
+
Returns the call_infos parent call_info object (the method that called this method).*/
|
|
155
|
+
static VALUE
|
|
156
|
+
prof_call_info_parent(VALUE self)
|
|
157
|
+
{
|
|
158
|
+
prof_call_info_t* call_info = prof_get_call_info(self);
|
|
159
|
+
if (call_info->parent)
|
|
160
|
+
return prof_method_wrap(call_info->parent);
|
|
161
|
+
else
|
|
162
|
+
return Qnil;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* call-seq:
|
|
166
|
+
called -> MethodInfo
|
|
167
|
+
|
|
168
|
+
Returns the target method. */
|
|
169
|
+
static VALUE
|
|
170
|
+
prof_call_info_target(VALUE self)
|
|
171
|
+
{
|
|
172
|
+
prof_call_info_t *call_info = prof_get_call_info(self);
|
|
173
|
+
return prof_method_wrap(call_info->method);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* call-seq:
|
|
177
|
+
called -> Measurement
|
|
178
|
+
|
|
179
|
+
Returns the measurement associated with this call_info. */
|
|
180
|
+
static VALUE
|
|
181
|
+
prof_call_info_measurement(VALUE self)
|
|
182
|
+
{
|
|
183
|
+
prof_call_info_t* call_info = prof_get_call_info(self);
|
|
184
|
+
return prof_measurement_wrap(call_info->measurement);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* call-seq:
|
|
188
|
+
depth -> int
|
|
189
|
+
|
|
190
|
+
returns the depth of this call info in the call graph */
|
|
191
|
+
static VALUE
|
|
192
|
+
prof_call_info_depth(VALUE self)
|
|
193
|
+
{
|
|
194
|
+
prof_call_info_t *result = prof_get_call_info(self);
|
|
195
|
+
return rb_int_new(result->depth);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* call-seq:
|
|
199
|
+
source_file => string
|
|
200
|
+
|
|
201
|
+
return the source file of the method
|
|
202
|
+
*/
|
|
203
|
+
static VALUE
|
|
204
|
+
prof_call_info_source_file(VALUE self)
|
|
205
|
+
{
|
|
206
|
+
prof_call_info_t* result = prof_get_call_info(self);
|
|
207
|
+
return result->source_file;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* call-seq:
|
|
211
|
+
line_no -> int
|
|
212
|
+
|
|
213
|
+
returns the line number of the method */
|
|
214
|
+
static VALUE
|
|
215
|
+
prof_call_info_line(VALUE self)
|
|
216
|
+
{
|
|
217
|
+
prof_call_info_t *result = prof_get_call_info(self);
|
|
218
|
+
return INT2FIX(result->source_line);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* :nodoc: */
|
|
222
|
+
static VALUE
|
|
223
|
+
prof_call_info_dump(VALUE self)
|
|
224
|
+
{
|
|
225
|
+
prof_call_info_t* call_info_data = prof_get_call_info(self);
|
|
226
|
+
VALUE result = rb_hash_new();
|
|
227
|
+
|
|
228
|
+
rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(call_info_data->measurement));
|
|
229
|
+
|
|
230
|
+
rb_hash_aset(result, ID2SYM(rb_intern("depth")), INT2FIX(call_info_data->depth));
|
|
231
|
+
rb_hash_aset(result, ID2SYM(rb_intern("source_file")), call_info_data->source_file);
|
|
232
|
+
rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(call_info_data->source_line));
|
|
233
|
+
|
|
234
|
+
rb_hash_aset(result, ID2SYM(rb_intern("parent")), prof_call_info_parent(self));
|
|
235
|
+
rb_hash_aset(result, ID2SYM(rb_intern("target")), prof_call_info_target(self));
|
|
236
|
+
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* :nodoc: */
|
|
241
|
+
static VALUE
|
|
242
|
+
prof_call_info_load(VALUE self, VALUE data)
|
|
243
|
+
{
|
|
244
|
+
VALUE target = Qnil;
|
|
245
|
+
VALUE parent = Qnil;
|
|
246
|
+
prof_call_info_t* call_info = prof_get_call_info(self);
|
|
247
|
+
call_info->object = self;
|
|
248
|
+
|
|
249
|
+
VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
|
|
250
|
+
call_info->measurement = prof_get_measurement(measurement);
|
|
251
|
+
|
|
252
|
+
call_info->depth = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("depth"))));
|
|
253
|
+
call_info->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
|
|
254
|
+
call_info->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
|
|
255
|
+
|
|
256
|
+
parent = rb_hash_aref(data, ID2SYM(rb_intern("parent")));
|
|
257
|
+
if (parent != Qnil)
|
|
258
|
+
call_info->parent = prof_method_get(parent);
|
|
259
|
+
|
|
260
|
+
target = rb_hash_aref(data, ID2SYM(rb_intern("target")));
|
|
261
|
+
call_info->method = prof_method_get(target);
|
|
262
|
+
|
|
263
|
+
return data;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
void rp_init_call_info()
|
|
267
|
+
{
|
|
268
|
+
/* CallInfo */
|
|
269
|
+
cRpCallnfo = rb_define_class_under(mProf, "CallInfo", rb_cData);
|
|
270
|
+
rb_undef_method(CLASS_OF(cRpCallnfo), "new");
|
|
271
|
+
rb_define_alloc_func(cRpCallnfo, prof_call_info_allocate);
|
|
272
|
+
|
|
273
|
+
rb_define_method(cRpCallnfo, "parent", prof_call_info_parent, 0);
|
|
274
|
+
rb_define_method(cRpCallnfo, "target", prof_call_info_target, 0);
|
|
275
|
+
rb_define_method(cRpCallnfo, "measurement", prof_call_info_measurement, 0);
|
|
276
|
+
|
|
277
|
+
rb_define_method(cRpCallnfo, "depth", prof_call_info_depth, 0);
|
|
278
|
+
rb_define_method(cRpCallnfo, "source_file", prof_call_info_source_file, 0);
|
|
279
|
+
rb_define_method(cRpCallnfo, "line", prof_call_info_line, 0);
|
|
280
|
+
|
|
281
|
+
rb_define_method(cRpCallnfo, "_dump_data", prof_call_info_dump, 0);
|
|
282
|
+
rb_define_method(cRpCallnfo, "_load_data", prof_call_info_load, 1);
|
|
283
|
+
}
|