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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 830cc27b4a444096a36db13d0425b920cd87c8e4
4
- data.tar.gz: a331e1b13ea0f3c61af86c166c1592cb21fb67cd
3
+ metadata.gz: fe62a5d40bec9e63a7f686a912ad5b7025de0baa
4
+ data.tar.gz: 8103c971df3779bb9d2aa3a57b98208540be98ef
5
5
  SHA512:
6
- metadata.gz: 61150fdea8743a53a2f2d7954d2cb969e75ff46b23ddb7bbb50afaccb2885632067533b10c5bac91b2fe393d8cd739b59eb3de16b521ed4869cdb6c18dfcdbd0
7
- data.tar.gz: 66bcc1e1a75d05caf7dc54a655a4ebc657db34fb9fa6410dbef5faebeb0783248851cbf9ea8ae1936a2348d95c0232ceddd22979494516d9268e80818a46287b
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
- const char *path;
93
- int line;
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 *k1 = (struct memcmp_key_data *)a;
109
- return (((st_index_t)k1->path) << 8) & (st_index_t)k1->line;
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->class_path);
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 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;
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->class_path);
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 = RBASIC_CLASS(obj);
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
- info->mid = mid;
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
- key_data.path = info->path;
239
- key_data.line = info->line;
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 = ALLOC_N(struct memcmp_key_data, 1);
245
- *key_buff = key_data;
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
- VALUE result = (VALUE)data;
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
- VALUE result = rb_hash_new();
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)result);
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
- 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"));
466
+ struct traceobj_arg * arg = get_traceobj_arg();
351
467
 
352
- /* stop hooks */
353
- if (newobj_hook && freeobj_hook) {
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(int argc, VALUE *argv, VALUE self)
491
+ gc_tracer_start_allocation_tracing(VALUE self)
373
492
  {
374
- if (rb_ivar_get(rb_mGCTracer, rb_intern("allocation_tracer")) != Qnil) {
375
- rb_raise(rb_eRuntimeError, "can't run recursive");
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
+
@@ -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(int argc, VALUE *argv, VALUE self);
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, -1);
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
- puts "file\tline\tcount\ttotal_age\tmax_age\tmin_age"
7
- GC::Tracer.stop_allocation_tracing.sort_by{|k, v| k}.each{|k, v|
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
  }
@@ -1,3 +1,3 @@
1
1
  module GC::Tracer
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -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.0
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-16 00:00:00.000000000 Z
11
+ date: 2014-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler