gctrack 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.travis.yml +5 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +40 -0
- data/LICENSE.md +21 -0
- data/README.md +2 -0
- data/Rakefile +34 -0
- data/ext/gctrack/extconf.rb +5 -0
- data/ext/gctrack/gctrack.c +170 -0
- data/gctrack.gemspec +17 -0
- data/test/test_gctrack.rb +63 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 92838d06f2819fc114876a76cb748508d1d1468d
|
4
|
+
data.tar.gz: 403295552c868ce6284861af1071cb14350f6bd8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5942ef39807b64f69e448325265f9bb2b6a7575de22534073c11d58f19525bc6641beb10b0996a66d758c62e60becfcfa191bddd0a6527c706b640aee3b7ff5e
|
7
|
+
data.tar.gz: 9f9e5f8c6bb6b976b15d9e09f85e34492245bc7d040b0a6bbe1b167d3854d53d15af4a472d7e4a97631e27a59516980657535b0a7ba3ed7b838f853ed806e7a7
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
gctrack (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
highline (1.6.20)
|
10
|
+
json_pure (1.8.1)
|
11
|
+
mime-types (1.25.1)
|
12
|
+
package_cloud (0.2.44)
|
13
|
+
highline (= 1.6.20)
|
14
|
+
json_pure (= 1.8.1)
|
15
|
+
rainbow (= 2.1.0)
|
16
|
+
rest-client (~> 1.6.7)
|
17
|
+
thor (~> 0.18)
|
18
|
+
power_assert (1.0.2)
|
19
|
+
rainbow (2.1.0)
|
20
|
+
rake (12.0.0)
|
21
|
+
rake-compiler (1.0.4)
|
22
|
+
rake
|
23
|
+
rest-client (1.6.9)
|
24
|
+
mime-types (~> 1.16)
|
25
|
+
test-unit (3.2.5)
|
26
|
+
power_assert
|
27
|
+
thor (0.20.0)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
gctrack!
|
34
|
+
package_cloud
|
35
|
+
rake
|
36
|
+
rake-compiler (~> 1.0)
|
37
|
+
test-unit
|
38
|
+
|
39
|
+
BUNDLED WITH
|
40
|
+
1.15.3
|
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Scott Francis <scott.francis@shopify.com>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
task :default => :test
|
2
|
+
|
3
|
+
# ==========================================================
|
4
|
+
# Packaging
|
5
|
+
# ==========================================================
|
6
|
+
|
7
|
+
GEMSPEC = eval(File.read('gctrack.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('gctrack', GEMSPEC) do |ext|
|
19
|
+
ext.ext_dir = 'ext/gctrack'
|
20
|
+
ext.lib_dir = 'lib/gctrack'
|
21
|
+
end
|
22
|
+
task :build => :compile
|
23
|
+
|
24
|
+
# ==========================================================
|
25
|
+
# Testing
|
26
|
+
# ==========================================================
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new 'test' do |t|
|
30
|
+
t.test_files = FileList['test/test_*.rb']
|
31
|
+
end
|
32
|
+
task :test => :build
|
33
|
+
|
34
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,170 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <ruby/debug.h>
|
3
|
+
#include <ruby/intern.h>
|
4
|
+
#include <stdbool.h>
|
5
|
+
#include <errno.h>
|
6
|
+
#include <time.h>
|
7
|
+
#include <stdlib.h>
|
8
|
+
|
9
|
+
typedef struct record_t record_t;
|
10
|
+
|
11
|
+
struct record_t {
|
12
|
+
uint32_t cycles;
|
13
|
+
uint64_t duration;
|
14
|
+
record_t *parent;
|
15
|
+
};
|
16
|
+
|
17
|
+
static VALUE tracepoint = Qnil;
|
18
|
+
|
19
|
+
static record_t *last_record = NULL;
|
20
|
+
static uint64_t last_enter = 0;
|
21
|
+
|
22
|
+
static uint64_t
|
23
|
+
nanotime()
|
24
|
+
{
|
25
|
+
struct timespec ts;
|
26
|
+
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
|
27
|
+
rb_sys_fail("clock_gettime");
|
28
|
+
}
|
29
|
+
return ts.tv_sec * (uint64_t) 1000000000 + ts.tv_nsec;
|
30
|
+
}
|
31
|
+
|
32
|
+
static inline void
|
33
|
+
add_gc_cycle(uint64_t duration)
|
34
|
+
{
|
35
|
+
record_t *record = last_record;
|
36
|
+
while (record) {
|
37
|
+
record->cycles = record->cycles + 1;
|
38
|
+
record->duration = record->duration + duration;
|
39
|
+
record = record->parent;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
static inline bool
|
44
|
+
gctracker_enabled()
|
45
|
+
{
|
46
|
+
return !NIL_P(tracepoint) && rb_tracepoint_enabled_p(tracepoint);
|
47
|
+
}
|
48
|
+
|
49
|
+
static void
|
50
|
+
gctracker_hook(VALUE tpval, void *data)
|
51
|
+
{
|
52
|
+
if (!gctracker_enabled()) {
|
53
|
+
return;
|
54
|
+
}
|
55
|
+
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
56
|
+
switch (rb_tracearg_event_flag(tparg)) {
|
57
|
+
case RUBY_INTERNAL_EVENT_GC_ENTER: {
|
58
|
+
last_enter = nanotime();
|
59
|
+
}
|
60
|
+
break;
|
61
|
+
case RUBY_INTERNAL_EVENT_GC_EXIT: {
|
62
|
+
if (last_enter) {
|
63
|
+
add_gc_cycle(nanotime() - last_enter);
|
64
|
+
}
|
65
|
+
last_enter = 0;
|
66
|
+
}
|
67
|
+
break;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
static void
|
72
|
+
create_tracepoint()
|
73
|
+
{
|
74
|
+
rb_event_flag_t events;
|
75
|
+
events = RUBY_INTERNAL_EVENT_GC_ENTER | RUBY_INTERNAL_EVENT_GC_EXIT;
|
76
|
+
tracepoint = rb_tracepoint_new(0, events, gctracker_hook, (void *) NULL);
|
77
|
+
if (NIL_P(tracepoint)) {
|
78
|
+
rb_raise(rb_eRuntimeError, "GCTracker: Couldn't create tracepoint!");
|
79
|
+
}
|
80
|
+
rb_global_variable(&tracepoint);
|
81
|
+
}
|
82
|
+
|
83
|
+
static VALUE
|
84
|
+
gctracker_start_record(int argc, VALUE *argv, VALUE klass)
|
85
|
+
{
|
86
|
+
if(!gctracker_enabled()) {
|
87
|
+
return Qfalse;
|
88
|
+
}
|
89
|
+
|
90
|
+
record_t *record = (record_t *) calloc(1, sizeof(record_t));
|
91
|
+
if (!record) {
|
92
|
+
return Qfalse;
|
93
|
+
}
|
94
|
+
|
95
|
+
record->parent = last_record;
|
96
|
+
last_record = record;
|
97
|
+
return Qtrue;
|
98
|
+
}
|
99
|
+
|
100
|
+
static VALUE
|
101
|
+
gctracker_end_record(int argc, VALUE *argv, VALUE klass)
|
102
|
+
{
|
103
|
+
if (!last_record) {
|
104
|
+
return Qnil;
|
105
|
+
}
|
106
|
+
record_t *record = last_record;
|
107
|
+
last_record = record->parent;
|
108
|
+
|
109
|
+
VALUE stats = rb_ary_new2(2);
|
110
|
+
rb_ary_store(stats, 0, ULONG2NUM(record->cycles));
|
111
|
+
rb_ary_store(stats, 1, ULONG2NUM(record->duration));
|
112
|
+
|
113
|
+
free(record);
|
114
|
+
|
115
|
+
return stats;
|
116
|
+
}
|
117
|
+
|
118
|
+
static VALUE
|
119
|
+
gctracker_enable(int argc, VALUE *argv, VALUE klass)
|
120
|
+
{
|
121
|
+
if (NIL_P(tracepoint)) {
|
122
|
+
create_tracepoint();
|
123
|
+
}
|
124
|
+
|
125
|
+
if (gctracker_enabled()) {
|
126
|
+
return Qtrue;
|
127
|
+
}
|
128
|
+
|
129
|
+
rb_tracepoint_enable(tracepoint);
|
130
|
+
if (!gctracker_enabled()) {
|
131
|
+
rb_raise(rb_eRuntimeError, "GCTracker: Couldn't enable tracepoint!");
|
132
|
+
}
|
133
|
+
|
134
|
+
return Qtrue;
|
135
|
+
}
|
136
|
+
|
137
|
+
static VALUE
|
138
|
+
gctracker_disable(VALUE self)
|
139
|
+
{
|
140
|
+
if (!gctracker_enabled()) {
|
141
|
+
return Qfalse;
|
142
|
+
}
|
143
|
+
|
144
|
+
rb_tracepoint_disable(tracepoint);
|
145
|
+
if (gctracker_enabled()) {
|
146
|
+
rb_raise(rb_eRuntimeError, "GCTracker: Couldn't disable tracepoint!");
|
147
|
+
}
|
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
|
+
|
156
|
+
return Qtrue;
|
157
|
+
}
|
158
|
+
|
159
|
+
void
|
160
|
+
Init_gctrack()
|
161
|
+
{
|
162
|
+
VALUE mGC = rb_define_module("GC");
|
163
|
+
VALUE cTracker = rb_define_module_under(mGC, "Tracker");
|
164
|
+
|
165
|
+
rb_define_module_function(cTracker, "enable", gctracker_enable, 0);
|
166
|
+
rb_define_module_function(cTracker, "disable", gctracker_disable, 0);
|
167
|
+
|
168
|
+
rb_define_module_function(cTracker, "start_record", gctracker_start_record, 0);
|
169
|
+
rb_define_module_function(cTracker, "end_record", gctracker_end_record, 0);
|
170
|
+
}
|
data/gctrack.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'gctrack'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.summary = 'Track Ruby GC events'
|
5
|
+
s.description = <<-DOC
|
6
|
+
This gem can be used to track Ruby GC tracepoints that are normally only visible through GC extensions.
|
7
|
+
DOC
|
8
|
+
s.homepage = 'https://github.com/Shopify/gctrack'
|
9
|
+
s.authors = ['Scott Francis', 'Alex Snaps']
|
10
|
+
s.email = ['scott.francis@shopify.com', 'alex.snaps@shopify.com']
|
11
|
+
s.license = 'MIT'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.extensions = ['ext/gctrack/extconf.rb']
|
15
|
+
s.add_development_dependency 'rake-compiler', '~> 1.0'
|
16
|
+
s.add_development_dependency 'test-unit'
|
17
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'gctrack/gctrack'
|
3
|
+
|
4
|
+
class TestGctrack < Test::Unit::TestCase
|
5
|
+
def test_enable
|
6
|
+
assert GC::Tracker.enable
|
7
|
+
ensure
|
8
|
+
GC::Tracker.disable
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_disable
|
12
|
+
assert !GC::Tracker.disable
|
13
|
+
assert GC::Tracker.enable
|
14
|
+
assert GC::Tracker.disable
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_returns_false_when_not_enabled
|
18
|
+
assert_equal false, GC::Tracker.disable
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_enabled_generates_events
|
22
|
+
assert GC::Tracker.enable
|
23
|
+
assert GC::Tracker.start_record
|
24
|
+
GC.start
|
25
|
+
cycles, duration = GC::Tracker.end_record
|
26
|
+
assert cycles > 0
|
27
|
+
assert duration > 0
|
28
|
+
ensure
|
29
|
+
GC::Tracker.disable
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_recurses
|
33
|
+
assert GC::Tracker.enable
|
34
|
+
assert GC::Tracker.start_record
|
35
|
+
a = "a"
|
36
|
+
10.times { |i| a += a * i }
|
37
|
+
GC.start
|
38
|
+
assert GC::Tracker.start_record
|
39
|
+
a = "a"
|
40
|
+
10.times { |i| a += a * i }
|
41
|
+
cycles_c, duration_c = GC::Tracker.end_record
|
42
|
+
cycles_p, duration_p = GC::Tracker.end_record
|
43
|
+
assert cycles_p > cycles_c
|
44
|
+
assert duration_p > duration_c
|
45
|
+
ensure
|
46
|
+
GC::Tracker.disable
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_no_data_when_disabled
|
50
|
+
assert !GC::Tracker.start_record
|
51
|
+
assert GC::Tracker.end_record.nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_no_data_for_started_records_on_disable
|
55
|
+
assert GC::Tracker.enable
|
56
|
+
assert GC::Tracker.start_record
|
57
|
+
assert GC::Tracker.disable
|
58
|
+
assert GC::Tracker.end_record.nil?
|
59
|
+
ensure
|
60
|
+
GC::Tracker.disable
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gctrack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Scott Francis
|
8
|
+
- Alex Snaps
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-09-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake-compiler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: test-unit
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
description: " This gem can be used to track Ruby GC tracepoints that are normally
|
43
|
+
only visible through GC extensions.\n"
|
44
|
+
email:
|
45
|
+
- scott.francis@shopify.com
|
46
|
+
- alex.snaps@shopify.com
|
47
|
+
executables: []
|
48
|
+
extensions:
|
49
|
+
- ext/gctrack/extconf.rb
|
50
|
+
extra_rdoc_files: []
|
51
|
+
files:
|
52
|
+
- ".gitignore"
|
53
|
+
- ".travis.yml"
|
54
|
+
- Gemfile
|
55
|
+
- Gemfile.lock
|
56
|
+
- LICENSE.md
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- ext/gctrack/extconf.rb
|
60
|
+
- ext/gctrack/gctrack.c
|
61
|
+
- gctrack.gemspec
|
62
|
+
- test/test_gctrack.rb
|
63
|
+
homepage: https://github.com/Shopify/gctrack
|
64
|
+
licenses:
|
65
|
+
- MIT
|
66
|
+
metadata: {}
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 2.5.2
|
84
|
+
signing_key:
|
85
|
+
specification_version: 4
|
86
|
+
summary: Track Ruby GC events
|
87
|
+
test_files: []
|