gc_tracer 0.2.0 → 0.2.1
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 +58 -0
- data/ext/gc_tracer/allocation_tracer.c +207 -39
- data/ext/gc_tracer/gc_tracer.c +7 -2
- data/lib/gc_tracer/allocation_trace.rb +4 -2
- data/lib/gc_tracer/version.rb +1 -1
- data/spec/gc_tracer_spec.rb +13 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe62a5d40bec9e63a7f686a912ad5b7025de0baa
|
4
|
+
data.tar.gz: 8103c971df3779bb9d2aa3a57b98208540be98ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5901c6798b9f33b90e584db043ad102feba487c4206bc1b67b9d7d6570353bb08cf39980130bb767ec89fa22debb3c5ed9543ef531db2a8137bf7ee6e9a128b1
|
7
|
+
data.tar.gz: 5ce7a8796a30b4e3f05284a7108043ea942b52878904ec6925cf5def9ad1f4dc114cfee9dec0466f39fb7f5df0f5f82da34123ea511c404d33f4bc785e1e124c
|
data/README.md
CHANGED
@@ -89,6 +89,64 @@ In this case, 50,000 objects are created at `test.rb:6'. 44,290 is total
|
|
89
89
|
age of objects created at this line. Average age of object created at
|
90
90
|
this line is 50000/44290 = 0.8858. 0 is minimum age and 6 is maximum age.
|
91
91
|
|
92
|
+
You can also specify `type' in GC::Tracer.setup_allocation_tracing() to
|
93
|
+
specify what should be keys to aggregate like that.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
require 'gc_tracer'
|
97
|
+
require 'pp'
|
98
|
+
|
99
|
+
GC::Tracer.setup_allocation_tracing(%i{path line type})
|
100
|
+
|
101
|
+
result = GC::Tracer.start_allocation_tracing do
|
102
|
+
50_000.times{|i|
|
103
|
+
a = [i.to_s]
|
104
|
+
b = {i.to_s => nil}
|
105
|
+
c = (i.to_s .. i.to_s)
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
pp result
|
110
|
+
```
|
111
|
+
|
112
|
+
and you will get:
|
113
|
+
|
114
|
+
```
|
115
|
+
{["test.rb", 8, :T_STRING]=>[50000, 49067, 0, 17],
|
116
|
+
["test.rb", 8, :T_ARRAY]=>[50000, 49053, 0, 17],
|
117
|
+
["test.rb", 9, :T_STRING]=>[100000, 98146, 0, 17],
|
118
|
+
["test.rb", 9, :T_HASH]=>[50000, 49111, 0, 17],
|
119
|
+
["test.rb", 10, :T_STRING]=>[100000, 98267, 0, 17],
|
120
|
+
["test.rb", 10, :T_STRUCT]=>[50000, 49126, 0, 17]}
|
121
|
+
```
|
122
|
+
|
123
|
+
Interestingly, you can not see array creations in a middle of block:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
require 'gc_tracer'
|
127
|
+
require 'pp'
|
128
|
+
|
129
|
+
GC::Tracer.setup_allocation_tracing(%i{path line type})
|
130
|
+
|
131
|
+
result = GC::Tracer.start_allocation_tracing do
|
132
|
+
50_000.times{|i|
|
133
|
+
[i.to_s]
|
134
|
+
nil
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
pp result
|
139
|
+
```
|
140
|
+
|
141
|
+
and it prints:
|
142
|
+
|
143
|
+
```
|
144
|
+
{["test.rb", 8, :T_STRING]=>[50000, 38322, 0, 2]}
|
145
|
+
```
|
146
|
+
|
147
|
+
There are only string creation. This is because unused array creation is
|
148
|
+
ommitted by optimizer.
|
149
|
+
|
92
150
|
Simply you can require `gc_tracer/allocation_trace' to start allocation
|
93
151
|
tracer and output the aggregated information into stdot at the end of
|
94
152
|
program.
|
@@ -13,19 +13,29 @@ struct allocation_info {
|
|
13
13
|
int living;
|
14
14
|
VALUE flags;
|
15
15
|
VALUE klass;
|
16
|
+
const char *klass_path;
|
17
|
+
size_t generation;
|
16
18
|
|
17
19
|
/* allocation info */
|
18
20
|
const char *path;
|
19
21
|
unsigned long line;
|
20
|
-
const char *class_path;
|
21
|
-
VALUE mid;
|
22
|
-
size_t generation;
|
23
22
|
|
24
23
|
struct allocation_info *next;
|
25
24
|
};
|
26
25
|
|
26
|
+
#define KEY_PATH (1<<1)
|
27
|
+
#define KEY_LINE (1<<2)
|
28
|
+
#define KEY_TYPE (1<<3)
|
29
|
+
#define KEY_CLASS (1<<4)
|
30
|
+
|
31
|
+
#define VAL_COUNT (1<<1)
|
32
|
+
#define VAL_TOTAL_AGE (1<<2)
|
33
|
+
#define VAL_MAX_AGE (1<<3)
|
34
|
+
#define VAL_MIN_AGE (1<<4)
|
35
|
+
|
27
36
|
struct traceobj_arg {
|
28
37
|
int running;
|
38
|
+
int keys, vals;
|
29
39
|
st_table *aggregate_table; /* user defined key -> [count, total_age, max_age, min_age] */
|
30
40
|
st_table *object_table; /* obj (VALUE) -> allocation_info */
|
31
41
|
st_table *str_table; /* cstr -> refcount */
|
@@ -39,7 +49,7 @@ keep_unique_str(st_table *tbl, const char *str)
|
|
39
49
|
{
|
40
50
|
st_data_t n;
|
41
51
|
|
42
|
-
if (st_lookup(tbl, (st_data_t)str, &n)) {
|
52
|
+
if (str && st_lookup(tbl, (st_data_t)str, &n)) {
|
43
53
|
char *result;
|
44
54
|
|
45
55
|
st_insert(tbl, (st_data_t)str, n+1);
|
@@ -88,9 +98,12 @@ delete_unique_str(st_table *tbl, const char *str)
|
|
88
98
|
}
|
89
99
|
}
|
90
100
|
|
101
|
+
/* file, line, type */
|
102
|
+
#define MAX_KEY_DATA 4
|
103
|
+
|
91
104
|
struct memcmp_key_data {
|
92
|
-
|
93
|
-
|
105
|
+
int n;
|
106
|
+
st_data_t data[4];
|
94
107
|
};
|
95
108
|
|
96
109
|
static int
|
@@ -98,15 +111,14 @@ memcmp_hash_compare(st_data_t a, st_data_t b)
|
|
98
111
|
{
|
99
112
|
struct memcmp_key_data *k1 = (struct memcmp_key_data *)a;
|
100
113
|
struct memcmp_key_data *k2 = (struct memcmp_key_data *)b;
|
101
|
-
|
102
|
-
return (k1->path == k2->path && k1->line == k2->line) ? 0 : 1;
|
114
|
+
return memcmp(&k1->data[0], &k2->data[0], k1->n * sizeof(st_data_t));
|
103
115
|
}
|
104
116
|
|
105
117
|
static st_index_t
|
106
118
|
memcmp_hash_hash(st_data_t a)
|
107
119
|
{
|
108
|
-
struct memcmp_key_data *
|
109
|
-
return (
|
120
|
+
struct memcmp_key_data *k = (struct memcmp_key_data *)a;
|
121
|
+
return rb_memhash(k->data, sizeof(st_data_t) * k->n);
|
110
122
|
}
|
111
123
|
|
112
124
|
static const struct st_hash_type memcmp_hash_type = {
|
@@ -120,6 +132,9 @@ get_traceobj_arg(void)
|
|
120
132
|
{
|
121
133
|
if (tmp_trace_arg == 0) {
|
122
134
|
tmp_trace_arg = ALLOC_N(struct traceobj_arg, 1);
|
135
|
+
tmp_trace_arg->running = 0;
|
136
|
+
tmp_trace_arg->keys = 0;
|
137
|
+
tmp_trace_arg->vals = VAL_COUNT | VAL_TOTAL_AGE | VAL_MAX_AGE | VAL_MIN_AGE;
|
123
138
|
tmp_trace_arg->aggregate_table = st_init_table(&memcmp_hash_type);
|
124
139
|
tmp_trace_arg->object_table = st_init_numtable();
|
125
140
|
tmp_trace_arg->str_table = st_init_strtable();
|
@@ -173,7 +188,7 @@ static void
|
|
173
188
|
free_allocation_info(struct traceobj_arg *arg, struct allocation_info *info)
|
174
189
|
{
|
175
190
|
delete_unique_str(arg->str_table, info->path);
|
176
|
-
delete_unique_str(arg->str_table, info->
|
191
|
+
delete_unique_str(arg->str_table, info->klass_path);
|
177
192
|
ruby_xfree(info);
|
178
193
|
}
|
179
194
|
|
@@ -181,16 +196,15 @@ static void
|
|
181
196
|
newobj_i(VALUE tpval, void *data)
|
182
197
|
{
|
183
198
|
struct traceobj_arg *arg = (struct traceobj_arg *)data;
|
199
|
+
struct allocation_info *info;
|
184
200
|
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
185
201
|
VALUE obj = rb_tracearg_object(tparg);
|
202
|
+
VALUE klass = RBASIC_CLASS(obj);
|
186
203
|
VALUE path = rb_tracearg_path(tparg);
|
187
204
|
VALUE line = rb_tracearg_lineno(tparg);
|
188
|
-
VALUE
|
189
|
-
|
190
|
-
|
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;
|
205
|
+
VALUE klass_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_class_path_cached(klass) : Qnil;
|
206
|
+
const char *path_cstr = RTEST(path) ? make_unique_str(arg->str_table, RSTRING_PTR(path), RSTRING_LEN(path)) : NULL;
|
207
|
+
const char *klass_path_cstr = RTEST(klass_path) ? make_unique_str(arg->str_table, RSTRING_PTR(klass_path), RSTRING_LEN(klass_path)) : NULL;
|
194
208
|
|
195
209
|
if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
|
196
210
|
if (info->living) {
|
@@ -198,7 +212,7 @@ newobj_i(VALUE tpval, void *data)
|
|
198
212
|
}
|
199
213
|
/* reuse info */
|
200
214
|
delete_unique_str(arg->str_table, info->path);
|
201
|
-
delete_unique_str(arg->str_table, info->
|
215
|
+
delete_unique_str(arg->str_table, info->klass_path);
|
202
216
|
}
|
203
217
|
else {
|
204
218
|
info = create_allocation_info();
|
@@ -207,16 +221,17 @@ newobj_i(VALUE tpval, void *data)
|
|
207
221
|
info->next = NULL;
|
208
222
|
info->living = 1;
|
209
223
|
info->flags = RBASIC(obj)->flags;
|
210
|
-
info->klass =
|
224
|
+
info->klass = klass;
|
225
|
+
info->klass_path = klass_path_cstr;
|
226
|
+
info->generation = rb_gc_count();
|
211
227
|
|
212
228
|
info->path = path_cstr;
|
213
229
|
info->line = NUM2INT(line);
|
214
|
-
|
215
|
-
info->class_path = class_path_cstr;
|
216
|
-
info->generation = rb_gc_count();
|
230
|
+
|
217
231
|
st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
|
218
232
|
}
|
219
233
|
|
234
|
+
/* file, line, type, klass */
|
220
235
|
#define MAX_KEY_SIZE 4
|
221
236
|
|
222
237
|
void
|
@@ -234,15 +249,31 @@ aggregator_i(void *data)
|
|
234
249
|
struct memcmp_key_data key_data;
|
235
250
|
int *val_buff;
|
236
251
|
int age = (int)(gc_count - info->generation);
|
252
|
+
int i;
|
237
253
|
|
238
|
-
|
239
|
-
|
254
|
+
i = 0;
|
255
|
+
if (arg->keys & KEY_PATH) {
|
256
|
+
key_data.data[i++] = (st_data_t)info->path;
|
257
|
+
}
|
258
|
+
if (arg->keys & KEY_LINE) {
|
259
|
+
key_data.data[i++] = (st_data_t)info->line;
|
260
|
+
}
|
261
|
+
if (arg->keys & KEY_TYPE) {
|
262
|
+
key_data.data[i++] = (st_data_t)(info->flags & T_MASK);
|
263
|
+
}
|
264
|
+
if (arg->keys & KEY_CLASS) {
|
265
|
+
key_data.data[i++] = (st_data_t)info->klass_path;
|
266
|
+
}
|
267
|
+
key_data.n = i;
|
240
268
|
key = (st_data_t)&key_data;
|
241
|
-
keep_unique_str(arg->str_table, info->path);
|
242
269
|
|
243
270
|
if (st_lookup(arg->aggregate_table, key, &val) == 0) {
|
244
|
-
struct memcmp_key_data *key_buff =
|
245
|
-
|
271
|
+
struct memcmp_key_data *key_buff = ruby_xmalloc(sizeof(int) + sizeof(st_data_t) * key_data.n);
|
272
|
+
key_buff->n = key_data.n;
|
273
|
+
|
274
|
+
for (i=0; i<key_data.n; i++) {
|
275
|
+
key_buff->data[i] = key_data.data[i];
|
276
|
+
}
|
246
277
|
key = (st_data_t)key_buff;
|
247
278
|
|
248
279
|
/* count, total age, max age, min age */
|
@@ -250,6 +281,9 @@ aggregator_i(void *data)
|
|
250
281
|
val_buff[0] = val_buff[1] = 0;
|
251
282
|
val_buff[2] = val_buff[3] = age;
|
252
283
|
|
284
|
+
if (arg->keys & KEY_PATH) keep_unique_str(arg->str_table, info->path);
|
285
|
+
if (arg->keys & KEY_CLASS) keep_unique_str(arg->str_table, info->klass_path);
|
286
|
+
|
253
287
|
st_insert(arg->aggregate_table, (st_data_t)key_buff, (st_data_t)val_buff);
|
254
288
|
}
|
255
289
|
else {
|
@@ -309,14 +343,94 @@ start_alloc_hooks(VALUE mod)
|
|
309
343
|
rb_tracepoint_enable(freeobj_hook);
|
310
344
|
}
|
311
345
|
|
346
|
+
static const char *
|
347
|
+
type_name(int type)
|
348
|
+
{
|
349
|
+
switch (type) {
|
350
|
+
#define TYPE_NAME(t) case (t): return #t;
|
351
|
+
TYPE_NAME(T_NONE);
|
352
|
+
TYPE_NAME(T_OBJECT);
|
353
|
+
TYPE_NAME(T_CLASS);
|
354
|
+
TYPE_NAME(T_MODULE);
|
355
|
+
TYPE_NAME(T_FLOAT);
|
356
|
+
TYPE_NAME(T_STRING);
|
357
|
+
TYPE_NAME(T_REGEXP);
|
358
|
+
TYPE_NAME(T_ARRAY);
|
359
|
+
TYPE_NAME(T_HASH);
|
360
|
+
TYPE_NAME(T_STRUCT);
|
361
|
+
TYPE_NAME(T_BIGNUM);
|
362
|
+
TYPE_NAME(T_FILE);
|
363
|
+
TYPE_NAME(T_MATCH);
|
364
|
+
TYPE_NAME(T_COMPLEX);
|
365
|
+
TYPE_NAME(T_RATIONAL);
|
366
|
+
TYPE_NAME(T_NIL);
|
367
|
+
TYPE_NAME(T_TRUE);
|
368
|
+
TYPE_NAME(T_FALSE);
|
369
|
+
TYPE_NAME(T_SYMBOL);
|
370
|
+
TYPE_NAME(T_FIXNUM);
|
371
|
+
TYPE_NAME(T_UNDEF);
|
372
|
+
TYPE_NAME(T_NODE);
|
373
|
+
TYPE_NAME(T_ICLASS);
|
374
|
+
TYPE_NAME(T_ZOMBIE);
|
375
|
+
TYPE_NAME(T_DATA);
|
376
|
+
#undef TYPE_NAME
|
377
|
+
}
|
378
|
+
return "unknown";
|
379
|
+
}
|
380
|
+
|
381
|
+
struct arg_and_result {
|
382
|
+
struct traceobj_arg *arg;
|
383
|
+
VALUE result;
|
384
|
+
};
|
385
|
+
|
312
386
|
static int
|
313
387
|
aggregate_result_i(st_data_t key, st_data_t val, void *data)
|
314
388
|
{
|
315
|
-
|
389
|
+
struct arg_and_result *aar = (struct arg_and_result *)data;
|
390
|
+
struct traceobj_arg *arg = aar->arg;
|
391
|
+
VALUE result = aar->result;
|
392
|
+
|
316
393
|
int *val_buff = (int *)val;
|
317
394
|
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
395
|
VALUE v = rb_ary_new3(4, INT2FIX(val_buff[0]), INT2FIX(val_buff[1]), INT2FIX(val_buff[2]), INT2FIX(val_buff[3]));
|
396
|
+
VALUE k = rb_ary_new();
|
397
|
+
int i = 0;
|
398
|
+
static VALUE type_symbols[T_MASK] = {0};
|
399
|
+
|
400
|
+
if (type_symbols[0] == 0) {
|
401
|
+
int i;
|
402
|
+
for (i=0; i<T_MASK; i++) {
|
403
|
+
type_symbols[i] = ID2SYM(rb_intern(type_name(i)));
|
404
|
+
}
|
405
|
+
}
|
406
|
+
|
407
|
+
i = 0;
|
408
|
+
if (arg->keys & KEY_PATH) {
|
409
|
+
const char *path = (const char *)key_buff->data[i++];
|
410
|
+
if (path) {
|
411
|
+
rb_ary_push(k, rb_str_new2(path));
|
412
|
+
delete_unique_str(arg->str_table, path);
|
413
|
+
}
|
414
|
+
else {
|
415
|
+
rb_ary_push(k, Qnil);
|
416
|
+
}
|
417
|
+
}
|
418
|
+
if (arg->keys & KEY_LINE) {
|
419
|
+
rb_ary_push(k, INT2FIX((int)key_buff->data[i++]));
|
420
|
+
}
|
421
|
+
if (arg->keys & KEY_TYPE) {
|
422
|
+
rb_ary_push(k, type_symbols[key_buff->data[i++]]);
|
423
|
+
}
|
424
|
+
if (arg->keys & KEY_CLASS) {
|
425
|
+
const char *klass_path = (const char *)key_buff->data[i++];
|
426
|
+
if (klass_path) {
|
427
|
+
|
428
|
+
delete_unique_str(arg->str_table, klass_path);
|
429
|
+
}
|
430
|
+
else {
|
431
|
+
rb_ary_push(k, Qnil);
|
432
|
+
}
|
433
|
+
}
|
320
434
|
|
321
435
|
rb_hash_aset(result, k, v);
|
322
436
|
|
@@ -335,24 +449,29 @@ aggregate_rest_object_i(st_data_t key, st_data_t val, void *data)
|
|
335
449
|
static VALUE
|
336
450
|
aggregate_result(struct traceobj_arg *arg)
|
337
451
|
{
|
338
|
-
|
452
|
+
struct arg_and_result aar;
|
453
|
+
aar.result = rb_hash_new();
|
454
|
+
aar.arg = arg;
|
455
|
+
|
339
456
|
st_foreach(arg->object_table, aggregate_rest_object_i, (st_data_t)arg);
|
340
457
|
aggregator_i(arg);
|
341
|
-
st_foreach(arg->aggregate_table, aggregate_result_i, (st_data_t)
|
458
|
+
st_foreach(arg->aggregate_table, aggregate_result_i, (st_data_t)&aar);
|
342
459
|
clear_traceobj_arg();
|
343
|
-
return result;
|
460
|
+
return aar.result;
|
344
461
|
}
|
345
462
|
|
346
463
|
static VALUE
|
347
464
|
stop_allocation_tracing(VALUE self)
|
348
465
|
{
|
349
|
-
|
350
|
-
VALUE freeobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("freeobj_hook"));
|
466
|
+
struct traceobj_arg * arg = get_traceobj_arg();
|
351
467
|
|
352
|
-
|
353
|
-
|
468
|
+
if (arg->running) {
|
469
|
+
VALUE newobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("newobj_hook"));
|
470
|
+
VALUE freeobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("freeobj_hook"));
|
354
471
|
rb_tracepoint_disable(newobj_hook);
|
355
472
|
rb_tracepoint_disable(freeobj_hook);
|
473
|
+
|
474
|
+
arg->running = 0;
|
356
475
|
}
|
357
476
|
else {
|
358
477
|
rb_raise(rb_eRuntimeError, "not started yet.");
|
@@ -369,12 +488,16 @@ gc_tracer_stop_allocation_tracing(VALUE self)
|
|
369
488
|
}
|
370
489
|
|
371
490
|
VALUE
|
372
|
-
gc_tracer_start_allocation_tracing(
|
491
|
+
gc_tracer_start_allocation_tracing(VALUE self)
|
373
492
|
{
|
374
|
-
|
375
|
-
|
493
|
+
struct traceobj_arg * arg = get_traceobj_arg();
|
494
|
+
|
495
|
+
if (arg->running) {
|
496
|
+
rb_raise(rb_eRuntimeError, "can't run recursivly");
|
376
497
|
}
|
377
498
|
else {
|
499
|
+
arg->running = 1;
|
500
|
+
if (arg->keys == 0) arg->keys = KEY_PATH | KEY_LINE;
|
378
501
|
start_alloc_hooks(rb_mGCTracer);
|
379
502
|
|
380
503
|
if (rb_block_given_p()) {
|
@@ -386,3 +509,48 @@ gc_tracer_start_allocation_tracing(int argc, VALUE *argv, VALUE self)
|
|
386
509
|
return Qnil;
|
387
510
|
}
|
388
511
|
|
512
|
+
VALUE
|
513
|
+
gc_tracer_setup_allocation_tracing(int argc, VALUE *argv, VALUE self)
|
514
|
+
{
|
515
|
+
struct traceobj_arg * arg = get_traceobj_arg();
|
516
|
+
|
517
|
+
if (arg->running) {
|
518
|
+
rb_raise(rb_eRuntimeError, "can't change configuration during running");
|
519
|
+
}
|
520
|
+
else {
|
521
|
+
int i;
|
522
|
+
VALUE ary = rb_check_array_type(argv[0]);
|
523
|
+
|
524
|
+
for (i=0; i<(int)RARRAY_LEN(ary); i++) {
|
525
|
+
if (RARRAY_AREF(ary, i) == ID2SYM(rb_intern("path"))) arg->keys |= KEY_PATH;
|
526
|
+
else if (RARRAY_AREF(ary, i) == ID2SYM(rb_intern("line"))) arg->keys |= KEY_LINE;
|
527
|
+
else if (RARRAY_AREF(ary, i) == ID2SYM(rb_intern("type"))) arg->keys |= KEY_TYPE;
|
528
|
+
else if (RARRAY_AREF(ary, i) == ID2SYM(rb_intern("class"))) arg->keys |= KEY_CLASS;
|
529
|
+
else {
|
530
|
+
rb_raise(rb_eArgError, "not supported key type");
|
531
|
+
}
|
532
|
+
}
|
533
|
+
}
|
534
|
+
|
535
|
+
return Qnil;
|
536
|
+
}
|
537
|
+
|
538
|
+
VALUE
|
539
|
+
gc_tracer_header_of_allocation_tracing(VALUE self)
|
540
|
+
{
|
541
|
+
VALUE ary = rb_ary_new();
|
542
|
+
struct traceobj_arg * arg = get_traceobj_arg();
|
543
|
+
|
544
|
+
if (arg->keys & KEY_PATH) rb_ary_push(ary, ID2SYM(rb_intern("path")));
|
545
|
+
if (arg->keys & KEY_LINE) rb_ary_push(ary, ID2SYM(rb_intern("line")));
|
546
|
+
if (arg->keys & KEY_TYPE) rb_ary_push(ary, ID2SYM(rb_intern("type")));
|
547
|
+
if (arg->keys & KEY_CLASS) rb_ary_push(ary, ID2SYM(rb_intern("class")));
|
548
|
+
|
549
|
+
if (arg->vals & VAL_COUNT) rb_ary_push(ary, ID2SYM(rb_intern("count")));
|
550
|
+
if (arg->vals & VAL_TOTAL_AGE) rb_ary_push(ary, ID2SYM(rb_intern("total_age")));
|
551
|
+
if (arg->vals & VAL_MAX_AGE) rb_ary_push(ary, ID2SYM(rb_intern("max_age")));
|
552
|
+
if (arg->vals & VAL_MIN_AGE) rb_ary_push(ary, ID2SYM(rb_intern("min_age")));
|
553
|
+
|
554
|
+
return ary;
|
555
|
+
}
|
556
|
+
|
data/ext/gc_tracer/gc_tracer.c
CHANGED
@@ -781,8 +781,11 @@ 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(
|
784
|
+
VALUE gc_tracer_start_allocation_tracing(VALUE self);
|
785
785
|
VALUE gc_tracer_stop_allocation_tracing(VALUE self);
|
786
|
+
VALUE gc_tracer_setup_allocation_tracing(int argc, VALUE *argv, VALUE self);
|
787
|
+
VALUE gc_tracer_header_of_allocation_tracing(VALUE self);
|
788
|
+
|
786
789
|
VALUE rb_mGCTracer;
|
787
790
|
|
788
791
|
/**
|
@@ -819,8 +822,10 @@ Init_gc_tracer(void)
|
|
819
822
|
#endif
|
820
823
|
|
821
824
|
/* allocation tracer methods */
|
822
|
-
rb_define_module_function(mod, "start_allocation_tracing", gc_tracer_start_allocation_tracing,
|
825
|
+
rb_define_module_function(mod, "start_allocation_tracing", gc_tracer_start_allocation_tracing, 0);
|
823
826
|
rb_define_module_function(mod, "stop_allocation_tracing", gc_tracer_stop_allocation_tracing, 0);
|
827
|
+
rb_define_module_function(mod, "setup_allocation_tracing", gc_tracer_setup_allocation_tracing, -1);
|
828
|
+
rb_define_module_function(mod, "header_of_allocation_tracing", gc_tracer_header_of_allocation_tracing, 0);
|
824
829
|
|
825
830
|
/* setup default banners */
|
826
831
|
setup_gc_trace_symbols();
|
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'gc_tracer'
|
2
2
|
|
3
|
+
GC::Tracer.setup_allocation_tracing(%i{path line})
|
3
4
|
GC::Tracer.start_allocation_tracing
|
4
5
|
|
5
6
|
at_exit{
|
6
|
-
|
7
|
-
GC::Tracer.
|
7
|
+
results = GC::Tracer.stop_allocation_tracing
|
8
|
+
puts GC::Tracer.header_of_allocation_tracing.join("\t")
|
9
|
+
results.sort_by{|k, v| k}.each{|k, v|
|
8
10
|
puts (k+v).join("\t")
|
9
11
|
}
|
10
12
|
}
|
data/lib/gc_tracer/version.rb
CHANGED
data/spec/gc_tracer_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require 'fileutils'
|
|
4
4
|
|
5
5
|
describe GC::Tracer do
|
6
6
|
shared_examples "logging_test" do
|
7
|
-
it do
|
7
|
+
it 'should output correctly into file' do
|
8
8
|
Dir.mktmpdir('gc_tracer'){|dir|
|
9
9
|
logfile = "#{dir}/logging"
|
10
10
|
GC::Tracer.start_logging(logfile){
|
@@ -31,7 +31,7 @@ describe GC::Tracer do
|
|
31
31
|
shared_examples "objspace_recorder_test" do
|
32
32
|
dirname = "gc_tracer_objspace_recorder_spec.#{$$}"
|
33
33
|
|
34
|
-
it do
|
34
|
+
it 'should output snapshots correctly into directory' do
|
35
35
|
begin
|
36
36
|
GC::Tracer.start_objspace_recording(dirname){
|
37
37
|
count.times{
|
@@ -58,7 +58,17 @@ describe GC::Tracer do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
describe 'GC::Tracer.start_allocation_tracing' do
|
61
|
-
it do
|
61
|
+
it 'should includes allocation information' 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
|
+
|
71
|
+
it 'should run twice' do
|
62
72
|
line = __LINE__ + 2
|
63
73
|
result = GC::Tracer.start_allocation_tracing do
|
64
74
|
Object.new
|
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.2.
|
4
|
+
version: 0.2.1
|
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-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|