gc_tracer 0.1.2 → 0.2.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 +4 -4
- data/README.md +70 -1
- data/ext/gc_tracer/allocation_tracer.c +388 -0
- data/ext/gc_tracer/gc_tracer.c +9 -1
- data/lib/gc_tracer/allocation_trace.rb +10 -0
- data/lib/gc_tracer/version.rb +1 -1
- data/spec/gc_tracer_spec.rb +16 -4
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 830cc27b4a444096a36db13d0425b920cd87c8e4
|
4
|
+
data.tar.gz: a331e1b13ea0f3c61af86c166c1592cb21fb67cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61150fdea8743a53a2f2d7954d2cb969e75ff46b23ddb7bbb50afaccb2885632067533b10c5bac91b2fe393d8cd739b59eb3de16b521ed4869cdb6c18dfcdbd0
|
7
|
+
data.tar.gz: 66bcc1e1a75d05caf7dc54a655a4ebc657db34fb9fa6410dbef5faebeb0783248851cbf9ea8ae1936a2348d95c0232ceddd22979494516d9268e80818a46287b
|
data/README.md
CHANGED
@@ -20,6 +20,12 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
+
gc_tracer gem adds GC::Tracer module. GC::Tracer module has the following features.
|
24
|
+
|
25
|
+
- Logging GC statistics information
|
26
|
+
- Allocation tracing information
|
27
|
+
- ObjectSpace recorder
|
28
|
+
|
23
29
|
### Logging
|
24
30
|
|
25
31
|
You can get GC statistics information in block form like this:
|
@@ -54,6 +60,69 @@ at each events, there are one of:
|
|
54
60
|
|
55
61
|
For one GC, you can get all three lines.
|
56
62
|
|
63
|
+
### Allocation tracing
|
64
|
+
|
65
|
+
You can trace allocation information and you can get aggregated information.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
require 'gc_tracer'
|
69
|
+
require 'pp'
|
70
|
+
|
71
|
+
pp GC::Tracer.start_allocation_tracing{
|
72
|
+
50_000.times{|i|
|
73
|
+
i.to_s
|
74
|
+
i.to_s
|
75
|
+
i.to_s
|
76
|
+
}
|
77
|
+
}
|
78
|
+
```
|
79
|
+
|
80
|
+
will show
|
81
|
+
|
82
|
+
```
|
83
|
+
{["test.rb", 6]=>[50000, 44290, 0, 6],
|
84
|
+
["test.rb", 7]=>[50000, 44289, 0, 5],
|
85
|
+
["test.rb", 8]=>[50000, 44295, 0, 6]}
|
86
|
+
```
|
87
|
+
|
88
|
+
In this case, 50,000 objects are created at `test.rb:6'. 44,290 is total
|
89
|
+
age of objects created at this line. Average age of object created at
|
90
|
+
this line is 50000/44290 = 0.8858. 0 is minimum age and 6 is maximum age.
|
91
|
+
|
92
|
+
Simply you can require `gc_tracer/allocation_trace' to start allocation
|
93
|
+
tracer and output the aggregated information into stdot at the end of
|
94
|
+
program.
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
require 'gc_tracer/allocation_trace'
|
98
|
+
|
99
|
+
# Run your program here
|
100
|
+
50_000.times{|i|
|
101
|
+
i.to_s
|
102
|
+
i.to_s
|
103
|
+
i.to_s
|
104
|
+
}
|
105
|
+
```
|
106
|
+
|
107
|
+
and you will see:
|
108
|
+
|
109
|
+
```
|
110
|
+
file line count total_age max_age min_age
|
111
|
+
.../lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb 55 18 23 1 6
|
112
|
+
.../gc_tracer/lib/gc_tracer/allocation_trace.rb 5 2 12 6 6
|
113
|
+
.../gc_tracer/lib/gc_tracer/allocation_trace.rb 6 2 0 0 0
|
114
|
+
test.rb 0 1 0 0 0
|
115
|
+
test.rb 5 50000 41574 0 5
|
116
|
+
test.rb 6 50000 41566 0 4
|
117
|
+
test.rb 7 50000 41574 0 5
|
118
|
+
```
|
119
|
+
|
120
|
+
(tab separated colums)
|
121
|
+
|
122
|
+
This feature is similar to https://github.com/SamSaffron/memory_profiler
|
123
|
+
and https://github.com/srawlins/allocation_stats. But this feature
|
124
|
+
focused on `age' of objects.
|
125
|
+
|
57
126
|
### ObjectSpace recorder
|
58
127
|
|
59
128
|
You can records objspace snapshots on each events. Snapshots are stored
|
@@ -77,7 +146,7 @@ You can view all converted png images with "dirname/viewer.html" file in animati
|
|
77
146
|
|
78
147
|
This feature is supported only latest Ruby versions (2.2, and later).
|
79
148
|
|
80
|
-
####
|
149
|
+
#### Example
|
81
150
|
|
82
151
|
```ruby
|
83
152
|
require 'gc_tracer'
|
@@ -0,0 +1,388 @@
|
|
1
|
+
/*
|
2
|
+
* allocation tracer: adds GC::Tracer::start_allocation_tracing
|
3
|
+
*
|
4
|
+
* By Koichi Sasada
|
5
|
+
* created at Thu Apr 17 03:50:38 2014.
|
6
|
+
*/
|
7
|
+
|
8
|
+
#include "ruby/ruby.h"
|
9
|
+
#include "ruby/debug.h"
|
10
|
+
|
11
|
+
struct allocation_info {
|
12
|
+
/* all of information don't need marking. */
|
13
|
+
int living;
|
14
|
+
VALUE flags;
|
15
|
+
VALUE klass;
|
16
|
+
|
17
|
+
/* allocation info */
|
18
|
+
const char *path;
|
19
|
+
unsigned long line;
|
20
|
+
const char *class_path;
|
21
|
+
VALUE mid;
|
22
|
+
size_t generation;
|
23
|
+
|
24
|
+
struct allocation_info *next;
|
25
|
+
};
|
26
|
+
|
27
|
+
struct traceobj_arg {
|
28
|
+
int running;
|
29
|
+
st_table *aggregate_table; /* user defined key -> [count, total_age, max_age, min_age] */
|
30
|
+
st_table *object_table; /* obj (VALUE) -> allocation_info */
|
31
|
+
st_table *str_table; /* cstr -> refcount */
|
32
|
+
struct allocation_info *freed_allocation_info;
|
33
|
+
};
|
34
|
+
|
35
|
+
extern VALUE rb_mGCTracer;
|
36
|
+
|
37
|
+
static char *
|
38
|
+
keep_unique_str(st_table *tbl, const char *str)
|
39
|
+
{
|
40
|
+
st_data_t n;
|
41
|
+
|
42
|
+
if (st_lookup(tbl, (st_data_t)str, &n)) {
|
43
|
+
char *result;
|
44
|
+
|
45
|
+
st_insert(tbl, (st_data_t)str, n+1);
|
46
|
+
st_get_key(tbl, (st_data_t)str, (st_data_t *)&result);
|
47
|
+
|
48
|
+
return result;
|
49
|
+
}
|
50
|
+
else {
|
51
|
+
return NULL;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
static const char *
|
56
|
+
make_unique_str(st_table *tbl, const char *str, long len)
|
57
|
+
{
|
58
|
+
if (!str) {
|
59
|
+
return NULL;
|
60
|
+
}
|
61
|
+
else {
|
62
|
+
char *result;
|
63
|
+
|
64
|
+
if ((result = keep_unique_str(tbl, str)) == NULL) {
|
65
|
+
result = (char *)ruby_xmalloc(len+1);
|
66
|
+
strncpy(result, str, len);
|
67
|
+
result[len] = 0;
|
68
|
+
st_add_direct(tbl, (st_data_t)result, 1);
|
69
|
+
}
|
70
|
+
return result;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
static void
|
75
|
+
delete_unique_str(st_table *tbl, const char *str)
|
76
|
+
{
|
77
|
+
if (str) {
|
78
|
+
st_data_t n;
|
79
|
+
|
80
|
+
st_lookup(tbl, (st_data_t)str, &n);
|
81
|
+
if (n == 1) {
|
82
|
+
st_delete(tbl, (st_data_t *)&str, 0);
|
83
|
+
ruby_xfree((char *)str);
|
84
|
+
}
|
85
|
+
else {
|
86
|
+
st_insert(tbl, (st_data_t)str, n-1);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
struct memcmp_key_data {
|
92
|
+
const char *path;
|
93
|
+
int line;
|
94
|
+
};
|
95
|
+
|
96
|
+
static int
|
97
|
+
memcmp_hash_compare(st_data_t a, st_data_t b)
|
98
|
+
{
|
99
|
+
struct memcmp_key_data *k1 = (struct memcmp_key_data *)a;
|
100
|
+
struct memcmp_key_data *k2 = (struct memcmp_key_data *)b;
|
101
|
+
|
102
|
+
return (k1->path == k2->path && k1->line == k2->line) ? 0 : 1;
|
103
|
+
}
|
104
|
+
|
105
|
+
static st_index_t
|
106
|
+
memcmp_hash_hash(st_data_t a)
|
107
|
+
{
|
108
|
+
struct memcmp_key_data *k1 = (struct memcmp_key_data *)a;
|
109
|
+
return (((st_index_t)k1->path) << 8) & (st_index_t)k1->line;
|
110
|
+
}
|
111
|
+
|
112
|
+
static const struct st_hash_type memcmp_hash_type = {
|
113
|
+
memcmp_hash_compare, memcmp_hash_hash
|
114
|
+
};
|
115
|
+
|
116
|
+
static struct traceobj_arg *tmp_trace_arg; /* TODO: Do not use global variables */
|
117
|
+
|
118
|
+
static struct traceobj_arg *
|
119
|
+
get_traceobj_arg(void)
|
120
|
+
{
|
121
|
+
if (tmp_trace_arg == 0) {
|
122
|
+
tmp_trace_arg = ALLOC_N(struct traceobj_arg, 1);
|
123
|
+
tmp_trace_arg->aggregate_table = st_init_table(&memcmp_hash_type);
|
124
|
+
tmp_trace_arg->object_table = st_init_numtable();
|
125
|
+
tmp_trace_arg->str_table = st_init_strtable();
|
126
|
+
tmp_trace_arg->freed_allocation_info = NULL;
|
127
|
+
}
|
128
|
+
return tmp_trace_arg;
|
129
|
+
}
|
130
|
+
|
131
|
+
static int
|
132
|
+
free_keys_i(st_data_t key, st_data_t value, void *data)
|
133
|
+
{
|
134
|
+
ruby_xfree((void *)key);
|
135
|
+
return ST_CONTINUE;
|
136
|
+
}
|
137
|
+
|
138
|
+
static int
|
139
|
+
free_values_i(st_data_t key, st_data_t value, void *data)
|
140
|
+
{
|
141
|
+
ruby_xfree((void *)value);
|
142
|
+
return ST_CONTINUE;
|
143
|
+
}
|
144
|
+
|
145
|
+
static int
|
146
|
+
free_key_values_i(st_data_t key, st_data_t value, void *data)
|
147
|
+
{
|
148
|
+
ruby_xfree((void *)key);
|
149
|
+
ruby_xfree((void *)value);
|
150
|
+
return ST_CONTINUE;
|
151
|
+
}
|
152
|
+
|
153
|
+
static void
|
154
|
+
clear_traceobj_arg(void)
|
155
|
+
{
|
156
|
+
struct traceobj_arg * arg = get_traceobj_arg();
|
157
|
+
|
158
|
+
st_foreach(arg->aggregate_table, free_key_values_i, 0);
|
159
|
+
st_clear(arg->aggregate_table);
|
160
|
+
st_foreach(arg->object_table, free_values_i, 0);
|
161
|
+
st_clear(arg->object_table);
|
162
|
+
st_foreach(arg->str_table, free_keys_i, 0);
|
163
|
+
st_clear(arg->str_table);
|
164
|
+
}
|
165
|
+
|
166
|
+
static struct allocation_info *
|
167
|
+
create_allocation_info(void)
|
168
|
+
{
|
169
|
+
return (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
|
170
|
+
}
|
171
|
+
|
172
|
+
static void
|
173
|
+
free_allocation_info(struct traceobj_arg *arg, struct allocation_info *info)
|
174
|
+
{
|
175
|
+
delete_unique_str(arg->str_table, info->path);
|
176
|
+
delete_unique_str(arg->str_table, info->class_path);
|
177
|
+
ruby_xfree(info);
|
178
|
+
}
|
179
|
+
|
180
|
+
static void
|
181
|
+
newobj_i(VALUE tpval, void *data)
|
182
|
+
{
|
183
|
+
struct traceobj_arg *arg = (struct traceobj_arg *)data;
|
184
|
+
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
185
|
+
VALUE obj = rb_tracearg_object(tparg);
|
186
|
+
VALUE path = rb_tracearg_path(tparg);
|
187
|
+
VALUE line = rb_tracearg_lineno(tparg);
|
188
|
+
VALUE mid = rb_tracearg_method_id(tparg);
|
189
|
+
VALUE klass = rb_tracearg_defined_class(tparg);
|
190
|
+
struct allocation_info *info;
|
191
|
+
const char *path_cstr = RTEST(path) ? make_unique_str(arg->str_table, RSTRING_PTR(path), RSTRING_LEN(path)) : 0;
|
192
|
+
VALUE class_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_class_path_cached(klass) : Qnil;
|
193
|
+
const char *class_path_cstr = RTEST(class_path) ? make_unique_str(arg->str_table, RSTRING_PTR(class_path), RSTRING_LEN(class_path)) : 0;
|
194
|
+
|
195
|
+
if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
|
196
|
+
if (info->living) {
|
197
|
+
/* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */
|
198
|
+
}
|
199
|
+
/* reuse info */
|
200
|
+
delete_unique_str(arg->str_table, info->path);
|
201
|
+
delete_unique_str(arg->str_table, info->class_path);
|
202
|
+
}
|
203
|
+
else {
|
204
|
+
info = create_allocation_info();
|
205
|
+
}
|
206
|
+
|
207
|
+
info->next = NULL;
|
208
|
+
info->living = 1;
|
209
|
+
info->flags = RBASIC(obj)->flags;
|
210
|
+
info->klass = RBASIC_CLASS(obj);
|
211
|
+
|
212
|
+
info->path = path_cstr;
|
213
|
+
info->line = NUM2INT(line);
|
214
|
+
info->mid = mid;
|
215
|
+
info->class_path = class_path_cstr;
|
216
|
+
info->generation = rb_gc_count();
|
217
|
+
st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
|
218
|
+
}
|
219
|
+
|
220
|
+
#define MAX_KEY_SIZE 4
|
221
|
+
|
222
|
+
void
|
223
|
+
aggregator_i(void *data)
|
224
|
+
{
|
225
|
+
size_t gc_count = rb_gc_count();
|
226
|
+
struct traceobj_arg *arg = (struct traceobj_arg *)data;
|
227
|
+
struct allocation_info *info = arg->freed_allocation_info;
|
228
|
+
|
229
|
+
arg->freed_allocation_info = NULL;
|
230
|
+
|
231
|
+
while (info) {
|
232
|
+
struct allocation_info *next_info = info->next;
|
233
|
+
st_data_t key, val;
|
234
|
+
struct memcmp_key_data key_data;
|
235
|
+
int *val_buff;
|
236
|
+
int age = (int)(gc_count - info->generation);
|
237
|
+
|
238
|
+
key_data.path = info->path;
|
239
|
+
key_data.line = info->line;
|
240
|
+
key = (st_data_t)&key_data;
|
241
|
+
keep_unique_str(arg->str_table, info->path);
|
242
|
+
|
243
|
+
if (st_lookup(arg->aggregate_table, key, &val) == 0) {
|
244
|
+
struct memcmp_key_data *key_buff = ALLOC_N(struct memcmp_key_data, 1);
|
245
|
+
*key_buff = key_data;
|
246
|
+
key = (st_data_t)key_buff;
|
247
|
+
|
248
|
+
/* count, total age, max age, min age */
|
249
|
+
val_buff = ALLOC_N(int, 4);
|
250
|
+
val_buff[0] = val_buff[1] = 0;
|
251
|
+
val_buff[2] = val_buff[3] = age;
|
252
|
+
|
253
|
+
st_insert(arg->aggregate_table, (st_data_t)key_buff, (st_data_t)val_buff);
|
254
|
+
}
|
255
|
+
else {
|
256
|
+
val_buff = (int *)val;
|
257
|
+
}
|
258
|
+
|
259
|
+
val_buff[0] += 1;
|
260
|
+
val_buff[1] += age;
|
261
|
+
if (val_buff[2] > age) val_buff[2] = age;
|
262
|
+
if (val_buff[3] < age) val_buff[3] = age;
|
263
|
+
|
264
|
+
free_allocation_info(arg, info);
|
265
|
+
info = next_info;
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
static void
|
270
|
+
move_to_freed_list(struct traceobj_arg *arg, VALUE obj, struct allocation_info *info)
|
271
|
+
{
|
272
|
+
info->next = arg->freed_allocation_info;
|
273
|
+
arg->freed_allocation_info = info;
|
274
|
+
st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
|
275
|
+
}
|
276
|
+
|
277
|
+
static void
|
278
|
+
freeobj_i(VALUE tpval, void *data)
|
279
|
+
{
|
280
|
+
struct traceobj_arg *arg = (struct traceobj_arg *)data;
|
281
|
+
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
282
|
+
VALUE obj = rb_tracearg_object(tparg);
|
283
|
+
struct allocation_info *info;
|
284
|
+
|
285
|
+
if (arg->freed_allocation_info == NULL) {
|
286
|
+
rb_postponed_job_register_one(0, aggregator_i, arg);
|
287
|
+
}
|
288
|
+
|
289
|
+
if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
|
290
|
+
move_to_freed_list(arg, obj, info);
|
291
|
+
}
|
292
|
+
}
|
293
|
+
|
294
|
+
static void
|
295
|
+
start_alloc_hooks(VALUE mod)
|
296
|
+
{
|
297
|
+
VALUE newobj_hook, freeobj_hook;
|
298
|
+
struct traceobj_arg *arg = get_traceobj_arg();
|
299
|
+
|
300
|
+
if ((newobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("newobj_hook"))) == Qnil) {
|
301
|
+
rb_ivar_set(rb_mGCTracer, rb_intern("newobj_hook"), newobj_hook = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg));
|
302
|
+
rb_ivar_set(rb_mGCTracer, rb_intern("freeobj_hook"), freeobj_hook = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg));
|
303
|
+
}
|
304
|
+
else {
|
305
|
+
freeobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("freeobj_hook"));
|
306
|
+
}
|
307
|
+
|
308
|
+
rb_tracepoint_enable(newobj_hook);
|
309
|
+
rb_tracepoint_enable(freeobj_hook);
|
310
|
+
}
|
311
|
+
|
312
|
+
static int
|
313
|
+
aggregate_result_i(st_data_t key, st_data_t val, void *data)
|
314
|
+
{
|
315
|
+
VALUE result = (VALUE)data;
|
316
|
+
int *val_buff = (int *)val;
|
317
|
+
struct memcmp_key_data *key_buff = (struct memcmp_key_data *)key;
|
318
|
+
VALUE k = rb_ary_new3(2, rb_str_new2(key_buff->path), INT2FIX(key_buff->line));
|
319
|
+
VALUE v = rb_ary_new3(4, INT2FIX(val_buff[0]), INT2FIX(val_buff[1]), INT2FIX(val_buff[2]), INT2FIX(val_buff[3]));
|
320
|
+
|
321
|
+
rb_hash_aset(result, k, v);
|
322
|
+
|
323
|
+
return ST_CONTINUE;
|
324
|
+
}
|
325
|
+
|
326
|
+
static int
|
327
|
+
aggregate_rest_object_i(st_data_t key, st_data_t val, void *data)
|
328
|
+
{
|
329
|
+
struct traceobj_arg *arg = (struct traceobj_arg *)data;
|
330
|
+
struct allocation_info *info = (struct allocation_info *)val;
|
331
|
+
move_to_freed_list(arg, (VALUE)key, info);
|
332
|
+
return ST_CONTINUE;
|
333
|
+
}
|
334
|
+
|
335
|
+
static VALUE
|
336
|
+
aggregate_result(struct traceobj_arg *arg)
|
337
|
+
{
|
338
|
+
VALUE result = rb_hash_new();
|
339
|
+
st_foreach(arg->object_table, aggregate_rest_object_i, (st_data_t)arg);
|
340
|
+
aggregator_i(arg);
|
341
|
+
st_foreach(arg->aggregate_table, aggregate_result_i, (st_data_t)result);
|
342
|
+
clear_traceobj_arg();
|
343
|
+
return result;
|
344
|
+
}
|
345
|
+
|
346
|
+
static VALUE
|
347
|
+
stop_allocation_tracing(VALUE self)
|
348
|
+
{
|
349
|
+
VALUE newobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("newobj_hook"));
|
350
|
+
VALUE freeobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("freeobj_hook"));
|
351
|
+
|
352
|
+
/* stop hooks */
|
353
|
+
if (newobj_hook && freeobj_hook) {
|
354
|
+
rb_tracepoint_disable(newobj_hook);
|
355
|
+
rb_tracepoint_disable(freeobj_hook);
|
356
|
+
}
|
357
|
+
else {
|
358
|
+
rb_raise(rb_eRuntimeError, "not started yet.");
|
359
|
+
}
|
360
|
+
|
361
|
+
return Qnil;
|
362
|
+
}
|
363
|
+
|
364
|
+
VALUE
|
365
|
+
gc_tracer_stop_allocation_tracing(VALUE self)
|
366
|
+
{
|
367
|
+
stop_allocation_tracing(self);
|
368
|
+
return aggregate_result(get_traceobj_arg());
|
369
|
+
}
|
370
|
+
|
371
|
+
VALUE
|
372
|
+
gc_tracer_start_allocation_tracing(int argc, VALUE *argv, VALUE self)
|
373
|
+
{
|
374
|
+
if (rb_ivar_get(rb_mGCTracer, rb_intern("allocation_tracer")) != Qnil) {
|
375
|
+
rb_raise(rb_eRuntimeError, "can't run recursive");
|
376
|
+
}
|
377
|
+
else {
|
378
|
+
start_alloc_hooks(rb_mGCTracer);
|
379
|
+
|
380
|
+
if (rb_block_given_p()) {
|
381
|
+
rb_ensure(rb_yield, Qnil, stop_allocation_tracing, Qnil);
|
382
|
+
return aggregate_result(get_traceobj_arg());
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
return Qnil;
|
387
|
+
}
|
388
|
+
|
data/ext/gc_tracer/gc_tracer.c
CHANGED
@@ -781,6 +781,10 @@ gc_tracer_start_objspace_recording(int argc, VALUE *argv, VALUE self)
|
|
781
781
|
|
782
782
|
#endif /* HAVE_RB_OBJSPACE_EACH_OBJECTS_WITHOUT_SETUP */
|
783
783
|
|
784
|
+
VALUE gc_tracer_start_allocation_tracing(int argc, VALUE *argv, VALUE self);
|
785
|
+
VALUE gc_tracer_stop_allocation_tracing(VALUE self);
|
786
|
+
VALUE rb_mGCTracer;
|
787
|
+
|
784
788
|
/**
|
785
789
|
* GC::Tracer traces GC/ObjectSpace behavior.
|
786
790
|
*
|
@@ -801,7 +805,7 @@ gc_tracer_start_objspace_recording(int argc, VALUE *argv, VALUE self)
|
|
801
805
|
void
|
802
806
|
Init_gc_tracer(void)
|
803
807
|
{
|
804
|
-
VALUE mod = rb_define_module_under(rb_mGC, "Tracer");
|
808
|
+
VALUE mod = rb_mGCTracer = rb_define_module_under(rb_mGC, "Tracer");
|
805
809
|
|
806
810
|
/* logging methods */
|
807
811
|
rb_define_module_function(mod, "start_logging", gc_tracer_start_logging, -1);
|
@@ -814,6 +818,10 @@ Init_gc_tracer(void)
|
|
814
818
|
rb_define_module_function(mod, "stop_objspace_recording", gc_tracer_stop_objspace_recording, 0);
|
815
819
|
#endif
|
816
820
|
|
821
|
+
/* allocation tracer methods */
|
822
|
+
rb_define_module_function(mod, "start_allocation_tracing", gc_tracer_start_allocation_tracing, -1);
|
823
|
+
rb_define_module_function(mod, "stop_allocation_tracing", gc_tracer_stop_allocation_tracing, 0);
|
824
|
+
|
817
825
|
/* setup default banners */
|
818
826
|
setup_gc_trace_symbols();
|
819
827
|
start_tick = tick();
|
data/lib/gc_tracer/version.rb
CHANGED
data/spec/gc_tracer_spec.rb
CHANGED
@@ -29,20 +29,20 @@ describe GC::Tracer do
|
|
29
29
|
end
|
30
30
|
|
31
31
|
shared_examples "objspace_recorder_test" do
|
32
|
-
|
32
|
+
dirname = "gc_tracer_objspace_recorder_spec.#{$$}"
|
33
33
|
|
34
34
|
it do
|
35
35
|
begin
|
36
|
-
GC::Tracer.start_objspace_recording(
|
36
|
+
GC::Tracer.start_objspace_recording(dirname){
|
37
37
|
count.times{
|
38
38
|
GC.start
|
39
39
|
}
|
40
40
|
}
|
41
|
-
expect(Dir.glob("#{
|
41
|
+
expect(Dir.glob("#{dirname}/ppm/*.ppm").size).to be >= count * 3
|
42
42
|
rescue NoMethodError
|
43
43
|
pending "start_objspace_recording requires MRI >= 2.2"
|
44
44
|
ensure
|
45
|
-
FileUtils.rm_rf(
|
45
|
+
FileUtils.rm_rf(dirname) if File.directory?(dirname)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
@@ -56,4 +56,16 @@ describe GC::Tracer do
|
|
56
56
|
let(:count){2}
|
57
57
|
it_behaves_like "objspace_recorder_test"
|
58
58
|
end
|
59
|
+
|
60
|
+
describe 'GC::Tracer.start_allocation_tracing' do
|
61
|
+
it do
|
62
|
+
line = __LINE__ + 2
|
63
|
+
result = GC::Tracer.start_allocation_tracing do
|
64
|
+
Object.new
|
65
|
+
end
|
66
|
+
|
67
|
+
expect(result.length).to be >= 1
|
68
|
+
expect(result[[__FILE__, line]]).to eq [1, 0, 0, 0]
|
69
|
+
end
|
70
|
+
end
|
59
71
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gc_tracer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koichi Sasada
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -81,10 +81,12 @@ files:
|
|
81
81
|
- README.md
|
82
82
|
- Rakefile
|
83
83
|
- bin/objspace_recorder_convert.rb
|
84
|
+
- ext/gc_tracer/allocation_tracer.c
|
84
85
|
- ext/gc_tracer/extconf.rb
|
85
86
|
- ext/gc_tracer/gc_tracer.c
|
86
87
|
- gc_tracer.gemspec
|
87
88
|
- lib/gc_tracer.rb
|
89
|
+
- lib/gc_tracer/allocation_trace.rb
|
88
90
|
- lib/gc_tracer/version.rb
|
89
91
|
- lib/gc_tracer/viewer.html.erb
|
90
92
|
- public/jquery-2.1.0.min.js
|