gctrack 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +53 -0
- data/ext/gctrack/gctrack.c +99 -18
- data/gctrack.gemspec +1 -1
- data/lib/gctrack.rb +1 -0
- data/test/test_gctrack.rb +13 -7
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4388d6afd81b4cfb83867cb06a566660b460142d
|
4
|
+
data.tar.gz: b8f4b6ed72cee918d87ad60161b08eeae4875645
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e52f68e61eab3e758420725acf7f23b124b4c7230e03529291e2710bc53a509ddd5006636c63f885c41971e8a6a6b4911b60a9f59480ad6b339bed0e54fdd61
|
7
|
+
data.tar.gz: c138d07a3ac9685280b1bde07397e82d31ba5402a434c0c5ee543841dbe5d8ebbff5a882989c60145a06520cf82a2b0bc944aa007500111c4b41ae33ba0d964a
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,2 +1,55 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/gctrack.svg)](https://badge.fury.io/rb/gctrack)
|
2
|
+
[![Build Status](https://travis-ci.org/Shopify/gctrack.svg?branch=master)](https://travis-ci.org/Shopify/gctrack)
|
3
|
+
|
1
4
|
## GCTrack
|
2
5
|
|
6
|
+
The GCTrack extension lets you monitor incremental Garbage Collector (GC) information, the GC introduced in Ruby v2.2.
|
7
|
+
|
8
|
+
### Getting started
|
9
|
+
|
10
|
+
#### #1 Add the dependency
|
11
|
+
|
12
|
+
GCTrack is published to [rubygems.org](https://rubygems.org/gems/gctrack).
|
13
|
+
Simply add it as a dependency to your `Gemfile`
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'gctrack', '~> 0.1.0'
|
17
|
+
```
|
18
|
+
|
19
|
+
#### #2 Enable the extension
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'gctrack'
|
23
|
+
|
24
|
+
GC::Tracker.enable # returns true, if the Tracker is now enabled
|
25
|
+
```
|
26
|
+
|
27
|
+
#### #3 Record data
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
GC::Tracker.start_record # returns true, if a new record was started
|
31
|
+
# DO ACTUAL WORK
|
32
|
+
gc_cycles, gc_duration_ns = GC::Tracker.end_record
|
33
|
+
```
|
34
|
+
|
35
|
+
`#end_record` will return the `gc_cycles` (the amount of gc cycles observed) and `gc_duration_ns` (their cumulative duration in
|
36
|
+
nanoseconds), since the last invocation of `#start_record`. You can also invoke `#start_record` recursively as so:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
GC::Tracker.start_record # Start a first record
|
40
|
+
do_work_on(one)
|
41
|
+
GC::Tracker.start_record # Start a second record
|
42
|
+
do_work_on(two)
|
43
|
+
inner_data = GC::Tracker.end_record # Collect results from second record
|
44
|
+
do_work_on(three)
|
45
|
+
outter_data = GC::Tracker.end_record # Collect results from first record
|
46
|
+
```
|
47
|
+
|
48
|
+
In this example, the Array `inner_data` will only contain the GC information resulted of executing `do_work_on(two)`, while
|
49
|
+
`outter_data` will contain the GC information resulted from the three exectutions: `do_work_on` `one`, `two` and `three`.
|
50
|
+
|
51
|
+
Effectively, records are stacked and data from a GC cycle will be added to all "currently" records.
|
52
|
+
|
53
|
+
## License
|
54
|
+
|
55
|
+
[The MIT License (MIT)](LICENSE.md)
|
data/ext/gctrack/gctrack.c
CHANGED
@@ -49,13 +49,15 @@ gctracker_enabled()
|
|
49
49
|
static void
|
50
50
|
gctracker_hook(VALUE tpval, void *data)
|
51
51
|
{
|
52
|
-
if (!gctracker_enabled()) {
|
52
|
+
if (!gctracker_enabled() && !last_record) {
|
53
53
|
return;
|
54
54
|
}
|
55
55
|
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
56
56
|
switch (rb_tracearg_event_flag(tparg)) {
|
57
57
|
case RUBY_INTERNAL_EVENT_GC_ENTER: {
|
58
|
-
|
58
|
+
if (gctracker_enabled()) {
|
59
|
+
last_enter = nanotime();
|
60
|
+
}
|
59
61
|
}
|
60
62
|
break;
|
61
63
|
case RUBY_INTERNAL_EVENT_GC_EXIT: {
|
@@ -68,18 +70,40 @@ gctracker_hook(VALUE tpval, void *data)
|
|
68
70
|
}
|
69
71
|
}
|
70
72
|
|
71
|
-
static
|
73
|
+
static bool
|
72
74
|
create_tracepoint()
|
73
75
|
{
|
74
76
|
rb_event_flag_t events;
|
75
77
|
events = RUBY_INTERNAL_EVENT_GC_ENTER | RUBY_INTERNAL_EVENT_GC_EXIT;
|
76
78
|
tracepoint = rb_tracepoint_new(0, events, gctracker_hook, (void *) NULL);
|
77
79
|
if (NIL_P(tracepoint)) {
|
78
|
-
|
80
|
+
return false;
|
79
81
|
}
|
80
82
|
rb_global_variable(&tracepoint);
|
83
|
+
return true;
|
81
84
|
}
|
82
85
|
|
86
|
+
/*
|
87
|
+
* call-seq:
|
88
|
+
* GC::Tracker.start_record -> true or false
|
89
|
+
*
|
90
|
+
* Starts recording GC cycles and their duration. Returns +true+ if the
|
91
|
+
* recording was started, +false+ otherwise. If the record could be started
|
92
|
+
* an GC::Tracker#end_record invocation will always return data for the record,
|
93
|
+
* otherwise that invocation would return +nil+.
|
94
|
+
* One case where +false+ would be returned is if the Tracker isn't enabled.
|
95
|
+
* #start_record can be nested. These records will have a parent-child relationship,
|
96
|
+
* where the parent will contain all GC data of the child.
|
97
|
+
*
|
98
|
+
* GC::Tracker.enabled? # true
|
99
|
+
* GC::Tracker.start_record # true
|
100
|
+
* GC::Tracker.start_record # true
|
101
|
+
* GC::Tracker.end_record # [2, 12654] from the second #start_record
|
102
|
+
* GC::Tracker.disable # true
|
103
|
+
* GC::Tracker.start_record # false
|
104
|
+
* GC::Tracker.end_record # [3, 15782] from the first #start_record, contains the data from the first record
|
105
|
+
* GC::Tracker.end_record # nil from the last #start_record, that returned false
|
106
|
+
*/
|
83
107
|
static VALUE
|
84
108
|
gctracker_start_record(int argc, VALUE *argv, VALUE klass)
|
85
109
|
{
|
@@ -97,6 +121,26 @@ gctracker_start_record(int argc, VALUE *argv, VALUE klass)
|
|
97
121
|
return Qtrue;
|
98
122
|
}
|
99
123
|
|
124
|
+
/*
|
125
|
+
* call-seq:
|
126
|
+
* GC::Tracker.end_record -> Array or nil
|
127
|
+
*
|
128
|
+
* Ends the recording of the last started record and return the
|
129
|
+
* the collected information.
|
130
|
+
* The array contains two numbers: The amount of GC cycles observed and
|
131
|
+
* the cumulative duration in nanoseconds of these cycles. If multiple recordings
|
132
|
+
* were started they will be be popped from the stack of records and their
|
133
|
+
* values will be returned.
|
134
|
+
* GC data gathered is in all records of the stack.
|
135
|
+
*
|
136
|
+
* GC::Tracker.start_record # true
|
137
|
+
* GC::Tracker.start_record # true
|
138
|
+
* GC::Tracker.end_record # [2, 12654] amount of cycles and duration since the second #start_record
|
139
|
+
* GC::Tracker.end_record # [3, 15782] GC data since the first #start_record, including the data above
|
140
|
+
* GC::Tracker.disable # true
|
141
|
+
* GC::Tracker.start_record # false
|
142
|
+
* GC::Tracker.end_record # nil
|
143
|
+
*/
|
100
144
|
static VALUE
|
101
145
|
gctracker_end_record(int argc, VALUE *argv, VALUE klass)
|
102
146
|
{
|
@@ -115,43 +159,79 @@ gctracker_end_record(int argc, VALUE *argv, VALUE klass)
|
|
115
159
|
return stats;
|
116
160
|
}
|
117
161
|
|
162
|
+
/*
|
163
|
+
* call-seq:
|
164
|
+
* GC::Tracker.enabled? -> true or false
|
165
|
+
*
|
166
|
+
* Returns +true+ if the Tracker is enabled, +false+ otherwise.
|
167
|
+
*
|
168
|
+
* GC::Tracker.enabled? # false
|
169
|
+
* GC::Tracker.enable # true
|
170
|
+
* GC::Tracker.enabled? # true
|
171
|
+
* GC::Tracker.enable # true
|
172
|
+
*/
|
173
|
+
static VALUE
|
174
|
+
gctracker_enabled_p(int argc, VALUE *argv, VALUE klass)
|
175
|
+
{
|
176
|
+
return gctracker_enabled() ? Qtrue : Qfalse;
|
177
|
+
}
|
178
|
+
|
179
|
+
/*
|
180
|
+
* call-seq:
|
181
|
+
* GC::Tracker.enable -> true or false
|
182
|
+
*
|
183
|
+
* Globally enables the tracker. Returns +true+ if the Tracker is
|
184
|
+
* enabled after this call (whether it enabled it or not), +false+ otherwise.
|
185
|
+
*
|
186
|
+
* GC::Tracker.disable # true
|
187
|
+
* GC::Tracker.enabled? # false
|
188
|
+
* GC::Tracker.enable # true
|
189
|
+
* GC::Tracker.enabled? # true
|
190
|
+
*/
|
118
191
|
static VALUE
|
119
192
|
gctracker_enable(int argc, VALUE *argv, VALUE klass)
|
120
193
|
{
|
121
|
-
if (NIL_P(tracepoint)) {
|
122
|
-
create_tracepoint();
|
123
|
-
}
|
124
|
-
|
125
194
|
if (gctracker_enabled()) {
|
126
195
|
return Qtrue;
|
127
196
|
}
|
128
197
|
|
198
|
+
if (NIL_P(tracepoint)) {
|
199
|
+
if(!create_tracepoint()) {
|
200
|
+
return Qfalse;
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
129
204
|
rb_tracepoint_enable(tracepoint);
|
130
205
|
if (!gctracker_enabled()) {
|
131
|
-
|
206
|
+
return Qfalse;
|
132
207
|
}
|
133
208
|
|
134
209
|
return Qtrue;
|
135
210
|
}
|
136
211
|
|
212
|
+
/*
|
213
|
+
* call-seq:
|
214
|
+
* GC::Tracker.disable -> true or false
|
215
|
+
*
|
216
|
+
* Globally disables the tracker. Returns +true+ if the Tracker is
|
217
|
+
* disabled after this call (whether it disabled it or not), +false+ otherwise.
|
218
|
+
*
|
219
|
+
* GC::Tracker.enabled? # true
|
220
|
+
* GC::Tracker.disable # true
|
221
|
+
* GC::Tracker.enabled? # false
|
222
|
+
* GC::Tracker.disable # true
|
223
|
+
*/
|
137
224
|
static VALUE
|
138
225
|
gctracker_disable(VALUE self)
|
139
226
|
{
|
140
227
|
if (!gctracker_enabled()) {
|
141
|
-
return
|
228
|
+
return Qtrue;
|
142
229
|
}
|
143
230
|
|
144
231
|
rb_tracepoint_disable(tracepoint);
|
145
232
|
if (gctracker_enabled()) {
|
146
|
-
|
233
|
+
return Qfalse;
|
147
234
|
}
|
148
|
-
|
149
|
-
while (last_record) {
|
150
|
-
record_t *record = last_record;
|
151
|
-
last_record = record->parent;
|
152
|
-
free(record);
|
153
|
-
}
|
154
|
-
last_record = NULL;
|
155
235
|
|
156
236
|
return Qtrue;
|
157
237
|
}
|
@@ -163,6 +243,7 @@ Init_gctrack()
|
|
163
243
|
VALUE cTracker = rb_define_module_under(mGC, "Tracker");
|
164
244
|
|
165
245
|
rb_define_module_function(cTracker, "enable", gctracker_enable, 0);
|
246
|
+
rb_define_module_function(cTracker, "enabled?", gctracker_enabled_p, 0);
|
166
247
|
rb_define_module_function(cTracker, "disable", gctracker_disable, 0);
|
167
248
|
|
168
249
|
rb_define_module_function(cTracker, "start_record", gctracker_start_record, 0);
|
data/gctrack.gemspec
CHANGED
data/lib/gctrack.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'gctrack/gctrack'
|
data/test/test_gctrack.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require 'gctrack
|
2
|
+
require 'gctrack'
|
3
3
|
|
4
4
|
class TestGctrack < Test::Unit::TestCase
|
5
5
|
def test_enable
|
@@ -9,15 +9,11 @@ class TestGctrack < Test::Unit::TestCase
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_disable
|
12
|
-
assert
|
12
|
+
assert GC::Tracker.disable
|
13
13
|
assert GC::Tracker.enable
|
14
14
|
assert GC::Tracker.disable
|
15
15
|
end
|
16
16
|
|
17
|
-
def test_returns_false_when_not_enabled
|
18
|
-
assert_equal false, GC::Tracker.disable
|
19
|
-
end
|
20
|
-
|
21
17
|
def test_enabled_generates_events
|
22
18
|
assert GC::Tracker.enable
|
23
19
|
assert GC::Tracker.start_record
|
@@ -51,10 +47,20 @@ class TestGctrack < Test::Unit::TestCase
|
|
51
47
|
assert GC::Tracker.end_record.nil?
|
52
48
|
end
|
53
49
|
|
54
|
-
def
|
50
|
+
def test_enabled_returns_accrodingly
|
51
|
+
assert !GC::Tracker.enabled?
|
52
|
+
GC::Tracker.enable
|
53
|
+
assert GC::Tracker.enabled?
|
54
|
+
ensure
|
55
|
+
GC::Tracker.disable
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_data_for_started_records_on_disable
|
55
59
|
assert GC::Tracker.enable
|
56
60
|
assert GC::Tracker.start_record
|
57
61
|
assert GC::Tracker.disable
|
62
|
+
assert !GC::Tracker.start_record
|
63
|
+
assert !GC::Tracker.end_record.nil?
|
58
64
|
assert GC::Tracker.end_record.nil?
|
59
65
|
ensure
|
60
66
|
GC::Tracker.disable
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gctrack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Francis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-09-
|
12
|
+
date: 2017-09-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake-compiler
|
@@ -59,6 +59,7 @@ files:
|
|
59
59
|
- ext/gctrack/extconf.rb
|
60
60
|
- ext/gctrack/gctrack.c
|
61
61
|
- gctrack.gemspec
|
62
|
+
- lib/gctrack.rb
|
62
63
|
- test/test_gctrack.rb
|
63
64
|
homepage: https://github.com/Shopify/gctrack
|
64
65
|
licenses:
|