gctools 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZDEyYzcxODZkNjQ4NDA4ZDg0NDA4ZDZhZGM3Y2RkOTcwOWYzYTM0MA==
5
+ data.tar.gz: !binary |-
6
+ NTBhOWZkOTRkOTg2NzJjODhlNWNhZjk1ZWQ0MWFkYWYzOWIxODAzNQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YzNjMTMyMzZmMjVjMDA3OTUwNGU5YTY3NmVhZjg0OTU2NTMyZWQ4Mjc1ODNj
10
+ OWYwMDZiNGMxNjNmMTRkNzMwNmM2YmJkYTAyODM4NTQyNmJmMTZmOTU2NGFi
11
+ NWYyNzQ1NTVmYWZlNzJmODEzYjc2MGRmNzljZWNkMDNlNmIxMDA=
12
+ data.tar.gz: !binary |-
13
+ N2IxZTJiYWY3YTUxYmNjZjcyZTMwZGQzNDQ4ZmM1MTllMTY2NmZhMmZlODlj
14
+ ODVlYjgyODZiZTFiOGNkMDQ0OTY1ZDVjZTBjYjI1ZjE2Y2IyYTZkMWJmNTBi
15
+ ZTZiNWE5MTQ5YmE2OWNjYzQwMmM0NGJjNjJkYTRmNTQ2ZGU4Zjc=
@@ -0,0 +1,4 @@
1
+ /tmp
2
+ /lib/gctools/*.bundle
3
+ /lib/gctools/*.so
4
+ /.bundle
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gctools (0.2.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ rake (10.1.0)
10
+ rake-compiler (0.9.2)
11
+ rake
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ gctools!
18
+ rake-compiler (~> 0.9)
@@ -0,0 +1,24 @@
1
+ ## gctools
2
+
3
+ gc profiler/logger and oobgc for rgengc in ruby 2.1
4
+
5
+ ### design
6
+
7
+ built on new apis and events offered in ruby 2.1:
8
+
9
+ * rb_gc_stat()
10
+ * rb_gc_latest_gc_info()
11
+ * RUBY_INTERNAL_EVENT_GC_START
12
+ * RUBY_INTERNAL_EVENT_GC_END_MARK
13
+ * RUBY_INTERNAL_EVENT_GC_END_SWEEP
14
+
15
+ ### usage
16
+
17
+ #### logger
18
+
19
+ require 'gctools/logger'
20
+
21
+ #### oobgc
22
+
23
+ require 'gctools/oobgc'
24
+ GC::OOB.run
@@ -0,0 +1,36 @@
1
+ task :default => :test
2
+
3
+ # ==========================================================
4
+ # Packaging
5
+ # ==========================================================
6
+
7
+ GEMSPEC = eval(File.read('gctools.gemspec'))
8
+
9
+ require 'rubygems/package_task'
10
+ Gem::PackageTask.new(GEMSPEC) do |pkg|
11
+ end
12
+
13
+ # ==========================================================
14
+ # Ruby Extension
15
+ # ==========================================================
16
+
17
+ require 'rake/extensiontask'
18
+ Rake::ExtensionTask.new('gcprof', GEMSPEC) do |ext|
19
+ ext.ext_dir = 'ext/gcprof'
20
+ ext.lib_dir = 'lib/gctools'
21
+ end
22
+ Rake::ExtensionTask.new('oobgc', GEMSPEC) do |ext|
23
+ ext.ext_dir = 'ext/oobgc'
24
+ ext.lib_dir = 'lib/gctools'
25
+ end
26
+ task :build => :compile
27
+
28
+ # ==========================================================
29
+ # Testing
30
+ # ==========================================================
31
+
32
+ require 'rake/testtask'
33
+ Rake::TestTask.new 'test' do |t|
34
+ t.test_files = FileList['test/test_*.rb']
35
+ end
36
+ task :test => :build
@@ -0,0 +1,12 @@
1
+ require 'mkmf'
2
+ have_func('rb_gc_stat')
3
+ have_func('rb_gc_latest_gc_info')
4
+ gc_event = have_const('RUBY_INTERNAL_EVENT_GC_END_MARK')
5
+
6
+ if gc_event
7
+ create_makefile('gctools/gcprof')
8
+ else
9
+ File.open('Makefile', 'w') do |f|
10
+ f.puts "install:\n\t\n"
11
+ end
12
+ end
@@ -0,0 +1,146 @@
1
+ #include <time.h>
2
+ #include <sys/time.h>
3
+ #include "ruby/ruby.h"
4
+ #include "ruby/debug.h"
5
+
6
+ static VALUE mGCProf;
7
+ static VALUE sym_time, sym_count, sym_immediate_sweep;
8
+ static struct {
9
+ size_t serial;
10
+ VALUE tpval;
11
+ VALUE proc;
12
+
13
+ VALUE info;
14
+ VALUE start;
15
+ VALUE end_mark;
16
+ VALUE end_sweep;
17
+ } _gcprof;
18
+
19
+ static inline double
20
+ walltime()
21
+ {
22
+ struct timespec ts;
23
+ #ifdef HAVE_CLOCK_GETTIME
24
+ if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
25
+ rb_sys_fail("clock_gettime");
26
+ }
27
+ #else
28
+ {
29
+ struct timeval tv;
30
+ if (gettimeofday(&tv, 0) < 0) {
31
+ rb_sys_fail("gettimeofday");
32
+ }
33
+ ts.tv_sec = tv.tv_sec;
34
+ ts.tv_nsec = tv.tv_usec * 1000;
35
+ }
36
+ #endif
37
+ return ts.tv_sec + ts.tv_nsec * 1e-9;
38
+ }
39
+
40
+ static const char *
41
+ event_flag_name(rb_event_flag_t flag)
42
+ {
43
+ switch (flag) {
44
+ case RUBY_INTERNAL_EVENT_GC_START:
45
+ return "gc_start";
46
+ case RUBY_INTERNAL_EVENT_GC_END_MARK:
47
+ return "gc_end_mark";
48
+ case RUBY_INTERNAL_EVENT_GC_END_SWEEP:
49
+ return "gc_end_sweep";
50
+ default:
51
+ return "unknown";
52
+ }
53
+ }
54
+
55
+ static void
56
+ invoke_proc(void *data)
57
+ {
58
+ VALUE args[4] = {_gcprof.info, _gcprof.start, _gcprof.end_mark, _gcprof.end_sweep};
59
+ if (_gcprof.serial != (size_t)data) return;
60
+ if (_gcprof.serial != NUM2SIZET(rb_hash_aref(_gcprof.end_sweep, sym_count))) args[3] = Qnil;
61
+ rb_proc_call_with_block(_gcprof.proc, 4, args, Qnil);
62
+ }
63
+
64
+ static void
65
+ gc_hook_i(VALUE tpval, void *data)
66
+ {
67
+ rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
68
+ rb_event_flag_t flag = rb_tracearg_event_flag(tparg);
69
+ VALUE obj =
70
+ flag == RUBY_INTERNAL_EVENT_GC_START ? _gcprof.start :
71
+ flag == RUBY_INTERNAL_EVENT_GC_END_MARK ? _gcprof.end_mark :
72
+ _gcprof.end_sweep;
73
+
74
+ if (0) {
75
+ fprintf(stderr, "TRACEPOINT: time=%f, event=%s(%zu)\n", walltime(), event_flag_name(rb_tracearg_event_flag(tparg)), rb_gc_count());
76
+ }
77
+
78
+ rb_hash_aset(obj, sym_time, DBL2NUM(walltime()));
79
+ rb_gc_stat(obj);
80
+
81
+ switch (flag) {
82
+ case RUBY_INTERNAL_EVENT_GC_START:
83
+ rb_gc_latest_gc_info(_gcprof.info);
84
+ _gcprof.serial = rb_gc_count();
85
+ return;
86
+ case RUBY_INTERNAL_EVENT_GC_END_MARK:
87
+ if (RTEST(rb_hash_aref(_gcprof.info, sym_immediate_sweep))) return;
88
+ /* fall through */
89
+ case RUBY_INTERNAL_EVENT_GC_END_SWEEP:
90
+ rb_postponed_job_register(0, invoke_proc, (void *)_gcprof.serial);
91
+ break;
92
+ }
93
+ }
94
+
95
+ static VALUE
96
+ set_after_gc_hook(VALUE module, VALUE proc)
97
+ {
98
+ rb_event_flag_t events;
99
+
100
+ /* disable previous keys */
101
+ if (RTEST(_gcprof.tpval)) {
102
+ rb_tracepoint_disable(_gcprof.tpval);
103
+ _gcprof.tpval = Qnil;
104
+ _gcprof.proc = Qnil;
105
+ }
106
+
107
+ if (RTEST(proc)) {
108
+ if (!rb_obj_is_proc(proc)) {
109
+ rb_raise(rb_eTypeError, "trace_func needs to be Proc");
110
+ }
111
+
112
+ events = RUBY_INTERNAL_EVENT_GC_START|RUBY_INTERNAL_EVENT_GC_END_MARK|RUBY_INTERNAL_EVENT_GC_END_SWEEP;
113
+ _gcprof.tpval = rb_tracepoint_new(0, events, gc_hook_i, (void *)0);
114
+ _gcprof.proc = proc;
115
+ rb_tracepoint_enable(_gcprof.tpval);
116
+ }
117
+
118
+ return proc;
119
+ }
120
+
121
+
122
+ void
123
+ Init_gcprof(void)
124
+ {
125
+ mGCProf = rb_define_module("GCProf");
126
+ rb_define_module_function(mGCProf, "after_gc_hook=", set_after_gc_hook, 1);
127
+
128
+ sym_immediate_sweep = ID2SYM(rb_intern("immediate_sweep"));
129
+ sym_time = ID2SYM(rb_intern("time"));
130
+ sym_count = ID2SYM(rb_intern("count"));
131
+
132
+ rb_gc_latest_gc_info(_gcprof.info = rb_hash_new());
133
+ _gcprof.start = rb_hash_new();
134
+ rb_hash_aset(_gcprof.start, sym_time, INT2FIX(0));
135
+ rb_gc_stat(_gcprof.start);
136
+ _gcprof.end_mark = rb_obj_dup(_gcprof.start);
137
+ _gcprof.end_sweep = rb_obj_dup(_gcprof.start);
138
+
139
+ rb_gc_register_mark_object(_gcprof.info);
140
+ rb_gc_register_mark_object(_gcprof.start);
141
+ rb_gc_register_mark_object(_gcprof.end_mark);
142
+ rb_gc_register_mark_object(_gcprof.end_sweep);
143
+
144
+ rb_gc_register_address(&_gcprof.proc);
145
+ rb_gc_register_address(&_gcprof.tpval);
146
+ }
@@ -0,0 +1,12 @@
1
+ require 'mkmf'
2
+ have_func('rb_gc_stat')
3
+ have_func('rb_gc_latest_gc_info')
4
+ gc_event = have_const('RUBY_INTERNAL_EVENT_GC_END_MARK')
5
+
6
+ if gc_event
7
+ create_makefile('gctools/oobgc')
8
+ else
9
+ File.open('Makefile', 'w') do |f|
10
+ f.puts "install:\n\t\n"
11
+ end
12
+ end
@@ -0,0 +1,217 @@
1
+ #include <time.h>
2
+ #include <sys/time.h>
3
+ #include "ruby/ruby.h"
4
+ #include "ruby/intern.h"
5
+ #include "ruby/debug.h"
6
+
7
+ struct {
8
+ int installed;
9
+ VALUE tpval;
10
+
11
+ int sweep_needed;
12
+ size_t prev_allocated_object;
13
+ size_t allocation_limit;
14
+ size_t heap_obj_limit;
15
+
16
+ struct {
17
+ size_t total_allocated_object;
18
+ size_t heap_tomb_page_length;
19
+ } start;
20
+
21
+ struct {
22
+ size_t mean;
23
+ size_t max;
24
+ } threshold;
25
+
26
+ struct {
27
+ size_t minor;
28
+ size_t major;
29
+ size_t sweep;
30
+ } stat;
31
+ } _oobgc;
32
+
33
+ static VALUE mOOB;
34
+ static VALUE sym_total_allocated_object, sym_heap_swept_slot, sym_heap_tomb_page_length, sym_heap_final_slot;
35
+ static VALUE sym_old_object, sym_old_object_limit, sym_remembered_shady_object, sym_remembered_shady_object_limit;
36
+ static VALUE sym_major_by, sym_count, sym_major_count, sym_minor_count, sym_sweep_count, minor_gc_args;
37
+ static ID id_start;
38
+
39
+ static void
40
+ gc_event_i(VALUE tpval, void *data)
41
+ {
42
+ rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
43
+ rb_event_flag_t flag = rb_tracearg_event_flag(tparg);
44
+
45
+ switch (flag) {
46
+ case RUBY_INTERNAL_EVENT_GC_START:
47
+ _oobgc.allocation_limit = 0;
48
+ _oobgc.start.total_allocated_object = rb_gc_stat(sym_total_allocated_object);
49
+ _oobgc.start.heap_tomb_page_length = rb_gc_stat(sym_heap_tomb_page_length);
50
+ break;
51
+
52
+ case RUBY_INTERNAL_EVENT_GC_END_MARK:
53
+ _oobgc.sweep_needed = 1;
54
+ break;
55
+
56
+ case RUBY_INTERNAL_EVENT_GC_END_SWEEP:
57
+ _oobgc.sweep_needed = 0;
58
+ _oobgc.allocation_limit =
59
+ _oobgc.start.total_allocated_object +
60
+ rb_gc_stat(sym_heap_swept_slot) +
61
+ (rb_gc_stat(sym_heap_tomb_page_length) * _oobgc.heap_obj_limit) -
62
+ rb_gc_stat(sym_heap_final_slot);
63
+ break;
64
+ }
65
+ }
66
+
67
+ static VALUE
68
+ install()
69
+ {
70
+ rb_event_flag_t events =
71
+ RUBY_INTERNAL_EVENT_GC_START |
72
+ RUBY_INTERNAL_EVENT_GC_END_MARK |
73
+ RUBY_INTERNAL_EVENT_GC_END_SWEEP;
74
+
75
+ if (_oobgc.installed)
76
+ return Qfalse;
77
+
78
+ if (!_oobgc.tpval) {
79
+ _oobgc.tpval = rb_tracepoint_new(0, events, gc_event_i, (void *)0);
80
+ rb_ivar_set(mOOB, rb_intern("tpval"), _oobgc.tpval);
81
+ }
82
+
83
+ rb_tracepoint_enable(_oobgc.tpval);
84
+ _oobgc.installed = 1;
85
+ return Qtrue;
86
+ }
87
+
88
+ static void
89
+ gc_start_major()
90
+ {
91
+ rb_gc_start();
92
+ _oobgc.stat.major++;
93
+ }
94
+
95
+ static void
96
+ gc_start_minor()
97
+ {
98
+ rb_funcall(rb_mGC, id_start, 1, minor_gc_args);
99
+
100
+ if (RTEST(rb_gc_latest_gc_info(sym_major_by)))
101
+ _oobgc.stat.major++;
102
+ else
103
+ _oobgc.stat.minor++;
104
+ }
105
+
106
+ static VALUE
107
+ oobgc(VALUE self)
108
+ {
109
+ size_t curr = rb_gc_stat(sym_total_allocated_object);
110
+ if (!_oobgc.installed) install();
111
+
112
+ if (!_oobgc.prev_allocated_object) {
113
+ _oobgc.prev_allocated_object = curr;
114
+ } else {
115
+ size_t diff = curr - _oobgc.prev_allocated_object;
116
+ _oobgc.prev_allocated_object = curr;
117
+
118
+ if (_oobgc.threshold.mean)
119
+ _oobgc.threshold.mean = (diff / 4) + (_oobgc.threshold.mean * 3 / 4);
120
+ else
121
+ _oobgc.threshold.mean = diff;
122
+
123
+ if (diff > _oobgc.threshold.max)
124
+ _oobgc.threshold.max = diff;
125
+ if (_oobgc.threshold.max > 200000)
126
+ _oobgc.threshold.max = 200000;
127
+ }
128
+
129
+ if (_oobgc.sweep_needed) {
130
+ /* lazy sweep started sometime recently.
131
+ * disable/enable the GC to force gc_rest_sweep() OOB
132
+ */
133
+ if (rb_gc_disable() == Qfalse) rb_gc_enable();
134
+ _oobgc.stat.sweep++;
135
+ return Qtrue;
136
+
137
+ } else if (_oobgc.allocation_limit) {
138
+ /* GC will be required when total_allocated_object gets
139
+ * close to allocation_limit
140
+ */
141
+ if ((rb_gc_stat(sym_old_object) >= rb_gc_stat(sym_old_object_limit)*0.97 ||
142
+ rb_gc_stat(sym_remembered_shady_object) >= rb_gc_stat(sym_remembered_shady_object_limit)*0.97) &&
143
+ curr >= _oobgc.allocation_limit - _oobgc.threshold.max*0.98) {
144
+ /*fprintf(stderr, "oobgc MAJOR: %zu >= %zu - %zu\n", curr, _oobgc.allocation_limit, _oobgc.threshold.max);*/
145
+ gc_start_major();
146
+ return Qtrue;
147
+
148
+ } else if (curr >= _oobgc.allocation_limit - _oobgc.threshold.mean) {
149
+ /*fprintf(stderr, "oobgc minor: %zu >= %zu - %zu\n", curr, _oobgc.allocation_limit, _oobgc.threshold.mean);*/
150
+ gc_start_minor();
151
+ return Qtrue;
152
+ }
153
+ }
154
+
155
+ return Qfalse;
156
+ }
157
+
158
+ static VALUE
159
+ oobgc_stat(VALUE self, VALUE key)
160
+ {
161
+ if (!_oobgc.installed)
162
+ return Qnil;
163
+
164
+ if (key == sym_count)
165
+ return SIZET2NUM(_oobgc.stat.major + _oobgc.stat.minor + _oobgc.stat.sweep);
166
+ else if (key == sym_major_count)
167
+ return SIZET2NUM(_oobgc.stat.major);
168
+ else if (key == sym_minor_count)
169
+ return SIZET2NUM(_oobgc.stat.minor);
170
+ else if (key == sym_sweep_count)
171
+ return SIZET2NUM(_oobgc.stat.sweep);
172
+ else
173
+ return Qnil;
174
+ }
175
+
176
+ static VALUE
177
+ oobgc_clear(VALUE self)
178
+ {
179
+ MEMZERO(&_oobgc.stat, _oobgc.stat, 1);
180
+ return Qnil;
181
+ }
182
+
183
+ void
184
+ Init_oobgc()
185
+ {
186
+ mOOB = rb_define_module_under(rb_mGC, "OOB");
187
+ rb_define_singleton_method(mOOB, "setup", install, 0);
188
+ rb_define_singleton_method(mOOB, "run", oobgc, 0);
189
+ rb_define_singleton_method(mOOB, "stat", oobgc_stat, 1);
190
+ rb_define_singleton_method(mOOB, "clear", oobgc_clear, 0);
191
+
192
+ #define S(name) sym_##name = ID2SYM(rb_intern(#name));
193
+ S(total_allocated_object);
194
+ S(heap_swept_slot);
195
+ S(heap_tomb_page_length);
196
+ S(heap_final_slot);
197
+
198
+ S(old_object);
199
+ S(old_object_limit);
200
+ S(remembered_shady_object);
201
+ S(remembered_shady_object_limit);
202
+
203
+ S(major_by);
204
+ S(count);
205
+ S(major_count);
206
+ S(minor_count);
207
+ S(sweep_count);
208
+ #undef S
209
+
210
+ id_start = rb_intern("start");
211
+ _oobgc.heap_obj_limit =
212
+ NUM2SIZET(rb_hash_aref(rb_const_get(rb_mGC, rb_intern("INTERNAL_CONSTANTS")), ID2SYM(rb_intern("HEAP_OBJ_LIMIT"))));
213
+
214
+ minor_gc_args = rb_hash_new();
215
+ rb_hash_aset(minor_gc_args, ID2SYM(rb_intern("full_mark")), Qfalse);
216
+ rb_ivar_set(mOOB, rb_intern("minor_gc_args"), minor_gc_args);
217
+ }
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'gctools'
3
+ s.version = '0.2.1'
4
+ s.summary = 'rgengc tools for ruby 2.1+'
5
+ s.description = 'gc debugger, logger and profiler for rgengc in ruby 2.1'
6
+
7
+ s.homepage = 'https://github.com/tmm1/gctools'
8
+ s.authors = 'Aman Gupta'
9
+ s.email = 'aman@tmm1.net'
10
+ s.license = 'MIT'
11
+
12
+ s.files = `git ls-files`.split("\n")
13
+ s.extensions = ['ext/gcprof/extconf.rb', 'ext/oobgc/extconf.rb']
14
+ s.add_development_dependency 'rake-compiler', '~> 0.9'
15
+ end
@@ -0,0 +1,71 @@
1
+ require 'gctools/gcprof'
2
+
3
+ =begin
4
+ GC::Profiler.enable
5
+ at_exit{ GC::Profiler.raw_data.map{ |d| p(d) } }
6
+ =end
7
+
8
+ =begin
9
+ prev_alloc = GC.stat(:total_allocated_object)
10
+ GCProf.after_gc_hook = proc { |info, start, end_mark, end_sweep|
11
+ before = start
12
+ after = end_sweep || end_mark
13
+
14
+ if !info[:immediate_sweep] && end_sweep
15
+ STDERR.printf "[%f:% 4d] swept after %1.3fs swept:% 8d marked:% 8d\n",
16
+ after[:time],
17
+ after[:count],
18
+ after[:time] - end_mark[:time],
19
+ after[:heap_swept_slot],
20
+ marked_num
21
+ else
22
+ type = info[:major_by] ? "MAJOR GC" : "minor gc"
23
+ diff = after[:time] - before[:time]
24
+ diff_alloc, prev_alloc = before[:total_allocated_object] - prev_alloc, after[:total_allocated_object]
25
+
26
+ STDERR.printf "[%f:% 4d] %s (% 13s) took %1.3fs alloc:% 8d live: % 8d -> % 8d free: % 8d -> % 8d %s\n",
27
+ before[:time],
28
+ before[:count],
29
+ type,
30
+ info.values_at(:major_by, :gc_by).compact.join(","),
31
+ after[:time] - before[:time],
32
+ diff_alloc,
33
+ before[:heap_live_slot],
34
+ after[:heap_live_slot],
35
+ before[:heap_free_slot],
36
+ after[:heap_free_slot],
37
+ info[:immediate_sweep] ? " +immediate_sweep" : ""
38
+ end
39
+ }
40
+ =end
41
+
42
+ GCProf.after_gc_hook =
43
+ proc{ |info, start, end_mark, end_sweep|
44
+ if end_sweep
45
+ # eden_slots = oldgen_slots + remembered_shady_slots + longlived_infant_slots + new_infant_slots + empty_eden_slots
46
+ # heap_swept_slot = new_infant_slots + empty_eden_slots + empty_moved_tomb_slots
47
+ eden_slots = GC::INTERNAL_CONSTANTS[:HEAP_OBJ_LIMIT] * end_sweep[:heap_eden_page_length]
48
+ oldgen_slots = end_sweep[:old_object]
49
+ remembered_shady_slots = end_sweep[:remembered_shady_object]
50
+ new_infant_slots = end_sweep[:total_allocated_object] - end_mark[:total_allocated_object]
51
+ empty_moved_tomb_slots = GC::INTERNAL_CONSTANTS[:HEAP_OBJ_LIMIT] * (end_sweep[:heap_tomb_page_length] - end_mark[:heap_tomb_page_length])
52
+ empty_eden_slots = end_sweep[:heap_swept_slot] - new_infant_slots - empty_moved_tomb_slots
53
+ longlived_infant_slots = eden_slots - oldgen_slots - remembered_shady_slots - new_infant_slots - empty_eden_slots
54
+
55
+ # total_slots = eden_slots + tomb_slots
56
+ tomb_slots = GC::INTERNAL_CONSTANTS[:HEAP_OBJ_LIMIT] * end_sweep[:heap_tomb_page_length]
57
+ total_slots = eden_slots + tomb_slots
58
+
59
+ STDERR.printf "[%s in %1.3fs (% 13s)] % 8d slots = old (%4.1f%%) + remembered_shady (%4.1f%%) + shady (%4.1f%%) + infant (%4.1f%%) + empty (%4.1f%%) + tomb (%4.1f%%)\n",
60
+ info[:major_by] ? 'MAJOR GC' : 'minor gc',
61
+ end_mark[:time] - start[:time],
62
+ info.values_at(:major_by, :gc_by).compact.join(","),
63
+ total_slots,
64
+ oldgen_slots * 100.0 / total_slots,
65
+ remembered_shady_slots * 100.0 / total_slots,
66
+ longlived_infant_slots * 100.0 / total_slots,
67
+ new_infant_slots * 100.0 / total_slots,
68
+ empty_eden_slots * 100.0 / total_slots,
69
+ tomb_slots * 100.0 / total_slots
70
+ end
71
+ }
@@ -0,0 +1,16 @@
1
+ require 'test/unit'
2
+ require 'gctools/gcprof'
3
+
4
+ class TestGCProf < Test::Unit::TestCase
5
+ def teardown
6
+ GCProf.after_gc_hook = nil
7
+ end
8
+
9
+ def test_after_gc_hook
10
+ n = 0
11
+ GCProf.after_gc_hook = proc{ n += 1 }
12
+ GC.start
13
+ Object.new
14
+ assert_equal 1, n
15
+ end
16
+ end
@@ -0,0 +1,53 @@
1
+ require 'test/unit'
2
+ require 'gctools/oobgc'
3
+ require 'json'
4
+
5
+ class TestOOBGC < Test::Unit::TestCase
6
+ def setup
7
+ GC::OOB.setup
8
+ GC::OOB.clear
9
+ end
10
+
11
+ def test_oob_sweep
12
+ GC.start(immediate_sweep: false)
13
+ assert_equal false, GC.latest_gc_info(:immediate_sweep)
14
+
15
+ before = GC.stat(:heap_swept_slot)
16
+ assert_equal true, GC::OOB.run
17
+ assert_operator GC.stat(:heap_swept_slot), :>, before
18
+ end
19
+
20
+ def test_oob_mark
21
+ oob = 0
22
+ before = GC.count
23
+ minor, major = GC.stat(:minor_gc_count), GC.stat(:major_gc_count)
24
+
25
+ 20.times do
26
+ (10_000 + rand(25_000)).times{ Object.new }
27
+ oob += 1 if GC::OOB.run
28
+ end
29
+
30
+ assert_equal 0, GC.count - before - oob
31
+ assert_equal 0, GC.stat(:minor_gc_count) - minor - oob
32
+ assert_equal major, GC.stat(:major_gc_count)
33
+ end
34
+
35
+ def test_oob_major_mark
36
+ oob = 0
37
+ before = GC.count
38
+ minor, major = GC.stat(:minor_gc_count), GC.stat(:major_gc_count)
39
+ list = []
40
+
41
+ 20.times do
42
+ 2_000.times do
43
+ list << JSON.parse('{"hello":"world", "foo":["bar","bar"], "zap":{"zap":"zap"}}')
44
+ list.shift if list.size > 2_000
45
+ end
46
+ oob += 1 if GC::OOB.run
47
+ end
48
+
49
+ assert_equal oob, GC::OOB.stat(:count)
50
+ assert_equal 1, GC.count - before -
51
+ GC::OOB.stat(:major_count) - GC::OOB.stat(:minor_count) - GC::OOB.stat(:sweep_count)
52
+ end
53
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gctools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Aman Gupta
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake-compiler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ description: gc debugger, logger and profiler for rgengc in ruby 2.1
28
+ email: aman@tmm1.net
29
+ executables: []
30
+ extensions:
31
+ - ext/gcprof/extconf.rb
32
+ - ext/oobgc/extconf.rb
33
+ extra_rdoc_files: []
34
+ files:
35
+ - .gitignore
36
+ - Gemfile
37
+ - Gemfile.lock
38
+ - README.md
39
+ - Rakefile
40
+ - ext/gcprof/extconf.rb
41
+ - ext/gcprof/gcprof.c
42
+ - ext/oobgc/extconf.rb
43
+ - ext/oobgc/oobgc.c
44
+ - gctools.gemspec
45
+ - lib/gctools/logger.rb
46
+ - test/test_gcprof.rb
47
+ - test/test_oobgc.rb
48
+ homepage: https://github.com/tmm1/gctools
49
+ licenses:
50
+ - MIT
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.1.11
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: rgengc tools for ruby 2.1+
72
+ test_files: []
73
+ has_rdoc: