gc_tracer 0.2.1 → 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: fe62a5d40bec9e63a7f686a912ad5b7025de0baa
4
- data.tar.gz: 8103c971df3779bb9d2aa3a57b98208540be98ef
3
+ metadata.gz: f02605c54bfd6ac29179037b21d3af38c7c8e0bd
4
+ data.tar.gz: a7a328f0b154d4952bf7ca57f669303d53b341a3
5
5
  SHA512:
6
- metadata.gz: 5901c6798b9f33b90e584db043ad102feba487c4206bc1b67b9d7d6570353bb08cf39980130bb767ec89fa22debb3c5ed9543ef531db2a8137bf7ee6e9a128b1
7
- data.tar.gz: 5ce7a8796a30b4e3f05284a7108043ea942b52878904ec6925cf5def9ad1f4dc114cfee9dec0466f39fb7f5df0f5f82da34123ea511c404d33f4bc785e1e124c
6
+ metadata.gz: 7e8bef62c7648c00b1a25dcfd0370ad53f8ee1b6dd9f210535f723982a60fcb05e8a75cfaaedd74f40c6d8d449248f1abce5a0ab2cb4e1ee07b1dfffa1264f9c
7
+ data.tar.gz: 76228e7ed79f5e94f45a732e0b7ca8fb26f69bcf36bac5dd058a78561cdcf883e4f9474c749b9efe31582e09a10fbbd108b99513a984cf373bd205ac4d6b2557
data/README.md CHANGED
@@ -23,7 +23,6 @@ Or install it yourself as:
23
23
  gc_tracer gem adds GC::Tracer module. GC::Tracer module has the following features.
24
24
 
25
25
  - Logging GC statistics information
26
- - Allocation tracing information
27
26
  - ObjectSpace recorder
28
27
 
29
28
  ### Logging
@@ -60,127 +59,6 @@ at each events, there are one of:
60
59
 
61
60
  For one GC, you can get all three lines.
62
61
 
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
- 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
-
150
- Simply you can require `gc_tracer/allocation_trace' to start allocation
151
- tracer and output the aggregated information into stdot at the end of
152
- program.
153
-
154
- ```ruby
155
- require 'gc_tracer/allocation_trace'
156
-
157
- # Run your program here
158
- 50_000.times{|i|
159
- i.to_s
160
- i.to_s
161
- i.to_s
162
- }
163
- ```
164
-
165
- and you will see:
166
-
167
- ```
168
- file line count total_age max_age min_age
169
- .../lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb 55 18 23 1 6
170
- .../gc_tracer/lib/gc_tracer/allocation_trace.rb 5 2 12 6 6
171
- .../gc_tracer/lib/gc_tracer/allocation_trace.rb 6 2 0 0 0
172
- test.rb 0 1 0 0 0
173
- test.rb 5 50000 41574 0 5
174
- test.rb 6 50000 41566 0 4
175
- test.rb 7 50000 41574 0 5
176
- ```
177
-
178
- (tab separated colums)
179
-
180
- This feature is similar to https://github.com/SamSaffron/memory_profiler
181
- and https://github.com/srawlins/allocation_stats. But this feature
182
- focused on `age' of objects.
183
-
184
62
  ### ObjectSpace recorder
185
63
 
186
64
  You can records objspace snapshots on each events. Snapshots are stored
@@ -781,13 +781,6 @@ 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(VALUE self);
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
-
789
- VALUE rb_mGCTracer;
790
-
791
784
  /**
792
785
  * GC::Tracer traces GC/ObjectSpace behavior.
793
786
  *
@@ -808,7 +801,7 @@ VALUE rb_mGCTracer;
808
801
  void
809
802
  Init_gc_tracer(void)
810
803
  {
811
- VALUE mod = rb_mGCTracer = rb_define_module_under(rb_mGC, "Tracer");
804
+ VALUE mod = rb_define_module_under(rb_mGC, "Tracer");
812
805
 
813
806
  /* logging methods */
814
807
  rb_define_module_function(mod, "start_logging", gc_tracer_start_logging, -1);
@@ -821,12 +814,6 @@ Init_gc_tracer(void)
821
814
  rb_define_module_function(mod, "stop_objspace_recording", gc_tracer_stop_objspace_recording, 0);
822
815
  #endif
823
816
 
824
- /* allocation tracer methods */
825
- rb_define_module_function(mod, "start_allocation_tracing", gc_tracer_start_allocation_tracing, 0);
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);
829
-
830
817
  /* setup default banners */
831
818
  setup_gc_trace_symbols();
832
819
  start_tick = tick();
@@ -1,3 +1,3 @@
1
1
  module GC::Tracer
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -56,26 +56,4 @@ 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 '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
72
- line = __LINE__ + 2
73
- result = GC::Tracer.start_allocation_tracing do
74
- Object.new
75
- end
76
-
77
- expect(result.length).to be >= 1
78
- expect(result[[__FILE__, line]]).to eq [1, 0, 0, 0]
79
- end
80
- end
81
59
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gc_tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Sasada
@@ -81,7 +81,6 @@ files:
81
81
  - README.md
82
82
  - Rakefile
83
83
  - bin/objspace_recorder_convert.rb
84
- - ext/gc_tracer/allocation_tracer.c
85
84
  - ext/gc_tracer/extconf.rb
86
85
  - ext/gc_tracer/gc_tracer.c
87
86
  - gc_tracer.gemspec
@@ -1,556 +0,0 @@
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
- const char *klass_path;
17
- size_t generation;
18
-
19
- /* allocation info */
20
- const char *path;
21
- unsigned long line;
22
-
23
- struct allocation_info *next;
24
- };
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
-
36
- struct traceobj_arg {
37
- int running;
38
- int keys, vals;
39
- st_table *aggregate_table; /* user defined key -> [count, total_age, max_age, min_age] */
40
- st_table *object_table; /* obj (VALUE) -> allocation_info */
41
- st_table *str_table; /* cstr -> refcount */
42
- struct allocation_info *freed_allocation_info;
43
- };
44
-
45
- extern VALUE rb_mGCTracer;
46
-
47
- static char *
48
- keep_unique_str(st_table *tbl, const char *str)
49
- {
50
- st_data_t n;
51
-
52
- if (str && st_lookup(tbl, (st_data_t)str, &n)) {
53
- char *result;
54
-
55
- st_insert(tbl, (st_data_t)str, n+1);
56
- st_get_key(tbl, (st_data_t)str, (st_data_t *)&result);
57
-
58
- return result;
59
- }
60
- else {
61
- return NULL;
62
- }
63
- }
64
-
65
- static const char *
66
- make_unique_str(st_table *tbl, const char *str, long len)
67
- {
68
- if (!str) {
69
- return NULL;
70
- }
71
- else {
72
- char *result;
73
-
74
- if ((result = keep_unique_str(tbl, str)) == NULL) {
75
- result = (char *)ruby_xmalloc(len+1);
76
- strncpy(result, str, len);
77
- result[len] = 0;
78
- st_add_direct(tbl, (st_data_t)result, 1);
79
- }
80
- return result;
81
- }
82
- }
83
-
84
- static void
85
- delete_unique_str(st_table *tbl, const char *str)
86
- {
87
- if (str) {
88
- st_data_t n;
89
-
90
- st_lookup(tbl, (st_data_t)str, &n);
91
- if (n == 1) {
92
- st_delete(tbl, (st_data_t *)&str, 0);
93
- ruby_xfree((char *)str);
94
- }
95
- else {
96
- st_insert(tbl, (st_data_t)str, n-1);
97
- }
98
- }
99
- }
100
-
101
- /* file, line, type */
102
- #define MAX_KEY_DATA 4
103
-
104
- struct memcmp_key_data {
105
- int n;
106
- st_data_t data[4];
107
- };
108
-
109
- static int
110
- memcmp_hash_compare(st_data_t a, st_data_t b)
111
- {
112
- struct memcmp_key_data *k1 = (struct memcmp_key_data *)a;
113
- struct memcmp_key_data *k2 = (struct memcmp_key_data *)b;
114
- return memcmp(&k1->data[0], &k2->data[0], k1->n * sizeof(st_data_t));
115
- }
116
-
117
- static st_index_t
118
- memcmp_hash_hash(st_data_t a)
119
- {
120
- struct memcmp_key_data *k = (struct memcmp_key_data *)a;
121
- return rb_memhash(k->data, sizeof(st_data_t) * k->n);
122
- }
123
-
124
- static const struct st_hash_type memcmp_hash_type = {
125
- memcmp_hash_compare, memcmp_hash_hash
126
- };
127
-
128
- static struct traceobj_arg *tmp_trace_arg; /* TODO: Do not use global variables */
129
-
130
- static struct traceobj_arg *
131
- get_traceobj_arg(void)
132
- {
133
- if (tmp_trace_arg == 0) {
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;
138
- tmp_trace_arg->aggregate_table = st_init_table(&memcmp_hash_type);
139
- tmp_trace_arg->object_table = st_init_numtable();
140
- tmp_trace_arg->str_table = st_init_strtable();
141
- tmp_trace_arg->freed_allocation_info = NULL;
142
- }
143
- return tmp_trace_arg;
144
- }
145
-
146
- static int
147
- free_keys_i(st_data_t key, st_data_t value, void *data)
148
- {
149
- ruby_xfree((void *)key);
150
- return ST_CONTINUE;
151
- }
152
-
153
- static int
154
- free_values_i(st_data_t key, st_data_t value, void *data)
155
- {
156
- ruby_xfree((void *)value);
157
- return ST_CONTINUE;
158
- }
159
-
160
- static int
161
- free_key_values_i(st_data_t key, st_data_t value, void *data)
162
- {
163
- ruby_xfree((void *)key);
164
- ruby_xfree((void *)value);
165
- return ST_CONTINUE;
166
- }
167
-
168
- static void
169
- clear_traceobj_arg(void)
170
- {
171
- struct traceobj_arg * arg = get_traceobj_arg();
172
-
173
- st_foreach(arg->aggregate_table, free_key_values_i, 0);
174
- st_clear(arg->aggregate_table);
175
- st_foreach(arg->object_table, free_values_i, 0);
176
- st_clear(arg->object_table);
177
- st_foreach(arg->str_table, free_keys_i, 0);
178
- st_clear(arg->str_table);
179
- }
180
-
181
- static struct allocation_info *
182
- create_allocation_info(void)
183
- {
184
- return (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
185
- }
186
-
187
- static void
188
- free_allocation_info(struct traceobj_arg *arg, struct allocation_info *info)
189
- {
190
- delete_unique_str(arg->str_table, info->path);
191
- delete_unique_str(arg->str_table, info->klass_path);
192
- ruby_xfree(info);
193
- }
194
-
195
- static void
196
- newobj_i(VALUE tpval, void *data)
197
- {
198
- struct traceobj_arg *arg = (struct traceobj_arg *)data;
199
- struct allocation_info *info;
200
- rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
201
- VALUE obj = rb_tracearg_object(tparg);
202
- VALUE klass = RBASIC_CLASS(obj);
203
- VALUE path = rb_tracearg_path(tparg);
204
- VALUE line = rb_tracearg_lineno(tparg);
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;
208
-
209
- if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
210
- if (info->living) {
211
- /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */
212
- }
213
- /* reuse info */
214
- delete_unique_str(arg->str_table, info->path);
215
- delete_unique_str(arg->str_table, info->klass_path);
216
- }
217
- else {
218
- info = create_allocation_info();
219
- }
220
-
221
- info->next = NULL;
222
- info->living = 1;
223
- info->flags = RBASIC(obj)->flags;
224
- info->klass = klass;
225
- info->klass_path = klass_path_cstr;
226
- info->generation = rb_gc_count();
227
-
228
- info->path = path_cstr;
229
- info->line = NUM2INT(line);
230
-
231
- st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
232
- }
233
-
234
- /* file, line, type, klass */
235
- #define MAX_KEY_SIZE 4
236
-
237
- void
238
- aggregator_i(void *data)
239
- {
240
- size_t gc_count = rb_gc_count();
241
- struct traceobj_arg *arg = (struct traceobj_arg *)data;
242
- struct allocation_info *info = arg->freed_allocation_info;
243
-
244
- arg->freed_allocation_info = NULL;
245
-
246
- while (info) {
247
- struct allocation_info *next_info = info->next;
248
- st_data_t key, val;
249
- struct memcmp_key_data key_data;
250
- int *val_buff;
251
- int age = (int)(gc_count - info->generation);
252
- int i;
253
-
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;
268
- key = (st_data_t)&key_data;
269
-
270
- if (st_lookup(arg->aggregate_table, key, &val) == 0) {
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
- }
277
- key = (st_data_t)key_buff;
278
-
279
- /* count, total age, max age, min age */
280
- val_buff = ALLOC_N(int, 4);
281
- val_buff[0] = val_buff[1] = 0;
282
- val_buff[2] = val_buff[3] = age;
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
-
287
- st_insert(arg->aggregate_table, (st_data_t)key_buff, (st_data_t)val_buff);
288
- }
289
- else {
290
- val_buff = (int *)val;
291
- }
292
-
293
- val_buff[0] += 1;
294
- val_buff[1] += age;
295
- if (val_buff[2] > age) val_buff[2] = age;
296
- if (val_buff[3] < age) val_buff[3] = age;
297
-
298
- free_allocation_info(arg, info);
299
- info = next_info;
300
- }
301
- }
302
-
303
- static void
304
- move_to_freed_list(struct traceobj_arg *arg, VALUE obj, struct allocation_info *info)
305
- {
306
- info->next = arg->freed_allocation_info;
307
- arg->freed_allocation_info = info;
308
- st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
309
- }
310
-
311
- static void
312
- freeobj_i(VALUE tpval, void *data)
313
- {
314
- struct traceobj_arg *arg = (struct traceobj_arg *)data;
315
- rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
316
- VALUE obj = rb_tracearg_object(tparg);
317
- struct allocation_info *info;
318
-
319
- if (arg->freed_allocation_info == NULL) {
320
- rb_postponed_job_register_one(0, aggregator_i, arg);
321
- }
322
-
323
- if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
324
- move_to_freed_list(arg, obj, info);
325
- }
326
- }
327
-
328
- static void
329
- start_alloc_hooks(VALUE mod)
330
- {
331
- VALUE newobj_hook, freeobj_hook;
332
- struct traceobj_arg *arg = get_traceobj_arg();
333
-
334
- if ((newobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("newobj_hook"))) == Qnil) {
335
- rb_ivar_set(rb_mGCTracer, rb_intern("newobj_hook"), newobj_hook = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg));
336
- rb_ivar_set(rb_mGCTracer, rb_intern("freeobj_hook"), freeobj_hook = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg));
337
- }
338
- else {
339
- freeobj_hook = rb_ivar_get(rb_mGCTracer, rb_intern("freeobj_hook"));
340
- }
341
-
342
- rb_tracepoint_enable(newobj_hook);
343
- rb_tracepoint_enable(freeobj_hook);
344
- }
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
-
386
- static int
387
- aggregate_result_i(st_data_t key, st_data_t val, void *data)
388
- {
389
- struct arg_and_result *aar = (struct arg_and_result *)data;
390
- struct traceobj_arg *arg = aar->arg;
391
- VALUE result = aar->result;
392
-
393
- int *val_buff = (int *)val;
394
- struct memcmp_key_data *key_buff = (struct memcmp_key_data *)key;
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
- }
434
-
435
- rb_hash_aset(result, k, v);
436
-
437
- return ST_CONTINUE;
438
- }
439
-
440
- static int
441
- aggregate_rest_object_i(st_data_t key, st_data_t val, void *data)
442
- {
443
- struct traceobj_arg *arg = (struct traceobj_arg *)data;
444
- struct allocation_info *info = (struct allocation_info *)val;
445
- move_to_freed_list(arg, (VALUE)key, info);
446
- return ST_CONTINUE;
447
- }
448
-
449
- static VALUE
450
- aggregate_result(struct traceobj_arg *arg)
451
- {
452
- struct arg_and_result aar;
453
- aar.result = rb_hash_new();
454
- aar.arg = arg;
455
-
456
- st_foreach(arg->object_table, aggregate_rest_object_i, (st_data_t)arg);
457
- aggregator_i(arg);
458
- st_foreach(arg->aggregate_table, aggregate_result_i, (st_data_t)&aar);
459
- clear_traceobj_arg();
460
- return aar.result;
461
- }
462
-
463
- static VALUE
464
- stop_allocation_tracing(VALUE self)
465
- {
466
- struct traceobj_arg * arg = get_traceobj_arg();
467
-
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"));
471
- rb_tracepoint_disable(newobj_hook);
472
- rb_tracepoint_disable(freeobj_hook);
473
-
474
- arg->running = 0;
475
- }
476
- else {
477
- rb_raise(rb_eRuntimeError, "not started yet.");
478
- }
479
-
480
- return Qnil;
481
- }
482
-
483
- VALUE
484
- gc_tracer_stop_allocation_tracing(VALUE self)
485
- {
486
- stop_allocation_tracing(self);
487
- return aggregate_result(get_traceobj_arg());
488
- }
489
-
490
- VALUE
491
- gc_tracer_start_allocation_tracing(VALUE self)
492
- {
493
- struct traceobj_arg * arg = get_traceobj_arg();
494
-
495
- if (arg->running) {
496
- rb_raise(rb_eRuntimeError, "can't run recursivly");
497
- }
498
- else {
499
- arg->running = 1;
500
- if (arg->keys == 0) arg->keys = KEY_PATH | KEY_LINE;
501
- start_alloc_hooks(rb_mGCTracer);
502
-
503
- if (rb_block_given_p()) {
504
- rb_ensure(rb_yield, Qnil, stop_allocation_tracing, Qnil);
505
- return aggregate_result(get_traceobj_arg());
506
- }
507
- }
508
-
509
- return Qnil;
510
- }
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
-