gc_tracer 0.1.2 → 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: 7ad7a07f89e6474de38728bba4e183173133c727
4
- data.tar.gz: 1be0ad94f2746c7fee8880725827a386cde7ef75
3
+ metadata.gz: 830cc27b4a444096a36db13d0425b920cd87c8e4
4
+ data.tar.gz: a331e1b13ea0f3c61af86c166c1592cb21fb67cd
5
5
  SHA512:
6
- metadata.gz: b3410157161b046b7870da5caae27c0be60968c825f38908a18907e7ee8941effa28240e32f05ce769a5ad459064ec9784b0ec42746c989d486b2b2a65805c45
7
- data.tar.gz: a168bf9cd168fbc5d4c80eacfb44d4b7dcc52053f5ba89bf821bb7a372dd874e9b06ed65073c21a02eda04b34321d6ee484a06513d5d42dac8b4cb95b053200c
6
+ metadata.gz: 61150fdea8743a53a2f2d7954d2cb969e75ff46b23ddb7bbb50afaccb2885632067533b10c5bac91b2fe393d8cd739b59eb3de16b521ed4869cdb6c18dfcdbd0
7
+ data.tar.gz: 66bcc1e1a75d05caf7dc54a655a4ebc657db34fb9fa6410dbef5faebeb0783248851cbf9ea8ae1936a2348d95c0232ceddd22979494516d9268e80818a46287b
data/README.md CHANGED
@@ -20,6 +20,12 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
+ gc_tracer gem adds GC::Tracer module. GC::Tracer module has the following features.
24
+
25
+ - Logging GC statistics information
26
+ - Allocation tracing information
27
+ - ObjectSpace recorder
28
+
23
29
  ### Logging
24
30
 
25
31
  You can get GC statistics information in block form like this:
@@ -54,6 +60,69 @@ at each events, there are one of:
54
60
 
55
61
  For one GC, you can get all three lines.
56
62
 
63
+ ### Allocation tracing
64
+
65
+ You can trace allocation information and you can get aggregated information.
66
+
67
+ ```ruby
68
+ require 'gc_tracer'
69
+ require 'pp'
70
+
71
+ pp GC::Tracer.start_allocation_tracing{
72
+ 50_000.times{|i|
73
+ i.to_s
74
+ i.to_s
75
+ i.to_s
76
+ }
77
+ }
78
+ ```
79
+
80
+ will show
81
+
82
+ ```
83
+ {["test.rb", 6]=>[50000, 44290, 0, 6],
84
+ ["test.rb", 7]=>[50000, 44289, 0, 5],
85
+ ["test.rb", 8]=>[50000, 44295, 0, 6]}
86
+ ```
87
+
88
+ In this case, 50,000 objects are created at `test.rb:6'. 44,290 is total
89
+ age of objects created at this line. Average age of object created at
90
+ this line is 50000/44290 = 0.8858. 0 is minimum age and 6 is maximum age.
91
+
92
+ Simply you can require `gc_tracer/allocation_trace' to start allocation
93
+ tracer and output the aggregated information into stdot at the end of
94
+ program.
95
+
96
+ ```ruby
97
+ require 'gc_tracer/allocation_trace'
98
+
99
+ # Run your program here
100
+ 50_000.times{|i|
101
+ i.to_s
102
+ i.to_s
103
+ i.to_s
104
+ }
105
+ ```
106
+
107
+ and you will see:
108
+
109
+ ```
110
+ file line count total_age max_age min_age
111
+ .../lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb 55 18 23 1 6
112
+ .../gc_tracer/lib/gc_tracer/allocation_trace.rb 5 2 12 6 6
113
+ .../gc_tracer/lib/gc_tracer/allocation_trace.rb 6 2 0 0 0
114
+ test.rb 0 1 0 0 0
115
+ test.rb 5 50000 41574 0 5
116
+ test.rb 6 50000 41566 0 4
117
+ test.rb 7 50000 41574 0 5
118
+ ```
119
+
120
+ (tab separated colums)
121
+
122
+ This feature is similar to https://github.com/SamSaffron/memory_profiler
123
+ and https://github.com/srawlins/allocation_stats. But this feature
124
+ focused on `age' of objects.
125
+
57
126
  ### ObjectSpace recorder
58
127
 
59
128
  You can records objspace snapshots on each events. Snapshots are stored
@@ -77,7 +146,7 @@ You can view all converted png images with "dirname/viewer.html" file in animati
77
146
 
78
147
  This feature is supported only latest Ruby versions (2.2, and later).
79
148
 
80
- #### Examaple
149
+ #### Example
81
150
 
82
151
  ```ruby
83
152
  require 'gc_tracer'
@@ -0,0 +1,388 @@
1
+ /*
2
+ * allocation tracer: adds GC::Tracer::start_allocation_tracing
3
+ *
4
+ * By Koichi Sasada
5
+ * created at Thu Apr 17 03:50:38 2014.
6
+ */
7
+
8
+ #include "ruby/ruby.h"
9
+ #include "ruby/debug.h"
10
+
11
+ struct allocation_info {
12
+ /* all of information don't need marking. */
13
+ int living;
14
+ VALUE flags;
15
+ VALUE klass;
16
+
17
+ /* allocation info */
18
+ const char *path;
19
+ unsigned long line;
20
+ const char *class_path;
21
+ VALUE mid;
22
+ size_t generation;
23
+
24
+ struct allocation_info *next;
25
+ };
26
+
27
+ struct traceobj_arg {
28
+ int running;
29
+ st_table *aggregate_table; /* user defined key -> [count, total_age, max_age, min_age] */
30
+ st_table *object_table; /* obj (VALUE) -> allocation_info */
31
+ st_table *str_table; /* cstr -> refcount */
32
+ struct allocation_info *freed_allocation_info;
33
+ };
34
+
35
+ extern VALUE rb_mGCTracer;
36
+
37
+ static char *
38
+ keep_unique_str(st_table *tbl, const char *str)
39
+ {
40
+ st_data_t n;
41
+
42
+ if (st_lookup(tbl, (st_data_t)str, &n)) {
43
+ char *result;
44
+
45
+ st_insert(tbl, (st_data_t)str, n+1);
46
+ st_get_key(tbl, (st_data_t)str, (st_data_t *)&result);
47
+
48
+ return result;
49
+ }
50
+ else {
51
+ return NULL;
52
+ }
53
+ }
54
+
55
+ static const char *
56
+ make_unique_str(st_table *tbl, const char *str, long len)
57
+ {
58
+ if (!str) {
59
+ return NULL;
60
+ }
61
+ else {
62
+ char *result;
63
+
64
+ if ((result = keep_unique_str(tbl, str)) == NULL) {
65
+ result = (char *)ruby_xmalloc(len+1);
66
+ strncpy(result, str, len);
67
+ result[len] = 0;
68
+ st_add_direct(tbl, (st_data_t)result, 1);
69
+ }
70
+ return result;
71
+ }
72
+ }
73
+
74
+ static void
75
+ delete_unique_str(st_table *tbl, const char *str)
76
+ {
77
+ if (str) {
78
+ st_data_t n;
79
+
80
+ st_lookup(tbl, (st_data_t)str, &n);
81
+ if (n == 1) {
82
+ st_delete(tbl, (st_data_t *)&str, 0);
83
+ ruby_xfree((char *)str);
84
+ }
85
+ else {
86
+ st_insert(tbl, (st_data_t)str, n-1);
87
+ }
88
+ }
89
+ }
90
+
91
+ struct memcmp_key_data {
92
+ const char *path;
93
+ int line;
94
+ };
95
+
96
+ static int
97
+ memcmp_hash_compare(st_data_t a, st_data_t b)
98
+ {
99
+ struct memcmp_key_data *k1 = (struct memcmp_key_data *)a;
100
+ struct memcmp_key_data *k2 = (struct memcmp_key_data *)b;
101
+
102
+ return (k1->path == k2->path && k1->line == k2->line) ? 0 : 1;
103
+ }
104
+
105
+ static st_index_t
106
+ memcmp_hash_hash(st_data_t a)
107
+ {
108
+ struct memcmp_key_data *k1 = (struct memcmp_key_data *)a;
109
+ return (((st_index_t)k1->path) << 8) & (st_index_t)k1->line;
110
+ }
111
+
112
+ static const struct st_hash_type memcmp_hash_type = {
113
+ memcmp_hash_compare, memcmp_hash_hash
114
+ };
115
+
116
+ static struct traceobj_arg *tmp_trace_arg; /* TODO: Do not use global variables */
117
+
118
+ static struct traceobj_arg *
119
+ get_traceobj_arg(void)
120
+ {
121
+ if (tmp_trace_arg == 0) {
122
+ tmp_trace_arg = ALLOC_N(struct traceobj_arg, 1);
123
+ tmp_trace_arg->aggregate_table = st_init_table(&memcmp_hash_type);
124
+ tmp_trace_arg->object_table = st_init_numtable();
125
+ tmp_trace_arg->str_table = st_init_strtable();
126
+ tmp_trace_arg->freed_allocation_info = NULL;
127
+ }
128
+ return tmp_trace_arg;
129
+ }
130
+
131
+ static int
132
+ free_keys_i(st_data_t key, st_data_t value, void *data)
133
+ {
134
+ ruby_xfree((void *)key);
135
+ return ST_CONTINUE;
136
+ }
137
+
138
+ static int
139
+ free_values_i(st_data_t key, st_data_t value, void *data)
140
+ {
141
+ ruby_xfree((void *)value);
142
+ return ST_CONTINUE;
143
+ }
144
+
145
+ static int
146
+ free_key_values_i(st_data_t key, st_data_t value, void *data)
147
+ {
148
+ ruby_xfree((void *)key);
149
+ ruby_xfree((void *)value);
150
+ return ST_CONTINUE;
151
+ }
152
+
153
+ static void
154
+ clear_traceobj_arg(void)
155
+ {
156
+ struct traceobj_arg * arg = get_traceobj_arg();
157
+
158
+ st_foreach(arg->aggregate_table, free_key_values_i, 0);
159
+ st_clear(arg->aggregate_table);
160
+ st_foreach(arg->object_table, free_values_i, 0);
161
+ st_clear(arg->object_table);
162
+ st_foreach(arg->str_table, free_keys_i, 0);
163
+ st_clear(arg->str_table);
164
+ }
165
+
166
+ static struct allocation_info *
167
+ create_allocation_info(void)
168
+ {
169
+ return (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
170
+ }
171
+
172
+ static void
173
+ free_allocation_info(struct traceobj_arg *arg, struct allocation_info *info)
174
+ {
175
+ delete_unique_str(arg->str_table, info->path);
176
+ delete_unique_str(arg->str_table, info->class_path);
177
+ ruby_xfree(info);
178
+ }
179
+
180
+ static void
181
+ newobj_i(VALUE tpval, void *data)
182
+ {
183
+ struct traceobj_arg *arg = (struct traceobj_arg *)data;
184
+ rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
185
+ VALUE obj = rb_tracearg_object(tparg);
186
+ VALUE path = rb_tracearg_path(tparg);
187
+ 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;
194
+
195
+ if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
196
+ if (info->living) {
197
+ /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */
198
+ }
199
+ /* reuse info */
200
+ delete_unique_str(arg->str_table, info->path);
201
+ delete_unique_str(arg->str_table, info->class_path);
202
+ }
203
+ else {
204
+ info = create_allocation_info();
205
+ }
206
+
207
+ info->next = NULL;
208
+ info->living = 1;
209
+ info->flags = RBASIC(obj)->flags;
210
+ info->klass = RBASIC_CLASS(obj);
211
+
212
+ info->path = path_cstr;
213
+ info->line = NUM2INT(line);
214
+ info->mid = mid;
215
+ info->class_path = class_path_cstr;
216
+ info->generation = rb_gc_count();
217
+ st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
218
+ }
219
+
220
+ #define MAX_KEY_SIZE 4
221
+
222
+ void
223
+ aggregator_i(void *data)
224
+ {
225
+ size_t gc_count = rb_gc_count();
226
+ struct traceobj_arg *arg = (struct traceobj_arg *)data;
227
+ struct allocation_info *info = arg->freed_allocation_info;
228
+
229
+ arg->freed_allocation_info = NULL;
230
+
231
+ while (info) {
232
+ struct allocation_info *next_info = info->next;
233
+ st_data_t key, val;
234
+ struct memcmp_key_data key_data;
235
+ int *val_buff;
236
+ int age = (int)(gc_count - info->generation);
237
+
238
+ key_data.path = info->path;
239
+ key_data.line = info->line;
240
+ key = (st_data_t)&key_data;
241
+ keep_unique_str(arg->str_table, info->path);
242
+
243
+ 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;
246
+ key = (st_data_t)key_buff;
247
+
248
+ /* count, total age, max age, min age */
249
+ val_buff = ALLOC_N(int, 4);
250
+ val_buff[0] = val_buff[1] = 0;
251
+ val_buff[2] = val_buff[3] = age;
252
+
253
+ st_insert(arg->aggregate_table, (st_data_t)key_buff, (st_data_t)val_buff);
254
+ }
255
+ else {
256
+ val_buff = (int *)val;
257
+ }
258
+
259
+ val_buff[0] += 1;
260
+ val_buff[1] += age;
261
+ if (val_buff[2] > age) val_buff[2] = age;
262
+ if (val_buff[3] < age) val_buff[3] = age;
263
+
264
+ free_allocation_info(arg, info);
265
+ info = next_info;
266
+ }
267
+ }
268
+
269
+ static void
270
+ move_to_freed_list(struct traceobj_arg *arg, VALUE obj, struct allocation_info *info)
271
+ {
272
+ info->next = arg->freed_allocation_info;
273
+ arg->freed_allocation_info = info;
274
+ st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
275
+ }
276
+
277
+ static void
278
+ freeobj_i(VALUE tpval, void *data)
279
+ {
280
+ struct traceobj_arg *arg = (struct traceobj_arg *)data;
281
+ rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
282
+ VALUE obj = rb_tracearg_object(tparg);
283
+ struct allocation_info *info;
284
+
285
+ if (arg->freed_allocation_info == NULL) {
286
+ rb_postponed_job_register_one(0, aggregator_i, arg);
287
+ }
288
+
289
+ if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
290
+ move_to_freed_list(arg, obj, info);
291
+ }
292
+ }
293
+
294
+ static void
295
+ start_alloc_hooks(VALUE mod)
296
+ {
297
+ VALUE newobj_hook, freeobj_hook;
298
+ struct traceobj_arg *arg = get_traceobj_arg();
299
+
300
+ if ((newobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("newobj_hook"))) == Qnil) {
301
+ rb_ivar_set(rb_mGCTracer, rb_intern("newobj_hook"), newobj_hook = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg));
302
+ rb_ivar_set(rb_mGCTracer, rb_intern("freeobj_hook"), freeobj_hook = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg));
303
+ }
304
+ else {
305
+ freeobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("freeobj_hook"));
306
+ }
307
+
308
+ rb_tracepoint_enable(newobj_hook);
309
+ rb_tracepoint_enable(freeobj_hook);
310
+ }
311
+
312
+ static int
313
+ aggregate_result_i(st_data_t key, st_data_t val, void *data)
314
+ {
315
+ VALUE result = (VALUE)data;
316
+ int *val_buff = (int *)val;
317
+ 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
+ VALUE v = rb_ary_new3(4, INT2FIX(val_buff[0]), INT2FIX(val_buff[1]), INT2FIX(val_buff[2]), INT2FIX(val_buff[3]));
320
+
321
+ rb_hash_aset(result, k, v);
322
+
323
+ return ST_CONTINUE;
324
+ }
325
+
326
+ static int
327
+ aggregate_rest_object_i(st_data_t key, st_data_t val, void *data)
328
+ {
329
+ struct traceobj_arg *arg = (struct traceobj_arg *)data;
330
+ struct allocation_info *info = (struct allocation_info *)val;
331
+ move_to_freed_list(arg, (VALUE)key, info);
332
+ return ST_CONTINUE;
333
+ }
334
+
335
+ static VALUE
336
+ aggregate_result(struct traceobj_arg *arg)
337
+ {
338
+ VALUE result = rb_hash_new();
339
+ st_foreach(arg->object_table, aggregate_rest_object_i, (st_data_t)arg);
340
+ aggregator_i(arg);
341
+ st_foreach(arg->aggregate_table, aggregate_result_i, (st_data_t)result);
342
+ clear_traceobj_arg();
343
+ return result;
344
+ }
345
+
346
+ static VALUE
347
+ stop_allocation_tracing(VALUE self)
348
+ {
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"));
351
+
352
+ /* stop hooks */
353
+ if (newobj_hook && freeobj_hook) {
354
+ rb_tracepoint_disable(newobj_hook);
355
+ rb_tracepoint_disable(freeobj_hook);
356
+ }
357
+ else {
358
+ rb_raise(rb_eRuntimeError, "not started yet.");
359
+ }
360
+
361
+ return Qnil;
362
+ }
363
+
364
+ VALUE
365
+ gc_tracer_stop_allocation_tracing(VALUE self)
366
+ {
367
+ stop_allocation_tracing(self);
368
+ return aggregate_result(get_traceobj_arg());
369
+ }
370
+
371
+ VALUE
372
+ gc_tracer_start_allocation_tracing(int argc, VALUE *argv, VALUE self)
373
+ {
374
+ if (rb_ivar_get(rb_mGCTracer, rb_intern("allocation_tracer")) != Qnil) {
375
+ rb_raise(rb_eRuntimeError, "can't run recursive");
376
+ }
377
+ else {
378
+ start_alloc_hooks(rb_mGCTracer);
379
+
380
+ if (rb_block_given_p()) {
381
+ rb_ensure(rb_yield, Qnil, stop_allocation_tracing, Qnil);
382
+ return aggregate_result(get_traceobj_arg());
383
+ }
384
+ }
385
+
386
+ return Qnil;
387
+ }
388
+
@@ -781,6 +781,10 @@ 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);
785
+ VALUE gc_tracer_stop_allocation_tracing(VALUE self);
786
+ VALUE rb_mGCTracer;
787
+
784
788
  /**
785
789
  * GC::Tracer traces GC/ObjectSpace behavior.
786
790
  *
@@ -801,7 +805,7 @@ gc_tracer_start_objspace_recording(int argc, VALUE *argv, VALUE self)
801
805
  void
802
806
  Init_gc_tracer(void)
803
807
  {
804
- VALUE mod = rb_define_module_under(rb_mGC, "Tracer");
808
+ VALUE mod = rb_mGCTracer = rb_define_module_under(rb_mGC, "Tracer");
805
809
 
806
810
  /* logging methods */
807
811
  rb_define_module_function(mod, "start_logging", gc_tracer_start_logging, -1);
@@ -814,6 +818,10 @@ Init_gc_tracer(void)
814
818
  rb_define_module_function(mod, "stop_objspace_recording", gc_tracer_stop_objspace_recording, 0);
815
819
  #endif
816
820
 
821
+ /* allocation tracer methods */
822
+ rb_define_module_function(mod, "start_allocation_tracing", gc_tracer_start_allocation_tracing, -1);
823
+ rb_define_module_function(mod, "stop_allocation_tracing", gc_tracer_stop_allocation_tracing, 0);
824
+
817
825
  /* setup default banners */
818
826
  setup_gc_trace_symbols();
819
827
  start_tick = tick();
@@ -0,0 +1,10 @@
1
+ require 'gc_tracer'
2
+
3
+ GC::Tracer.start_allocation_tracing
4
+
5
+ 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|
8
+ puts (k+v).join("\t")
9
+ }
10
+ }
@@ -1,3 +1,3 @@
1
1
  module GC::Tracer
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -29,20 +29,20 @@ describe GC::Tracer do
29
29
  end
30
30
 
31
31
  shared_examples "objspace_recorder_test" do
32
- DIRNAME = "gc_tracer_objspace_recorder_spec.#{$$}"
32
+ dirname = "gc_tracer_objspace_recorder_spec.#{$$}"
33
33
 
34
34
  it do
35
35
  begin
36
- GC::Tracer.start_objspace_recording(DIRNAME){
36
+ GC::Tracer.start_objspace_recording(dirname){
37
37
  count.times{
38
38
  GC.start
39
39
  }
40
40
  }
41
- expect(Dir.glob("#{DIRNAME}/ppm/*.ppm").size).to be >= count * 3
41
+ expect(Dir.glob("#{dirname}/ppm/*.ppm").size).to be >= count * 3
42
42
  rescue NoMethodError
43
43
  pending "start_objspace_recording requires MRI >= 2.2"
44
44
  ensure
45
- FileUtils.rm_rf(DIRNAME) if File.directory?(DIRNAME)
45
+ FileUtils.rm_rf(dirname) if File.directory?(dirname)
46
46
  end
47
47
  end
48
48
  end
@@ -56,4 +56,16 @@ describe GC::Tracer do
56
56
  let(:count){2}
57
57
  it_behaves_like "objspace_recorder_test"
58
58
  end
59
+
60
+ describe 'GC::Tracer.start_allocation_tracing' do
61
+ it 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
+ end
59
71
  end
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.1.2
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-06 00:00:00.000000000 Z
11
+ date: 2014-04-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -81,10 +81,12 @@ files:
81
81
  - README.md
82
82
  - Rakefile
83
83
  - bin/objspace_recorder_convert.rb
84
+ - ext/gc_tracer/allocation_tracer.c
84
85
  - ext/gc_tracer/extconf.rb
85
86
  - ext/gc_tracer/gc_tracer.c
86
87
  - gc_tracer.gemspec
87
88
  - lib/gc_tracer.rb
89
+ - lib/gc_tracer/allocation_trace.rb
88
90
  - lib/gc_tracer/version.rb
89
91
  - lib/gc_tracer/viewer.html.erb
90
92
  - public/jquery-2.1.0.min.js