allocation_tracer 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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