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 +4 -4
- data/README.md +0 -122
- data/ext/gc_tracer/gc_tracer.c +1 -14
- data/lib/gc_tracer/version.rb +1 -1
- data/spec/gc_tracer_spec.rb +0 -22
- metadata +1 -2
- data/ext/gc_tracer/allocation_tracer.c +0 -556
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f02605c54bfd6ac29179037b21d3af38c7c8e0bd
|
4
|
+
data.tar.gz: a7a328f0b154d4952bf7ca57f669303d53b341a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/ext/gc_tracer/gc_tracer.c
CHANGED
@@ -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 =
|
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();
|
data/lib/gc_tracer/version.rb
CHANGED
data/spec/gc_tracer_spec.rb
CHANGED
@@ -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.
|
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
|
-
|