gc_tracer 0.2.1 → 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: 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
-