gctrack 0.0.1 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 92838d06f2819fc114876a76cb748508d1d1468d
4
- data.tar.gz: 403295552c868ce6284861af1071cb14350f6bd8
3
+ metadata.gz: 4388d6afd81b4cfb83867cb06a566660b460142d
4
+ data.tar.gz: b8f4b6ed72cee918d87ad60161b08eeae4875645
5
5
  SHA512:
6
- metadata.gz: 5942ef39807b64f69e448325265f9bb2b6a7575de22534073c11d58f19525bc6641beb10b0996a66d758c62e60becfcfa191bddd0a6527c706b640aee3b7ff5e
7
- data.tar.gz: 9f9e5f8c6bb6b976b15d9e09f85e34492245bc7d040b0a6bbe1b167d3854d53d15af4a472d7e4a97631e27a59516980657535b0a7ba3ed7b838f853ed806e7a7
6
+ metadata.gz: 6e52f68e61eab3e758420725acf7f23b124b4c7230e03529291e2710bc53a509ddd5006636c63f885c41971e8a6a6b4911b60a9f59480ad6b339bed0e54fdd61
7
+ data.tar.gz: c138d07a3ac9685280b1bde07397e82d31ba5402a434c0c5ee543841dbe5d8ebbff5a882989c60145a06520cf82a2b0bc944aa007500111c4b41ae33ba0d964a
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gctrack (0.0.1)
4
+ gctrack (0.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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)
@@ -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
- last_enter = nanotime();
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 void
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
- rb_raise(rb_eRuntimeError, "GCTracker: Couldn't create tracepoint!");
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
- rb_raise(rb_eRuntimeError, "GCTracker: Couldn't enable tracepoint!");
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 Qfalse;
228
+ return Qtrue;
142
229
  }
143
230
 
144
231
  rb_tracepoint_disable(tracepoint);
145
232
  if (gctracker_enabled()) {
146
- rb_raise(rb_eRuntimeError, "GCTracker: Couldn't disable tracepoint!");
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);
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'gctrack'
3
- s.version = '0.0.1'
3
+ s.version = '0.1.0'
4
4
  s.summary = 'Track Ruby GC events'
5
5
  s.description = <<-DOC
6
6
  This gem can be used to track Ruby GC tracepoints that are normally only visible through GC extensions.
@@ -0,0 +1 @@
1
+ require 'gctrack/gctrack'
@@ -1,5 +1,5 @@
1
1
  require 'test/unit'
2
- require 'gctrack/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 !GC::Tracker.disable
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 test_no_data_for_started_records_on_disable
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.1
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-07 00:00:00.000000000 Z
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: