allocation_tracer 0.2.0 → 0.3.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: 79049581513cae857637ce2a806116bb7f4f409a
4
- data.tar.gz: 0d24ddb1dd4328def9f76afce88761364fcea0e8
3
+ metadata.gz: e21df296d813762ba6a5b3393ae95cbc09fa2560
4
+ data.tar.gz: 089e3fcaa7df385f58c91a3762d1c1f714cbb50a
5
5
  SHA512:
6
- metadata.gz: 6e5d80fbdfd2e5bde9c13d05ccebc4ec3bd87425b0599e83d277fe690366d428352f8acaa2892a92b9dc6d4f7d23a440978412692e5c245b3bc5633b661fec69
7
- data.tar.gz: 81f1ab724b85bb73c079490c065b1649a4ef721a6e84ccd7e5e2a3b1f97c023628e82b2eddd4c5517c433b89dee017cb6603190931b291ff68dcd26244116316
6
+ metadata.gz: a9f98fcbbd4a1e7cac48aaab69e10e9857b63050ffb8ec7e9d8ef9800d9668cfb2715ec2414ed277276edaf3ca53527f1606ec589656b71ea972d33d4a3d8765
7
+ data.tar.gz: 7d34cd7d37a60411664a82b94edb9b832cbe00a351c5b9b4da33adce9ee354b98f3f79d99ab594b6253675c1ffe4c6e3a3487052f4e7f7ba9ad586e823ff2f7d
data/README.md CHANGED
@@ -151,6 +151,40 @@ test.rb 7 50000 41650 0 5
151
151
 
152
152
  (tab separated colums)
153
153
 
154
+ ### Lifetime table
155
+
156
+ You can collect lifetime statistics with
157
+ ObjectSpace::AllocationTracer.lifetime_table method.
158
+
159
+ ```ruby
160
+ require 'pp'
161
+ require 'allocation_tracer'
162
+
163
+ ObjectSpace::AllocationTracer.lifetime_table_setup true
164
+ result = ObjectSpace::AllocationTracer.trace do
165
+ 100000.times{
166
+ Object.new
167
+ ''
168
+ }
169
+ end
170
+ pp ObjectSpace::AllocationTracer.lifetime_table
171
+ ```
172
+
173
+ will show
174
+
175
+ ```
176
+ {:T_OBJECT=>[3434, 96563, 0, 0, 1, 0, 0, 2],
177
+ :T_STRING=>[3435, 96556, 2, 1, 1, 1, 1, 1, 2]}
178
+ ```
179
+
180
+ This output means that the age of 3434 T_OBJECT objects are 0, 96563
181
+ objects are 1 and 2 objects are 7. Also the age of 3435 T_STRING
182
+ objects are 0, 96556 are 1 and so on.
183
+
184
+ Note that these number includes living objects and dead objects. For
185
+ dead object, age means lifetime. For living objects, age means current
186
+ age.
187
+
154
188
  ## Contributing
155
189
 
156
190
  1. Fork it ( http://github.com/<my-github-username>/allocation_tracer/fork )
@@ -13,6 +13,19 @@ size_t rb_obj_memsize_of(VALUE obj); /* in gc.c */
13
13
 
14
14
  static VALUE rb_mAllocationTracer;
15
15
 
16
+ struct traceobj_arg {
17
+ int running;
18
+ int keys, vals;
19
+ st_table *object_table; /* obj (VALUE) -> allocation_info */
20
+ st_table *str_table; /* cstr -> refcount */
21
+
22
+ st_table *aggregate_table; /* user defined key -> [count, total_age, max_age, min_age] */
23
+ struct allocation_info *freed_allocation_info;
24
+
25
+ /* */
26
+ size_t **lifetime_table;
27
+ };
28
+
16
29
  struct allocation_info {
17
30
  struct allocation_info *next;
18
31
 
@@ -44,15 +57,6 @@ struct allocation_info {
44
57
  #define VAL_MAX_AGE (1<<5)
45
58
  #define VAL_MEMSIZE (1<<6)
46
59
 
47
- struct traceobj_arg {
48
- int running;
49
- int keys, vals;
50
- st_table *aggregate_table; /* user defined key -> [count, total_age, max_age, min_age] */
51
- st_table *object_table; /* obj (VALUE) -> allocation_info */
52
- st_table *str_table; /* cstr -> refcount */
53
- struct allocation_info *freed_allocation_info;
54
- };
55
-
56
60
  static char *
57
61
  keep_unique_str(st_table *tbl, const char *str)
58
62
  {
@@ -145,6 +149,7 @@ get_traceobj_arg(void)
145
149
  tmp_trace_arg->object_table = st_init_numtable();
146
150
  tmp_trace_arg->str_table = st_init_strtable();
147
151
  tmp_trace_arg->freed_allocation_info = NULL;
152
+ tmp_trace_arg->lifetime_table = NULL;
148
153
  }
149
154
  return tmp_trace_arg;
150
155
  }
@@ -182,6 +187,17 @@ clear_traceobj_arg(void)
182
187
  st_clear(arg->object_table);
183
188
  st_foreach(arg->str_table, free_keys_i, 0);
184
189
  st_clear(arg->str_table);
190
+
191
+ if (arg->lifetime_table) {
192
+ int i;
193
+
194
+ for (i=0; i<T_MASK; i++) {
195
+ if (arg->lifetime_table[i] != NULL) {
196
+ free(arg->lifetime_table[i]);
197
+ arg->lifetime_table[i] = NULL;
198
+ }
199
+ }
200
+ }
185
201
  }
186
202
 
187
203
  static struct allocation_info *
@@ -261,7 +277,7 @@ aggregate_each_info(struct traceobj_arg *arg, struct allocation_info *info, size
261
277
  key = (st_data_t)&key_data;
262
278
 
263
279
  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);
280
+ struct memcmp_key_data *key_buff = ruby_xmalloc(sizeof(struct memcmp_key_data));
265
281
  key_buff->n = key_data.n;
266
282
 
267
283
  for (i=0; i<key_data.n; i++) {
@@ -315,6 +331,34 @@ move_to_freed_list(struct traceobj_arg *arg, struct allocation_info *info)
315
331
  arg->freed_allocation_info = info;
316
332
  }
317
333
 
334
+ static void
335
+ add_lifetime_table(size_t **lines, int type, struct allocation_info *info)
336
+ {
337
+ size_t age = rb_gc_count() - info->generation;
338
+ size_t *line = lines[type];
339
+ size_t len, i;
340
+
341
+ if (line == NULL) {
342
+ len = age + 1;
343
+ line = lines[type] = malloc(sizeof(size_t) * (1 + len));
344
+ line[0] = len;
345
+ for (i=0; i<len; i++) line[i+1] = 0;
346
+ }
347
+ else {
348
+ len = line[0];
349
+
350
+ if (len < age + 1) {
351
+ size_t old_len = len;
352
+ len = age + 1;
353
+ line = lines[type] = realloc(line, sizeof(size_t) * (1 + len));
354
+ for (i=old_len; i<len; i++) line[i+1] = 0;
355
+ line[0] = len;
356
+ }
357
+ }
358
+
359
+ line[age + 1]++;
360
+ }
361
+
318
362
  static void
319
363
  freeobj_i(VALUE tpval, void *data)
320
364
  {
@@ -331,6 +375,20 @@ freeobj_i(VALUE tpval, void *data)
331
375
  if (arg->freed_allocation_info == NULL) {
332
376
  rb_postponed_job_register_one(0, aggregate_freed_info, arg);
333
377
  }
378
+
379
+ if (arg->lifetime_table) {
380
+ add_lifetime_table(arg->lifetime_table, BUILTIN_TYPE(obj), info);
381
+ }
382
+ }
383
+ }
384
+
385
+ static void
386
+ check_tracer_running(void)
387
+ {
388
+ struct traceobj_arg * arg = get_traceobj_arg();
389
+
390
+ if (!arg->running) {
391
+ rb_raise(rb_eRuntimeError, "not started yet");
334
392
  }
335
393
  }
336
394
 
@@ -339,6 +397,8 @@ enable_newobj_hook(void)
339
397
  {
340
398
  VALUE newobj_hook;
341
399
 
400
+ check_tracer_running();
401
+
342
402
  if ((newobj_hook = rb_ivar_get(rb_mAllocationTracer, rb_intern("newobj_hook"))) == Qnil) {
343
403
  rb_raise(rb_eRuntimeError, "not started.");
344
404
  }
@@ -354,6 +414,8 @@ disable_newobj_hook(void)
354
414
  {
355
415
  VALUE newobj_hook;
356
416
 
417
+ check_tracer_running();
418
+
357
419
  if ((newobj_hook = rb_ivar_get(rb_mAllocationTracer, rb_intern("newobj_hook"))) == Qnil) {
358
420
  rb_raise(rb_eRuntimeError, "not started.");
359
421
  }
@@ -364,16 +426,6 @@ disable_newobj_hook(void)
364
426
  rb_tracepoint_disable(newobj_hook);
365
427
  }
366
428
 
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");
374
- }
375
- }
376
-
377
429
  static void
378
430
  start_alloc_hooks(VALUE mod)
379
431
  {
@@ -412,39 +464,49 @@ stop_alloc_hooks(VALUE self)
412
464
  return Qnil;
413
465
  }
414
466
 
415
- static const char *
416
- type_name(int type)
417
- {
418
- switch (type) {
419
- #define TYPE_NAME(t) case (t): return #t;
420
- TYPE_NAME(T_NONE);
421
- TYPE_NAME(T_OBJECT);
422
- TYPE_NAME(T_CLASS);
423
- TYPE_NAME(T_MODULE);
424
- TYPE_NAME(T_FLOAT);
425
- TYPE_NAME(T_STRING);
426
- TYPE_NAME(T_REGEXP);
427
- TYPE_NAME(T_ARRAY);
428
- TYPE_NAME(T_HASH);
429
- TYPE_NAME(T_STRUCT);
430
- TYPE_NAME(T_BIGNUM);
431
- TYPE_NAME(T_FILE);
432
- TYPE_NAME(T_MATCH);
433
- TYPE_NAME(T_COMPLEX);
434
- TYPE_NAME(T_RATIONAL);
435
- TYPE_NAME(T_NIL);
436
- TYPE_NAME(T_TRUE);
437
- TYPE_NAME(T_FALSE);
438
- TYPE_NAME(T_SYMBOL);
439
- TYPE_NAME(T_FIXNUM);
440
- TYPE_NAME(T_UNDEF);
441
- TYPE_NAME(T_NODE);
442
- TYPE_NAME(T_ICLASS);
443
- TYPE_NAME(T_ZOMBIE);
444
- TYPE_NAME(T_DATA);
467
+ static VALUE
468
+ type_sym(int type)
469
+ {
470
+ static VALUE syms[T_MASK];
471
+
472
+ if (syms[0] == 0) {
473
+ int i;
474
+ for (i=0; i<T_MASK; i++) {
475
+ switch (i) {
476
+ #define TYPE_NAME(t) case (t): syms[i] = ID2SYM(rb_intern(#t)); break;
477
+ TYPE_NAME(T_NONE);
478
+ TYPE_NAME(T_OBJECT);
479
+ TYPE_NAME(T_CLASS);
480
+ TYPE_NAME(T_MODULE);
481
+ TYPE_NAME(T_FLOAT);
482
+ TYPE_NAME(T_STRING);
483
+ TYPE_NAME(T_REGEXP);
484
+ TYPE_NAME(T_ARRAY);
485
+ TYPE_NAME(T_HASH);
486
+ TYPE_NAME(T_STRUCT);
487
+ TYPE_NAME(T_BIGNUM);
488
+ TYPE_NAME(T_FILE);
489
+ TYPE_NAME(T_MATCH);
490
+ TYPE_NAME(T_COMPLEX);
491
+ TYPE_NAME(T_RATIONAL);
492
+ TYPE_NAME(T_NIL);
493
+ TYPE_NAME(T_TRUE);
494
+ TYPE_NAME(T_FALSE);
495
+ TYPE_NAME(T_SYMBOL);
496
+ TYPE_NAME(T_FIXNUM);
497
+ TYPE_NAME(T_UNDEF);
498
+ TYPE_NAME(T_NODE);
499
+ TYPE_NAME(T_ICLASS);
500
+ TYPE_NAME(T_ZOMBIE);
501
+ TYPE_NAME(T_DATA);
502
+ default:
503
+ syms[i] = ID2SYM(rb_intern("unknown"));
445
504
  #undef TYPE_NAME
505
+ }
506
+ }
446
507
  }
447
- return "unknown";
508
+
509
+ return syms[type];
448
510
  }
449
511
 
450
512
  struct arg_and_result {
@@ -467,14 +529,6 @@ aggregate_result_i(st_data_t key, st_data_t val, void *data)
467
529
  INT2FIX(val_buff[4]), INT2FIX(val_buff[5]));
468
530
  VALUE k = rb_ary_new();
469
531
  int i = 0;
470
- static VALUE type_symbols[T_MASK] = {0};
471
-
472
- if (type_symbols[0] == 0) {
473
- int i;
474
- for (i=0; i<T_MASK; i++) {
475
- type_symbols[i] = ID2SYM(rb_intern(type_name(i)));
476
- }
477
- }
478
532
 
479
533
  i = 0;
480
534
  if (arg->keys & KEY_PATH) {
@@ -493,7 +547,7 @@ aggregate_result_i(st_data_t key, st_data_t val, void *data)
493
547
  if (arg->keys & KEY_TYPE) {
494
548
  int sym_index = key_buff->data[i++];
495
549
  assert(T_MASK > sym_index);
496
- rb_ary_push(k, type_symbols[sym_index]);
550
+ rb_ary_push(k, type_sym(sym_index));
497
551
  }
498
552
  if (arg->keys & KEY_CLASS) {
499
553
  VALUE klass = key_buff->data[i++];
@@ -532,6 +586,32 @@ aggregate_live_object_i(st_data_t key, st_data_t val, void *data)
532
586
  return ST_CONTINUE;
533
587
  }
534
588
 
589
+ static int
590
+ lifetime_table_for_live_objects_i(st_data_t key, st_data_t val, st_data_t data)
591
+ {
592
+ struct allocation_info *info = (struct allocation_info *)val;
593
+ VALUE h = (VALUE)data;
594
+ int type = info->flags & T_MASK;
595
+ VALUE sym = type_sym(type);
596
+ size_t age = rb_gc_count() - info->generation;
597
+ VALUE line;
598
+ size_t count, i;
599
+
600
+ if ((line = rb_hash_aref(h, sym)) == Qnil) {
601
+ line = rb_ary_new();
602
+ rb_hash_aset(h, sym, line);
603
+ }
604
+
605
+ for (i=RARRAY_LEN(line); i<age+1; i++) {
606
+ rb_ary_push(line, INT2FIX(0));
607
+ }
608
+
609
+ count = NUM2SIZET(RARRAY_AREF(line, age));
610
+ RARRAY_ASET(line, age, SIZET2NUM(count + 1));
611
+
612
+ return ST_CONTINUE;
613
+ }
614
+
535
615
  static VALUE
536
616
  aggregate_result(struct traceobj_arg *arg)
537
617
  {
@@ -560,15 +640,33 @@ aggregate_result(struct traceobj_arg *arg)
560
640
 
561
641
  arg->aggregate_table = dead_object_aggregate_table;
562
642
  }
563
- return aar.result;
564
- }
565
643
 
566
- static VALUE
567
- allocation_tracer_stop(VALUE self)
568
- {
569
- VALUE result = aggregate_result(get_traceobj_arg());
570
- stop_alloc_hooks(self);
571
- return result;
644
+ /* lifetime table */
645
+ if (arg->lifetime_table) {
646
+ VALUE h = rb_hash_new();
647
+ int i;
648
+
649
+ for (i=0; i<T_MASK; i++) {
650
+ size_t *line = arg->lifetime_table[i];
651
+
652
+ if (line) {
653
+ size_t len = line[0], j;
654
+ VALUE ary = rb_ary_new();
655
+ VALUE sym = type_sym(i);
656
+
657
+ for (j=0; j<len; j++) {
658
+ rb_ary_push(ary, SIZET2NUM(line[j+1]));
659
+ }
660
+
661
+ rb_hash_aset(h, sym, ary);
662
+ }
663
+ }
664
+
665
+ st_foreach(arg->object_table, lifetime_table_for_live_objects_i, (st_data_t)h);
666
+ rb_ivar_set(rb_mAllocationTracer, rb_intern("lifetime_table"), h);
667
+ }
668
+
669
+ return aar.result;
572
670
  }
573
671
 
574
672
  static VALUE
@@ -577,8 +675,6 @@ allocation_tracer_result(VALUE self)
577
675
  VALUE result;
578
676
  struct traceobj_arg *arg = get_traceobj_arg();
579
677
 
580
- check_tracer_running();
581
-
582
678
  disable_newobj_hook();
583
679
  result = aggregate_result(arg);
584
680
  enable_newobj_hook();
@@ -620,10 +716,20 @@ allocation_tracer_trace(VALUE self)
620
716
  return Qnil;
621
717
  }
622
718
 
719
+ static VALUE
720
+ allocation_tracer_stop(VALUE self)
721
+ {
722
+ VALUE result;
723
+
724
+ disable_newobj_hook();
725
+ result = aggregate_result(get_traceobj_arg());
726
+ stop_alloc_hooks(self);
727
+ return result;
728
+ }
729
+
623
730
  static VALUE
624
731
  allocation_tracer_pause(VALUE self)
625
732
  {
626
- check_tracer_running();
627
733
  disable_newobj_hook();
628
734
  return Qnil;
629
735
  }
@@ -631,7 +737,6 @@ allocation_tracer_pause(VALUE self)
631
737
  static VALUE
632
738
  allocation_tracer_resume(VALUE self)
633
739
  {
634
- check_tracer_running();
635
740
  enable_newobj_hook();
636
741
  return Qnil;
637
742
  }
@@ -689,6 +794,39 @@ allocation_tracer_header(VALUE self)
689
794
  return ary;
690
795
  }
691
796
 
797
+ static VALUE
798
+ allocation_tracer_lifetime_table_setup(VALUE self, VALUE set)
799
+ {
800
+ struct traceobj_arg * arg = get_traceobj_arg();
801
+
802
+ if (arg->running) {
803
+ rb_raise(rb_eRuntimeError, "can't change configuration during running");
804
+ }
805
+
806
+ if (RTEST(set)) {
807
+ if (arg->lifetime_table == NULL) {
808
+ arg->lifetime_table = (size_t **)calloc(sizeof(size_t **), T_MASK);
809
+
810
+ }
811
+ }
812
+ else {
813
+ if (arg->lifetime_table != NULL) {
814
+ free(arg->lifetime_table);
815
+ arg->lifetime_table = NULL;
816
+ }
817
+ }
818
+
819
+ return Qnil;
820
+ }
821
+
822
+ static VALUE
823
+ allocation_tracer_lifetime_table(VALUE self)
824
+ {
825
+ VALUE result = rb_ivar_get(rb_mAllocationTracer, rb_intern("lifetime_table"));
826
+ rb_ivar_set(rb_mAllocationTracer, rb_intern("lifetime_table"), Qnil);
827
+ return result;
828
+ }
829
+
692
830
  void
693
831
  Init_allocation_tracer(void)
694
832
  {
@@ -706,4 +844,7 @@ Init_allocation_tracer(void)
706
844
  rb_define_module_function(mod, "clear", allocation_tracer_clear, 0);
707
845
  rb_define_module_function(mod, "setup", allocation_tracer_setup, -1);
708
846
  rb_define_module_function(mod, "header", allocation_tracer_header, 0);
847
+
848
+ rb_define_module_function(mod, "lifetime_table_setup", allocation_tracer_lifetime_table_setup, 1);
849
+ rb_define_module_function(mod, "lifetime_table", allocation_tracer_lifetime_table, 0);
709
850
  }
@@ -1,3 +1,3 @@
1
1
  module ObjectSpace::AllocationTracer
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -164,4 +164,43 @@ describe ObjectSpace::AllocationTracer do
164
164
  end
165
165
  end
166
166
  end
167
+
168
+ describe 'collect lifetime_table' do
169
+ before do
170
+ ObjectSpace::AllocationTracer.lifetime_table_setup true
171
+ end
172
+
173
+ after do
174
+ ObjectSpace::AllocationTracer.lifetime_table_setup false
175
+ end
176
+
177
+ it 'should make lifetime table' do
178
+ ObjectSpace::AllocationTracer.trace do
179
+ 100000.times{
180
+ Object.new
181
+ ''
182
+ }
183
+ end
184
+ table = ObjectSpace::AllocationTracer.lifetime_table
185
+
186
+ expect(table[:T_OBJECT].inject(&:+)).to be >= 10_000
187
+ expect(table[:T_STRING].inject(&:+)).to be >= 10_000
188
+ expect(table[:T_NONE]).to be nil
189
+ end
190
+
191
+ it 'should nil when ObjectSpace::AllocationTracer.lifetime_table_setup is nil' do
192
+ ObjectSpace::AllocationTracer.lifetime_table_setup false
193
+
194
+ ObjectSpace::AllocationTracer.trace do
195
+ 100000.times{
196
+ Object.new
197
+ ''
198
+ }
199
+ end
200
+
201
+ table = ObjectSpace::AllocationTracer.lifetime_table
202
+
203
+ expect(table).to be nil
204
+ end
205
+ end
167
206
  end
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.2.0
4
+ version: 0.3.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-21 00:00:00.000000000 Z
11
+ date: 2014-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -84,7 +84,6 @@ files:
84
84
  - ext/allocation_tracer/allocation_tracer.c
85
85
  - ext/allocation_tracer/extconf.rb
86
86
  - lib/allocation_tracer.rb
87
- - lib/allocation_tracer/allocation_tracer.so
88
87
  - lib/allocation_tracer/trace.rb
89
88
  - lib/allocation_tracer/version.rb
90
89
  - spec/allocation_tracer_spec.rb