allocation_tracer 0.1.1 → 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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79049581513cae857637ce2a806116bb7f4f409a
|
4
|
+
data.tar.gz: 0d24ddb1dd4328def9f76afce88761364fcea0e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e5d80fbdfd2e5bde9c13d05ccebc4ec3bd87425b0599e83d277fe690366d428352f8acaa2892a92b9dc6d4f7d23a440978412692e5c245b3bc5633b661fec69
|
7
|
+
data.tar.gz: 81f1ab724b85bb73c079490c065b1649a4ef721a6e84ccd7e5e2a3b1f97c023628e82b2eddd4c5517c433b89dee017cb6603190931b291ff68dcd26244116316
|
@@ -7,6 +7,7 @@
|
|
7
7
|
|
8
8
|
#include "ruby/ruby.h"
|
9
9
|
#include "ruby/debug.h"
|
10
|
+
#include <assert.h>
|
10
11
|
|
11
12
|
size_t rb_obj_memsize_of(VALUE obj); /* in gc.c */
|
12
13
|
|
@@ -223,7 +224,7 @@ newobj_i(VALUE tpval, void *data)
|
|
223
224
|
info->flags = RBASIC(obj)->flags;
|
224
225
|
info->living = 1;
|
225
226
|
info->memsize = 0;
|
226
|
-
info->klass = RTEST(klass) ? rb_class_real(klass) : Qnil;
|
227
|
+
info->klass = (RTEST(klass) && !RB_TYPE_P(obj, T_NODE)) ? rb_class_real(klass) : Qnil;
|
227
228
|
info->generation = rb_gc_count();
|
228
229
|
|
229
230
|
info->path = path_cstr;
|
@@ -235,69 +236,73 @@ newobj_i(VALUE tpval, void *data)
|
|
235
236
|
/* file, line, type, klass */
|
236
237
|
#define MAX_KEY_SIZE 4
|
237
238
|
|
238
|
-
void
|
239
|
-
|
239
|
+
static void
|
240
|
+
aggregate_each_info(struct traceobj_arg *arg, struct allocation_info *info, size_t gc_count)
|
240
241
|
{
|
241
|
-
|
242
|
-
struct
|
243
|
-
|
242
|
+
st_data_t key, val;
|
243
|
+
struct memcmp_key_data key_data;
|
244
|
+
size_t *val_buff;
|
245
|
+
size_t age = (int)(gc_count - info->generation);
|
246
|
+
int i = 0;
|
244
247
|
|
245
|
-
arg->
|
248
|
+
if (arg->keys & KEY_PATH) {
|
249
|
+
key_data.data[i++] = (st_data_t)info->path;
|
250
|
+
}
|
251
|
+
if (arg->keys & KEY_LINE) {
|
252
|
+
key_data.data[i++] = (st_data_t)info->line;
|
253
|
+
}
|
254
|
+
if (arg->keys & KEY_TYPE) {
|
255
|
+
key_data.data[i++] = (st_data_t)(info->flags & T_MASK);
|
256
|
+
}
|
257
|
+
if (arg->keys & KEY_CLASS) {
|
258
|
+
key_data.data[i++] = info->klass;
|
259
|
+
}
|
260
|
+
key_data.n = i;
|
261
|
+
key = (st_data_t)&key_data;
|
246
262
|
|
247
|
-
|
248
|
-
struct
|
249
|
-
|
250
|
-
struct memcmp_key_data key_data;
|
251
|
-
size_t *val_buff;
|
252
|
-
size_t age = (int)(gc_count - info->generation);
|
253
|
-
int i;
|
263
|
+
if (st_lookup(arg->aggregate_table, key, &val) == 0) {
|
264
|
+
struct memcmp_key_data *key_buff = ruby_xmalloc(sizeof(int) + sizeof(st_data_t) * key_data.n);
|
265
|
+
key_buff->n = key_data.n;
|
254
266
|
|
255
|
-
i
|
256
|
-
|
257
|
-
key_data.data[i++] = (st_data_t)info->path;
|
258
|
-
}
|
259
|
-
if (arg->keys & KEY_LINE) {
|
260
|
-
key_data.data[i++] = (st_data_t)info->line;
|
261
|
-
}
|
262
|
-
if (arg->keys & KEY_TYPE) {
|
263
|
-
key_data.data[i++] = (st_data_t)(info->flags & T_MASK);
|
264
|
-
}
|
265
|
-
if (arg->keys & KEY_CLASS) {
|
266
|
-
key_data.data[i++] = info->klass;
|
267
|
+
for (i=0; i<key_data.n; i++) {
|
268
|
+
key_buff->data[i] = key_data.data[i];
|
267
269
|
}
|
268
|
-
|
269
|
-
key = (st_data_t)&key_data;
|
270
|
+
key = (st_data_t)key_buff;
|
270
271
|
|
271
|
-
|
272
|
-
|
273
|
-
|
272
|
+
/* count, total age, max age, min age */
|
273
|
+
val_buff = ALLOC_N(size_t, 6);
|
274
|
+
val_buff[0] = val_buff[1] = val_buff[2] = 0;
|
275
|
+
val_buff[3] = val_buff[4] = age;
|
276
|
+
val_buff[5] = 0;
|
274
277
|
|
275
|
-
|
276
|
-
key_buff->data[i] = key_data.data[i];
|
277
|
-
}
|
278
|
-
key = (st_data_t)key_buff;
|
278
|
+
if (arg->keys & KEY_PATH) keep_unique_str(arg->str_table, info->path);
|
279
279
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
280
|
+
st_insert(arg->aggregate_table, (st_data_t)key_buff, (st_data_t)val_buff);
|
281
|
+
}
|
282
|
+
else {
|
283
|
+
val_buff = (size_t *)val;
|
284
|
+
}
|
285
285
|
|
286
|
-
|
286
|
+
val_buff[0] += 1;
|
287
|
+
if (info->flags & FL_PROMOTED) val_buff[1] += 1;
|
288
|
+
val_buff[2] += age;
|
289
|
+
if (val_buff[3] > age) val_buff[3] = age; /* min */
|
290
|
+
if (val_buff[4] < age) val_buff[4] = age; /* max */
|
291
|
+
val_buff[5] += info->memsize;
|
292
|
+
}
|
287
293
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
294
|
+
static void
|
295
|
+
aggregate_freed_info(void *data)
|
296
|
+
{
|
297
|
+
size_t gc_count = rb_gc_count();
|
298
|
+
struct traceobj_arg *arg = (struct traceobj_arg *)data;
|
299
|
+
struct allocation_info *info = arg->freed_allocation_info;
|
293
300
|
|
294
|
-
|
295
|
-
if (info->flags & FL_PROMOTED) val_buff[1] += 1;
|
296
|
-
val_buff[2] += age;
|
297
|
-
if (val_buff[3] > age) val_buff[3] = age; /* min */
|
298
|
-
if (val_buff[4] < age) val_buff[4] = age; /* max */
|
299
|
-
val_buff[5] += info->memsize;
|
301
|
+
arg->freed_allocation_info = NULL;
|
300
302
|
|
303
|
+
while (info) {
|
304
|
+
struct allocation_info *next_info = info->next;
|
305
|
+
aggregate_each_info(arg, info, gc_count);
|
301
306
|
free_allocation_info(arg, info);
|
302
307
|
info = next_info;
|
303
308
|
}
|
@@ -318,15 +323,54 @@ freeobj_i(VALUE tpval, void *data)
|
|
318
323
|
VALUE obj = rb_tracearg_object(tparg);
|
319
324
|
struct allocation_info *info;
|
320
325
|
|
321
|
-
if (arg->freed_allocation_info == NULL) {
|
322
|
-
rb_postponed_job_register_one(0, aggregator_i, arg);
|
323
|
-
}
|
324
|
-
|
325
326
|
if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
|
326
327
|
info->flags = RBASIC(obj)->flags;
|
327
328
|
info->memsize = rb_obj_memsize_of(obj);
|
328
329
|
move_to_freed_list(arg, info);
|
329
330
|
st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
|
331
|
+
if (arg->freed_allocation_info == NULL) {
|
332
|
+
rb_postponed_job_register_one(0, aggregate_freed_info, arg);
|
333
|
+
}
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
static void
|
338
|
+
enable_newobj_hook(void)
|
339
|
+
{
|
340
|
+
VALUE newobj_hook;
|
341
|
+
|
342
|
+
if ((newobj_hook = rb_ivar_get(rb_mAllocationTracer, rb_intern("newobj_hook"))) == Qnil) {
|
343
|
+
rb_raise(rb_eRuntimeError, "not started.");
|
344
|
+
}
|
345
|
+
if (rb_tracepoint_enabled_p(newobj_hook)) {
|
346
|
+
rb_raise(rb_eRuntimeError, "newobj hooks is already enabled.");
|
347
|
+
}
|
348
|
+
|
349
|
+
rb_tracepoint_enable(newobj_hook);
|
350
|
+
}
|
351
|
+
|
352
|
+
static void
|
353
|
+
disable_newobj_hook(void)
|
354
|
+
{
|
355
|
+
VALUE newobj_hook;
|
356
|
+
|
357
|
+
if ((newobj_hook = rb_ivar_get(rb_mAllocationTracer, rb_intern("newobj_hook"))) == Qnil) {
|
358
|
+
rb_raise(rb_eRuntimeError, "not started.");
|
359
|
+
}
|
360
|
+
if (rb_tracepoint_enabled_p(newobj_hook) == Qfalse) {
|
361
|
+
rb_raise(rb_eRuntimeError, "newobj hooks is already disabled.");
|
362
|
+
}
|
363
|
+
|
364
|
+
rb_tracepoint_disable(newobj_hook);
|
365
|
+
}
|
366
|
+
|
367
|
+
static void
|
368
|
+
check_tracer_running(void)
|
369
|
+
{
|
370
|
+
struct traceobj_arg * arg = get_traceobj_arg();
|
371
|
+
|
372
|
+
if (!arg->running) {
|
373
|
+
rb_raise(rb_eRuntimeError, "not started yet");
|
330
374
|
}
|
331
375
|
}
|
332
376
|
|
@@ -348,6 +392,26 @@ start_alloc_hooks(VALUE mod)
|
|
348
392
|
rb_tracepoint_enable(freeobj_hook);
|
349
393
|
}
|
350
394
|
|
395
|
+
static VALUE
|
396
|
+
stop_alloc_hooks(VALUE self)
|
397
|
+
{
|
398
|
+
struct traceobj_arg * arg = get_traceobj_arg();
|
399
|
+
check_tracer_running();
|
400
|
+
|
401
|
+
{
|
402
|
+
VALUE newobj_hook = rb_ivar_get(rb_mAllocationTracer, rb_intern("newobj_hook"));
|
403
|
+
VALUE freeobj_hook = rb_ivar_get(rb_mAllocationTracer, rb_intern("freeobj_hook"));
|
404
|
+
rb_tracepoint_disable(newobj_hook);
|
405
|
+
rb_tracepoint_disable(freeobj_hook);
|
406
|
+
|
407
|
+
clear_traceobj_arg();
|
408
|
+
|
409
|
+
arg->running = 0;
|
410
|
+
}
|
411
|
+
|
412
|
+
return Qnil;
|
413
|
+
}
|
414
|
+
|
351
415
|
static const char *
|
352
416
|
type_name(int type)
|
353
417
|
{
|
@@ -427,7 +491,9 @@ aggregate_result_i(st_data_t key, st_data_t val, void *data)
|
|
427
491
|
rb_ary_push(k, INT2FIX((int)key_buff->data[i++]));
|
428
492
|
}
|
429
493
|
if (arg->keys & KEY_TYPE) {
|
430
|
-
|
494
|
+
int sym_index = key_buff->data[i++];
|
495
|
+
assert(T_MASK > sym_index);
|
496
|
+
rb_ary_push(k, type_symbols[sym_index]);
|
431
497
|
}
|
432
498
|
if (arg->keys & KEY_CLASS) {
|
433
499
|
VALUE klass = key_buff->data[i++];
|
@@ -448,17 +514,20 @@ aggregate_result_i(st_data_t key, st_data_t val, void *data)
|
|
448
514
|
}
|
449
515
|
|
450
516
|
static int
|
451
|
-
|
517
|
+
aggregate_live_object_i(st_data_t key, st_data_t val, void *data)
|
452
518
|
{
|
453
|
-
struct traceobj_arg *arg = (struct traceobj_arg *)data;
|
454
|
-
struct allocation_info *info = (struct allocation_info *)val;
|
455
519
|
VALUE obj = (VALUE)key;
|
520
|
+
struct allocation_info *info = (struct allocation_info *)val;
|
521
|
+
size_t gc_count = rb_gc_count();
|
522
|
+
struct traceobj_arg *arg = (struct traceobj_arg *)data;
|
456
523
|
|
457
|
-
if ((
|
524
|
+
if (BUILTIN_TYPE(obj) == (info->flags & T_MASK)) {
|
525
|
+
VALUE klass = RBASIC_CLASS(obj);
|
458
526
|
info->flags = RBASIC(obj)->flags;
|
527
|
+
info->klass = (RTEST(klass) && !RB_TYPE_P(obj, T_NODE)) ? rb_class_real(klass) : Qnil;
|
459
528
|
}
|
460
|
-
|
461
|
-
|
529
|
+
|
530
|
+
aggregate_each_info(arg, info, gc_count);
|
462
531
|
|
463
532
|
return ST_CONTINUE;
|
464
533
|
}
|
@@ -470,39 +539,64 @@ aggregate_result(struct traceobj_arg *arg)
|
|
470
539
|
aar.result = rb_hash_new();
|
471
540
|
aar.arg = arg;
|
472
541
|
|
473
|
-
|
474
|
-
|
475
|
-
aggregator_i(arg);
|
542
|
+
/* aggregated table -> Ruby hash */
|
543
|
+
aggregate_freed_info(arg);
|
476
544
|
st_foreach(arg->aggregate_table, aggregate_result_i, (st_data_t)&aar);
|
477
|
-
|
545
|
+
|
546
|
+
/* collect live aggregate table */
|
547
|
+
{
|
548
|
+
st_table *dead_object_aggregate_table = arg->aggregate_table;
|
549
|
+
|
550
|
+
/* make live object aggregate table */
|
551
|
+
arg->aggregate_table = st_init_table(&memcmp_hash_type);
|
552
|
+
st_foreach(arg->object_table, aggregate_live_object_i, (st_data_t)arg);
|
553
|
+
|
554
|
+
/* aggregate table -> Ruby hash */
|
555
|
+
st_foreach(arg->aggregate_table, aggregate_result_i, (st_data_t)&aar);
|
556
|
+
|
557
|
+
/* remove live object aggregate table */
|
558
|
+
st_foreach(arg->aggregate_table, free_key_values_i, 0);
|
559
|
+
st_free_table(arg->aggregate_table);
|
560
|
+
|
561
|
+
arg->aggregate_table = dead_object_aggregate_table;
|
562
|
+
}
|
478
563
|
return aar.result;
|
479
564
|
}
|
480
565
|
|
481
566
|
static VALUE
|
482
|
-
|
567
|
+
allocation_tracer_stop(VALUE self)
|
568
|
+
{
|
569
|
+
VALUE result = aggregate_result(get_traceobj_arg());
|
570
|
+
stop_alloc_hooks(self);
|
571
|
+
return result;
|
572
|
+
}
|
573
|
+
|
574
|
+
static VALUE
|
575
|
+
allocation_tracer_result(VALUE self)
|
483
576
|
{
|
484
|
-
|
577
|
+
VALUE result;
|
578
|
+
struct traceobj_arg *arg = get_traceobj_arg();
|
485
579
|
|
486
|
-
|
487
|
-
VALUE newobj_hook = rb_ivar_get(rb_mAllocationTracer, rb_intern("newobj_hook"));
|
488
|
-
VALUE freeobj_hook = rb_ivar_get(rb_mAllocationTracer, rb_intern("freeobj_hook"));
|
489
|
-
rb_tracepoint_disable(newobj_hook);
|
490
|
-
rb_tracepoint_disable(freeobj_hook);
|
580
|
+
check_tracer_running();
|
491
581
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
582
|
+
disable_newobj_hook();
|
583
|
+
result = aggregate_result(arg);
|
584
|
+
enable_newobj_hook();
|
585
|
+
return result;
|
586
|
+
}
|
497
587
|
|
588
|
+
static VALUE
|
589
|
+
allocation_tracer_clear(VALUE self)
|
590
|
+
{
|
591
|
+
clear_traceobj_arg();
|
498
592
|
return Qnil;
|
499
593
|
}
|
500
594
|
|
501
595
|
static VALUE
|
502
|
-
|
596
|
+
allocation_tracer_trace_i(VALUE self)
|
503
597
|
{
|
504
|
-
|
505
|
-
return
|
598
|
+
rb_yield(Qnil);
|
599
|
+
return allocation_tracer_result(self);
|
506
600
|
}
|
507
601
|
|
508
602
|
static VALUE
|
@@ -519,14 +613,29 @@ allocation_tracer_trace(VALUE self)
|
|
519
613
|
start_alloc_hooks(rb_mAllocationTracer);
|
520
614
|
|
521
615
|
if (rb_block_given_p()) {
|
522
|
-
rb_ensure(
|
523
|
-
return aggregate_result(get_traceobj_arg());
|
616
|
+
return rb_ensure(allocation_tracer_trace_i, self, stop_alloc_hooks, Qnil);
|
524
617
|
}
|
525
618
|
}
|
526
619
|
|
527
620
|
return Qnil;
|
528
621
|
}
|
529
622
|
|
623
|
+
static VALUE
|
624
|
+
allocation_tracer_pause(VALUE self)
|
625
|
+
{
|
626
|
+
check_tracer_running();
|
627
|
+
disable_newobj_hook();
|
628
|
+
return Qnil;
|
629
|
+
}
|
630
|
+
|
631
|
+
static VALUE
|
632
|
+
allocation_tracer_resume(VALUE self)
|
633
|
+
{
|
634
|
+
check_tracer_running();
|
635
|
+
enable_newobj_hook();
|
636
|
+
return Qnil;
|
637
|
+
}
|
638
|
+
|
530
639
|
static VALUE
|
531
640
|
allocation_tracer_setup(int argc, VALUE *argv, VALUE self)
|
532
641
|
{
|
@@ -588,7 +697,13 @@ Init_allocation_tracer(void)
|
|
588
697
|
|
589
698
|
/* allocation tracer methods */
|
590
699
|
rb_define_module_function(mod, "trace", allocation_tracer_trace, 0);
|
700
|
+
rb_define_module_function(mod, "start", allocation_tracer_trace, 0);
|
591
701
|
rb_define_module_function(mod, "stop", allocation_tracer_stop, 0);
|
702
|
+
rb_define_module_function(mod, "pause", allocation_tracer_pause, 0);
|
703
|
+
rb_define_module_function(mod, "resume", allocation_tracer_resume, 0);
|
704
|
+
|
705
|
+
rb_define_module_function(mod, "result", allocation_tracer_result, 0);
|
706
|
+
rb_define_module_function(mod, "clear", allocation_tracer_clear, 0);
|
592
707
|
rb_define_module_function(mod, "setup", allocation_tracer_setup, -1);
|
593
708
|
rb_define_module_function(mod, "header", allocation_tracer_header, 0);
|
594
709
|
}
|
Binary file
|
@@ -50,6 +50,72 @@ describe ObjectSpace::AllocationTracer do
|
|
50
50
|
expect(size).to be > 1234 if size > 0
|
51
51
|
end
|
52
52
|
|
53
|
+
it 'can be paused and resumed' do
|
54
|
+
line = __LINE__ + 2
|
55
|
+
result = ObjectSpace::AllocationTracer.trace do
|
56
|
+
Object.new
|
57
|
+
ObjectSpace::AllocationTracer.pause
|
58
|
+
Object.new # ignore tracing
|
59
|
+
ObjectSpace::AllocationTracer.resume
|
60
|
+
Object.new
|
61
|
+
end
|
62
|
+
|
63
|
+
expect(result.length).to be 2
|
64
|
+
expect(result[[__FILE__, line ]]).to eq [1, 0, 0, 0, 0, 0]
|
65
|
+
expect(result[[__FILE__, line + 4]]).to eq [1, 0, 0, 0, 0, 0]
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'can be get middle result' do
|
69
|
+
middle_result = nil
|
70
|
+
line = __LINE__ + 2
|
71
|
+
result = ObjectSpace::AllocationTracer.trace do
|
72
|
+
Object.new
|
73
|
+
middle_result = ObjectSpace::AllocationTracer.result
|
74
|
+
Object.new
|
75
|
+
end
|
76
|
+
|
77
|
+
expect(result.length).to be 2
|
78
|
+
expect(result[[__FILE__, line ]]).to eq [1, 0, 0, 0, 0, 0]
|
79
|
+
expect(result[[__FILE__, line + 2]]).to eq [1, 0, 0, 0, 0, 0]
|
80
|
+
|
81
|
+
expect(middle_result.length).to be 1
|
82
|
+
expect(middle_result[[__FILE__, line ]]).to eq [1, 0, 0, 0, 0, 0]
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'stop when not started yet' do
|
86
|
+
it 'should raise RuntimeError' do
|
87
|
+
expect do
|
88
|
+
ObjectSpace::AllocationTracer.stop
|
89
|
+
end.to raise_error(RuntimeError)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'pause when not started yet' do
|
94
|
+
it 'should raise RuntimeError' do
|
95
|
+
expect do
|
96
|
+
ObjectSpace::AllocationTracer.pause
|
97
|
+
end.to raise_error(RuntimeError)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'resume when not started yet' do
|
102
|
+
it 'should raise RuntimeError' do
|
103
|
+
expect do
|
104
|
+
ObjectSpace::AllocationTracer.resume
|
105
|
+
end.to raise_error(RuntimeError)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'when starting recursively' do
|
110
|
+
it 'should raise RuntimeError' do
|
111
|
+
expect do
|
112
|
+
ObjectSpace::AllocationTracer.trace{
|
113
|
+
ObjectSpace::AllocationTracer.trace{}
|
114
|
+
}
|
115
|
+
end.to raise_error(RuntimeError)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
53
119
|
describe 'with different setup' do
|
54
120
|
it 'should work with type' do
|
55
121
|
line = __LINE__ + 3
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: allocation_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-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|