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