gctools 0.2.1

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.
@@ -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: