allocation_tracer 0.0.1 → 0.0.2
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 +4 -4
- data/README.md +10 -1
- data/ext/allocation_tracer/allocation_tracer.c +15 -18
- data/lib/allocation_tracer/allocation_tracer.so +0 -0
- data/lib/allocation_tracer/version.rb +1 -1
- data/spec/allocation_tracer_spec.rb +34 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1f129686d0942e7690ff66568f9af7f50f11148
|
4
|
+
data.tar.gz: fbc17e5b8358996aa55843c8632e15896d14f16d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 886adb15a4f94f3fd9ed2815f9aee9fa524bf48aa853ceafb9bf2ff450a5dcd204ce8b8c483029ba78d1bc81f2124e7bb6662770d253e2aed58262c24a54e8ad
|
7
|
+
data.tar.gz: 69b0c17158f3f8af672296a4868c609a2f12c0c8b8d10d08b7d2309c6357b35b2b9f843986594c62bfad1549a62aeae12f90e9cfe3db3c299ee59f5001b07b00
|
data/README.md
CHANGED
@@ -26,7 +26,16 @@ Or install it yourself as:
|
|
26
26
|
|
27
27
|
### Allocation tracing
|
28
28
|
|
29
|
-
You can trace allocation
|
29
|
+
You can trace allocation and aggregate information. Information includes:
|
30
|
+
|
31
|
+
count - how many objects are created.
|
32
|
+
total_age - total age of objects which created here
|
33
|
+
max_age - age of longest living object created here
|
34
|
+
min_age - age of shortest living object created here
|
35
|
+
|
36
|
+
Age of Object can be calculated by this formula: [current GC count] - [birth time GC count]
|
37
|
+
|
38
|
+
For example:
|
30
39
|
|
31
40
|
```ruby
|
32
41
|
require 'allocation_tracer'
|
@@ -15,7 +15,6 @@ struct allocation_info {
|
|
15
15
|
int living;
|
16
16
|
VALUE flags;
|
17
17
|
VALUE klass;
|
18
|
-
const char *klass_path;
|
19
18
|
size_t generation;
|
20
19
|
|
21
20
|
/* allocation info */
|
@@ -188,7 +187,6 @@ static void
|
|
188
187
|
free_allocation_info(struct traceobj_arg *arg, struct allocation_info *info)
|
189
188
|
{
|
190
189
|
delete_unique_str(arg->str_table, info->path);
|
191
|
-
delete_unique_str(arg->str_table, info->klass_path);
|
192
190
|
ruby_xfree(info);
|
193
191
|
}
|
194
192
|
|
@@ -199,12 +197,9 @@ newobj_i(VALUE tpval, void *data)
|
|
199
197
|
struct allocation_info *info;
|
200
198
|
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
201
199
|
VALUE obj = rb_tracearg_object(tparg);
|
202
|
-
VALUE klass = RBASIC_CLASS(obj);
|
203
200
|
VALUE path = rb_tracearg_path(tparg);
|
204
201
|
VALUE line = rb_tracearg_lineno(tparg);
|
205
|
-
VALUE klass_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_class_path_cached(klass) : Qnil;
|
206
202
|
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
203
|
|
209
204
|
if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
|
210
205
|
if (info->living) {
|
@@ -212,7 +207,6 @@ newobj_i(VALUE tpval, void *data)
|
|
212
207
|
}
|
213
208
|
/* reuse info */
|
214
209
|
delete_unique_str(arg->str_table, info->path);
|
215
|
-
delete_unique_str(arg->str_table, info->klass_path);
|
216
210
|
}
|
217
211
|
else {
|
218
212
|
info = create_allocation_info();
|
@@ -221,8 +215,7 @@ newobj_i(VALUE tpval, void *data)
|
|
221
215
|
info->next = NULL;
|
222
216
|
info->living = 1;
|
223
217
|
info->flags = RBASIC(obj)->flags;
|
224
|
-
info->klass =
|
225
|
-
info->klass_path = klass_path_cstr;
|
218
|
+
info->klass = rb_class_real(RBASIC_CLASS(obj));
|
226
219
|
info->generation = rb_gc_count();
|
227
220
|
|
228
221
|
info->path = path_cstr;
|
@@ -262,7 +255,7 @@ aggregator_i(void *data)
|
|
262
255
|
key_data.data[i++] = (st_data_t)(info->flags & T_MASK);
|
263
256
|
}
|
264
257
|
if (arg->keys & KEY_CLASS) {
|
265
|
-
key_data.data[i++] =
|
258
|
+
key_data.data[i++] = info->klass;
|
266
259
|
}
|
267
260
|
key_data.n = i;
|
268
261
|
key = (st_data_t)&key_data;
|
@@ -282,7 +275,6 @@ aggregator_i(void *data)
|
|
282
275
|
val_buff[2] = val_buff[3] = age;
|
283
276
|
|
284
277
|
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
278
|
|
287
279
|
st_insert(arg->aggregate_table, (st_data_t)key_buff, (st_data_t)val_buff);
|
288
280
|
}
|
@@ -301,11 +293,10 @@ aggregator_i(void *data)
|
|
301
293
|
}
|
302
294
|
|
303
295
|
static void
|
304
|
-
move_to_freed_list(struct traceobj_arg *arg,
|
296
|
+
move_to_freed_list(struct traceobj_arg *arg, struct allocation_info *info)
|
305
297
|
{
|
306
298
|
info->next = arg->freed_allocation_info;
|
307
299
|
arg->freed_allocation_info = info;
|
308
|
-
st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
|
309
300
|
}
|
310
301
|
|
311
302
|
static void
|
@@ -321,7 +312,8 @@ freeobj_i(VALUE tpval, void *data)
|
|
321
312
|
}
|
322
313
|
|
323
314
|
if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
|
324
|
-
move_to_freed_list(arg,
|
315
|
+
move_to_freed_list(arg, info);
|
316
|
+
st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
|
325
317
|
}
|
326
318
|
}
|
327
319
|
|
@@ -422,10 +414,12 @@ aggregate_result_i(st_data_t key, st_data_t val, void *data)
|
|
422
414
|
rb_ary_push(k, type_symbols[key_buff->data[i++]]);
|
423
415
|
}
|
424
416
|
if (arg->keys & KEY_CLASS) {
|
425
|
-
|
426
|
-
if (
|
427
|
-
|
428
|
-
|
417
|
+
VALUE klass = key_buff->data[i++];
|
418
|
+
if (BUILTIN_TYPE(klass) == T_CLASS) {
|
419
|
+
klass = rb_class_real(klass);
|
420
|
+
rb_ary_push(k, klass);
|
421
|
+
/* TODO: actually, it is dangerous code because klass can be sweeped */
|
422
|
+
/* So that class specifier is hidden feature */
|
429
423
|
}
|
430
424
|
else {
|
431
425
|
rb_ary_push(k, Qnil);
|
@@ -442,7 +436,7 @@ aggregate_rest_object_i(st_data_t key, st_data_t val, void *data)
|
|
442
436
|
{
|
443
437
|
struct traceobj_arg *arg = (struct traceobj_arg *)data;
|
444
438
|
struct allocation_info *info = (struct allocation_info *)val;
|
445
|
-
move_to_freed_list(arg,
|
439
|
+
move_to_freed_list(arg, info);
|
446
440
|
return ST_CONTINUE;
|
447
441
|
}
|
448
442
|
|
@@ -454,6 +448,7 @@ aggregate_result(struct traceobj_arg *arg)
|
|
454
448
|
aar.arg = arg;
|
455
449
|
|
456
450
|
st_foreach(arg->object_table, aggregate_rest_object_i, (st_data_t)arg);
|
451
|
+
st_clear(arg->object_table);
|
457
452
|
aggregator_i(arg);
|
458
453
|
st_foreach(arg->aggregate_table, aggregate_result_i, (st_data_t)&aar);
|
459
454
|
clear_traceobj_arg();
|
@@ -521,6 +516,8 @@ allocation_tracer_setup(int argc, VALUE *argv, VALUE self)
|
|
521
516
|
int i;
|
522
517
|
VALUE ary = rb_check_array_type(argv[0]);
|
523
518
|
|
519
|
+
arg->keys = 0;
|
520
|
+
|
524
521
|
for (i=0; i<(int)RARRAY_LEN(ary); i++) {
|
525
522
|
if (RARRAY_AREF(ary, i) == ID2SYM(rb_intern("path"))) arg->keys |= KEY_PATH;
|
526
523
|
else if (RARRAY_AREF(ary, i) == ID2SYM(rb_intern("line"))) arg->keys |= KEY_LINE;
|
Binary file
|
@@ -23,5 +23,39 @@ describe ObjectSpace::AllocationTracer do
|
|
23
23
|
expect(result.length).to be >= 1
|
24
24
|
expect(result[[__FILE__, line]]).to eq [1, 0, 0, 0]
|
25
25
|
end
|
26
|
+
|
27
|
+
describe 'with different setup' do
|
28
|
+
it 'should work with type' do
|
29
|
+
line = __LINE__ + 3
|
30
|
+
ObjectSpace::AllocationTracer.setup(%i(path line type))
|
31
|
+
result = ObjectSpace::AllocationTracer.trace do
|
32
|
+
a = [Object.new]
|
33
|
+
b = {Object.new => 'foo'}
|
34
|
+
end
|
35
|
+
|
36
|
+
expect(result.length).to be 5
|
37
|
+
expect(result[[__FILE__, line, :T_OBJECT]]).to eq [1, 0, 0, 0]
|
38
|
+
expect(result[[__FILE__, line, :T_ARRAY]]).to eq [1, 0, 0, 0]
|
39
|
+
expect(result[[__FILE__, line + 1, :T_HASH]]).to eq [1, 0, 0, 0]
|
40
|
+
expect(result[[__FILE__, line + 1, :T_OBJECT]]).to eq [1, 0, 0, 0]
|
41
|
+
expect(result[[__FILE__, line + 1, :T_STRING]]).to eq [1, 0, 0, 0]
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should work with class' do
|
45
|
+
line = __LINE__ + 3
|
46
|
+
ObjectSpace::AllocationTracer.setup(%i(path line class))
|
47
|
+
result = ObjectSpace::AllocationTracer.trace do
|
48
|
+
a = [Object.new]
|
49
|
+
b = {Object.new => 'foo'}
|
50
|
+
end
|
51
|
+
|
52
|
+
expect(result.length).to be 5
|
53
|
+
expect(result[[__FILE__, line, Object]]).to eq [1, 0, 0, 0]
|
54
|
+
expect(result[[__FILE__, line, Array]]).to eq [1, 0, 0, 0]
|
55
|
+
expect(result[[__FILE__, line + 1, Hash]]).to eq [1, 0, 0, 0]
|
56
|
+
expect(result[[__FILE__, line + 1, Object]]).to eq [1, 0, 0, 0]
|
57
|
+
expect(result[[__FILE__, line + 1, String]]).to eq [1, 0, 0, 0]
|
58
|
+
end
|
59
|
+
end
|
26
60
|
end
|
27
61
|
end
|