gvltools 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 42d6e3f8ea2cc420e17a413a62fba51ccb6c2b90dac11138186e9f4d203bed51
4
+ data.tar.gz: 97576f44774bb991699152d36140714b792e17ced8a7ea48d1210f0c8d94b1df
5
+ SHA512:
6
+ metadata.gz: ae23c00ab1f94c2af17753f5ba847690ebea31cb0d3f23134080ad34a475686ac0d9872fa56025a1fab8135c5ee57c58e19675bbc08160c9444a8d30edb847db
7
+ data.tar.gz: 54e1694fd5d16935c98a6b838fdf188427cf645f0fe00ac08a7323609c70f01721d9836b80c6bdf2096ee7341d4d39a44f713b20a770470072c72c48887564c3
data/.rubocop.yml ADDED
@@ -0,0 +1,41 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+
6
+ Lint/DuplicateMethods:
7
+ Exclude:
8
+ - lib/gvltools.rb # we need the self alias trick to avoid warnings
9
+
10
+ Style/Alias:
11
+ EnforcedStyle: prefer_alias_method
12
+
13
+ Style/StringLiterals:
14
+ Enabled: true
15
+ EnforcedStyle: double_quotes
16
+
17
+ Style/StringLiteralsInInterpolation:
18
+ Enabled: true
19
+ EnforcedStyle: double_quotes
20
+
21
+ Style/GlobalVars:
22
+ Exclude:
23
+ - ext/**/extconf.rb
24
+
25
+ Style/Documentation:
26
+ Enabled: false
27
+
28
+ Layout/LineLength:
29
+ Max: 120
30
+
31
+ Gemspec/RequiredRubyVersion:
32
+ Enabled: false
33
+
34
+ Gemspec/RequireMFA:
35
+ Enabled: false
36
+
37
+ Metrics/AbcSize:
38
+ Enabled: false
39
+
40
+ Metrics/MethodLength:
41
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-06-14
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in gvltools.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
11
+
12
+ gem "rubocop", "~> 1.21"
data/Gemfile.lock ADDED
@@ -0,0 +1,47 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gvltools (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ minitest (5.15.0)
11
+ parallel (1.22.1)
12
+ parser (3.1.2.0)
13
+ ast (~> 2.4.1)
14
+ rainbow (3.1.1)
15
+ rake (13.0.6)
16
+ rake-compiler (1.2.0)
17
+ rake
18
+ regexp_parser (2.5.0)
19
+ rexml (3.2.5)
20
+ rubocop (1.30.1)
21
+ parallel (~> 1.10)
22
+ parser (>= 3.1.0.0)
23
+ rainbow (>= 2.2.2, < 4.0)
24
+ regexp_parser (>= 1.8, < 3.0)
25
+ rexml (>= 3.2.5, < 4.0)
26
+ rubocop-ast (>= 1.18.0, < 2.0)
27
+ ruby-progressbar (~> 1.7)
28
+ unicode-display_width (>= 1.4.0, < 3.0)
29
+ rubocop-ast (1.18.0)
30
+ parser (>= 3.1.1.0)
31
+ ruby-progressbar (1.11.0)
32
+ unicode-display_width (2.1.0)
33
+
34
+ PLATFORMS
35
+ arm64-darwin-21
36
+ arm64-darwin-22
37
+ x86_64-linux
38
+
39
+ DEPENDENCIES
40
+ gvltools!
41
+ minitest (~> 5.0)
42
+ rake (~> 13.0)
43
+ rake-compiler
44
+ rubocop (~> 1.21)
45
+
46
+ BUNDLED WITH
47
+ 2.3.14
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Shopify
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # GVLTools
2
+
3
+ Set of GVL instrumentation tools
4
+
5
+ ## Requirements
6
+
7
+ GVLTools uses the GVL instrumentation API added in Ruby 3.2.0.
8
+
9
+ To make it easier to use, on older Rubies the gem will install and expose the same methods, but they won't have any effect and all metrics will report `0`.
10
+
11
+ ## Installation
12
+
13
+ Install the gem and add to the application's Gemfile by executing:
14
+
15
+ $ bundle add gvltools
16
+
17
+ If bundler is not being used to manage dependencies, install the gem by executing:
18
+
19
+ $ gem install gvltools
20
+
21
+ ## Usage
22
+
23
+ GVLTools expose metric modules with a common interface, for instance `GVLTools::LocalTimer`:
24
+
25
+ ```ruby
26
+ GVLTools::LocalTimer.enable
27
+ GVLTools::LocalTimer.disable
28
+ GVLTools::LocalTimer.enabled? # => true / false
29
+ GVLTools::LocalTimer.reset
30
+ ```
31
+
32
+ By default, all metrics are disabled.
33
+
34
+ Note that using the GVL instrumentation API adds some overhead each time Ruby has to switch threads.
35
+ In production it is recommended to only enable it on a subset of processes or for small periods of time as to not impact
36
+ the average latency too much.
37
+
38
+ The exact overhead is not yet known, early testing showed a `1-5%` slowdown on a fully saturated multi-threaded app.
39
+
40
+ ### LocalTimer
41
+
42
+ `LocalTimer` records the overall time spent waiting on the GVL by the current thread.
43
+ It is particularly useful to detect wether an application use too many threads, and how much latency is impacted.
44
+ For instance as a Rack middleware:
45
+
46
+ ```ruby
47
+ class GVLInstrumentationMiddleware
48
+ def initialize(app)
49
+ @app = app
50
+ end
51
+
52
+ def call(env)
53
+ before = GVLTools::LocalTimer.monotonic_time
54
+ response = @app.call(env)
55
+ diff = GVLTools::LocalTimer.monotonic_time - before
56
+ puts "Waited #{diff} nanoseconds on the GVL"
57
+ response
58
+ end
59
+ end
60
+ ```
61
+
62
+ ### GlobalTimer
63
+
64
+ `GlobalTimer` records the overall time spent waiting on the GVL by all threads combined.
65
+
66
+ ```ruby
67
+ def fibonacci(number)
68
+ number <= 1 ? number : fibonacci(number - 1) + fibonacci(number - 2)
69
+ end
70
+
71
+ before = GVLTools::GlobalTimer.monotonic_time
72
+ threads = 5.times.map do
73
+ Thread.new do
74
+ 5.times do
75
+ fibonacci(30)
76
+ end
77
+ end
78
+ end
79
+ threads.each(&:join)
80
+ diff = GVLTools::GlobalTimer.monotonic_time - before
81
+ puts "Waited #{(diff / 1_000_000.0).round(1)}ms on the GVL"
82
+ ```
83
+
84
+ outputs:
85
+
86
+ ```
87
+ Waited 4122.8ms on the GVL
88
+ ```
89
+
90
+ ### WaitingThreads
91
+
92
+ `WaitingThreads` records how many threads are currently waiting on the GVL to start executing code.
93
+
94
+ ```ruby
95
+ def fibonacci(number)
96
+ number <= 1 ? number : fibonacci(number - 1) + fibonacci(number - 2)
97
+ end
98
+
99
+ Thread.new do
100
+ 10.times do
101
+ sleep 0.001
102
+ p GVLTools::WaitingThreads.count
103
+ end
104
+ end
105
+
106
+ threads = 5.times.map do
107
+ Thread.new do
108
+ 5.times do
109
+ fibonacci(30)
110
+ end
111
+ end
112
+ end
113
+ threads.each(&:join)
114
+ ```
115
+
116
+ Outputs:
117
+
118
+ ```
119
+ 5
120
+ 5
121
+ 4
122
+ ```
123
+
124
+ It's less precise than timers, but the instrumentation overhead is lower.
125
+
126
+ ## Development
127
+
128
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
129
+
130
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
131
+
132
+ ## Contributing
133
+
134
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/gvltools.
135
+
136
+ ## License
137
+
138
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ has_ext = RUBY_ENGINE == "ruby" && RUBY_VERSION >= "3.2"
7
+
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.libs << "test"
10
+ t.libs << "lib"
11
+ t.test_files = has_ext ? FileList["test/**/test_*.rb"] : FileList["test/fallback/**/test_*.rb"]
12
+ t.warning = true
13
+ t.verbose = true
14
+ end
15
+
16
+ if has_ext
17
+ require "rake/extensiontask"
18
+
19
+ Rake::ExtensionTask.new("instrumentation") do |ext|
20
+ ext.ext_dir = "ext/gvltools"
21
+ ext.lib_dir = "lib/gvltools"
22
+ end
23
+ else
24
+ task :compile do
25
+ # noop
26
+ end
27
+
28
+ task :clean do
29
+ # noop
30
+ end
31
+ end
32
+
33
+ Rake::Task["test"].enhance(["compile"])
34
+
35
+ require "rubocop/rake_task"
36
+
37
+ RuboCop::RakeTask.new
38
+
39
+ task default: %i[test rubocop]
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+ if RUBY_ENGINE == "ruby" && have_func("rb_internal_thread_add_event_hook", ["ruby/thread.h"])
5
+ $CFLAGS << " -O3 -Wall "
6
+ create_makefile("gvltools/instrumentation")
7
+ else
8
+ File.write("Makefile", dummy_makefile($srcdir).join)
9
+ end
@@ -0,0 +1,175 @@
1
+ #include <time.h>
2
+ #include <ruby.h>
3
+ #include <ruby/thread.h>
4
+ #include <ruby/atomic.h>
5
+
6
+ // Metrics
7
+ static rb_internal_thread_event_hook_t *gt_hook = NULL;
8
+
9
+ static unsigned int enabled_mask = 0;
10
+ #define TIMER_GLOBAL 1 << 0
11
+ #define TIMER_LOCAL 1 << 1
12
+ #define WAITING_THREADS 1 << 2
13
+
14
+ #define ENABLED(metric) (enabled_mask & (metric))
15
+
16
+ #if __STDC_VERSION__ >= 201112
17
+ #define THREAD_LOCAL_SPECIFIER _Thread_local
18
+ #elif defined(__GNUC__) && !defined(RB_THREAD_LOCAL_SPECIFIER_IS_UNSUPPORTED)
19
+ /* note that ICC (linux) and Clang are covered by __GNUC__ */
20
+ #define THREAD_LOCAL_SPECIFIER __thread
21
+ #endif
22
+
23
+ // Common
24
+ #define SECONDS_TO_NANOSECONDS (1000 * 1000 * 1000)
25
+
26
+ static THREAD_LOCAL_SPECIFIER bool was_ready = 0;
27
+
28
+ static inline void gt_gettime(struct timespec *time) {
29
+ if (clock_gettime(CLOCK_MONOTONIC, time) == -1) {
30
+ rb_sys_fail("clock_gettime");
31
+ }
32
+ }
33
+
34
+ static inline rb_atomic_t gt_time_diff_ns(struct timespec before, struct timespec after) {
35
+ rb_atomic_t total = 0;
36
+ total += (after.tv_nsec - before.tv_nsec);
37
+ total += (after.tv_sec - before.tv_sec) * SECONDS_TO_NANOSECONDS;
38
+ return total;
39
+ }
40
+
41
+ static VALUE gt_metric_enabled_p(VALUE module, VALUE metric) {
42
+ return ENABLED(NUM2UINT(metric)) ? Qtrue : Qfalse;
43
+ }
44
+
45
+ static void gt_thread_callback(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *user_data);
46
+ static VALUE gt_enable_metric(VALUE module, VALUE metric) {
47
+ enabled_mask |= NUM2UINT(metric);
48
+ if (!gt_hook) {
49
+ gt_hook = rb_internal_thread_add_event_hook(
50
+ gt_thread_callback,
51
+ RUBY_INTERNAL_THREAD_EVENT_EXITED | RUBY_INTERNAL_THREAD_EVENT_READY | RUBY_INTERNAL_THREAD_EVENT_RESUMED,
52
+ NULL
53
+ );
54
+ }
55
+ return Qtrue;
56
+ }
57
+
58
+ static VALUE gt_disable_metric(VALUE module, VALUE metric) {
59
+ if (ENABLED(NUM2UINT(metric))) {
60
+ enabled_mask -= NUM2UINT(metric);
61
+ }
62
+ if (!enabled_mask && gt_hook) {
63
+ rb_internal_thread_remove_event_hook(gt_hook);
64
+ gt_hook = NULL;
65
+ }
66
+ return Qtrue;
67
+ }
68
+
69
+ // GVLTools::LocalTimer and GVLTools::GlobalTimer
70
+ static rb_atomic_t global_timer_total = 0;
71
+ static THREAD_LOCAL_SPECIFIER uint64_t local_timer_total = 0;
72
+ static THREAD_LOCAL_SPECIFIER struct timespec timer_ready_at = {0};
73
+
74
+ static VALUE global_timer_monotonic_time(VALUE module) {
75
+ return UINT2NUM(global_timer_total);
76
+ }
77
+
78
+ static VALUE global_timer_reset(VALUE module) {
79
+ RUBY_ATOMIC_SET(global_timer_total, 0);
80
+ return Qtrue;
81
+ }
82
+
83
+ static VALUE local_timer_monotonic_time(VALUE module) {
84
+ return ULL2NUM(local_timer_total);
85
+ }
86
+
87
+ static VALUE local_timer_reset(VALUE module) {
88
+ local_timer_total = 0;
89
+ return Qtrue;
90
+ }
91
+
92
+ // Thread counts
93
+ static rb_atomic_t waiting_threads_total = 0;
94
+
95
+ static VALUE waiting_threads_count(VALUE module) {
96
+ return UINT2NUM(waiting_threads_total);
97
+ }
98
+
99
+ static VALUE waiting_threads_reset(VALUE module) {
100
+ RUBY_ATOMIC_SET(waiting_threads_total, 0);
101
+ return Qtrue;
102
+ }
103
+
104
+ // General callback
105
+ static void gt_reset_thread_local_state(void) {
106
+ // MRI can re-use native threads, so we need to reset thread local state,
107
+ // otherwise it will leak from one Ruby thread from another.
108
+ was_ready = false;
109
+ local_timer_total = 0;
110
+ }
111
+
112
+ static void gt_thread_callback(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *user_data) {
113
+ switch(event) {
114
+ case RUBY_INTERNAL_THREAD_EVENT_STARTED:
115
+ case RUBY_INTERNAL_THREAD_EVENT_EXITED: {
116
+ gt_reset_thread_local_state();
117
+ }
118
+ break;
119
+ case RUBY_INTERNAL_THREAD_EVENT_READY: {
120
+ if (!was_ready) was_ready = true;
121
+
122
+ if (ENABLED(WAITING_THREADS)) {
123
+ RUBY_ATOMIC_INC(waiting_threads_total);
124
+ }
125
+
126
+ if (ENABLED(TIMER_GLOBAL | TIMER_LOCAL)) {
127
+ gt_gettime(&timer_ready_at);
128
+ }
129
+ }
130
+ break;
131
+ case RUBY_INTERNAL_THREAD_EVENT_RESUMED: {
132
+ if (!was_ready) break; // In case we registered the hook while some threads were already waiting on the GVL
133
+
134
+ if (ENABLED(WAITING_THREADS)) {
135
+ RUBY_ATOMIC_DEC(waiting_threads_total);
136
+ }
137
+
138
+ if (ENABLED(TIMER_GLOBAL | TIMER_LOCAL)) {
139
+ struct timespec current_time;
140
+ gt_gettime(&current_time);
141
+ rb_atomic_t diff = gt_time_diff_ns(timer_ready_at, current_time);
142
+
143
+ if (ENABLED(TIMER_LOCAL)) {
144
+ local_timer_total += diff;
145
+ }
146
+
147
+ if (ENABLED(TIMER_GLOBAL)) {
148
+ RUBY_ATOMIC_ADD(global_timer_total, diff);
149
+ }
150
+ }
151
+ }
152
+ break;
153
+ }
154
+ }
155
+
156
+ void Init_instrumentation(void) {
157
+ VALUE rb_mGVLTools = rb_const_get(rb_cObject, rb_intern("GVLTools"));
158
+
159
+ VALUE rb_mNative = rb_const_get(rb_mGVLTools, rb_intern("Native"));
160
+ rb_define_singleton_method(rb_mNative, "enable_metric", gt_enable_metric, 1);
161
+ rb_define_singleton_method(rb_mNative, "disable_metric", gt_disable_metric, 1);
162
+ rb_define_singleton_method(rb_mNative, "metric_enabled?", gt_metric_enabled_p, 1);
163
+
164
+ VALUE rb_mGlobalTimer = rb_const_get(rb_mGVLTools, rb_intern("GlobalTimer"));
165
+ rb_define_singleton_method(rb_mGlobalTimer, "reset", global_timer_reset, 0);
166
+ rb_define_singleton_method(rb_mGlobalTimer, "monotonic_time", global_timer_monotonic_time, 0);
167
+
168
+ VALUE rb_mLocalTimer = rb_const_get(rb_mGVLTools, rb_intern("LocalTimer"));
169
+ rb_define_singleton_method(rb_mLocalTimer, "reset", local_timer_reset, 0);
170
+ rb_define_singleton_method(rb_mLocalTimer, "monotonic_time", local_timer_monotonic_time, 0);
171
+
172
+ VALUE rb_mWaitingThreads = rb_const_get(rb_mGVLTools, rb_intern("WaitingThreads"));
173
+ rb_define_singleton_method(rb_mWaitingThreads, "reset", waiting_threads_reset, 0);
174
+ rb_define_singleton_method(rb_mWaitingThreads, "count", waiting_threads_count, 0);
175
+ }
data/gvltools.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/gvltools/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "gvltools"
7
+ spec.version = GVLTools::VERSION
8
+ spec.authors = ["Jean Boussier"]
9
+ spec.email = ["jean.boussier@gmail.com"]
10
+
11
+ spec.summary = "Set of GVL instrumentation tools"
12
+ spec.homepage = "https://github.com/Shopify/gvltools"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = spec.homepage
20
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
27
+ end
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+ spec.extensions = ["ext/gvltools/extconf.rb"]
33
+
34
+ spec.add_development_dependency "rake-compiler"
35
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GVLTools
4
+ VERSION = "0.1.0"
5
+ end
data/lib/gvltools.rb ADDED
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "gvltools/version"
4
+
5
+ module GVLTools
6
+ class Error < StandardError; end
7
+
8
+ module Native
9
+ class << self
10
+ def enable_metric(_metric)
11
+ false
12
+ end
13
+ alias_method :enable_metric, :enable_metric
14
+
15
+ def disable_metric(_metric)
16
+ false
17
+ end
18
+ alias_method :disable_metric, :disable_metric
19
+
20
+ def metric_enabled?(_metric)
21
+ false
22
+ end
23
+ alias_method :metric_enabled?, :metric_enabled?
24
+ end
25
+ end
26
+
27
+ module AbstractInstrumenter
28
+ TIMER_GLOBAL = 1 << 0
29
+ TIMER_LOCAL = 1 << 1
30
+ WAITING_THREADS = 1 << 2
31
+
32
+ def enabled?
33
+ Native.metric_enabled?(metric)
34
+ end
35
+
36
+ def enable
37
+ Native.enable_metric(metric)
38
+ end
39
+
40
+ def disable
41
+ Native.disable_metric(metric)
42
+ end
43
+
44
+ def reset
45
+ false
46
+ end
47
+
48
+ private
49
+
50
+ def metric
51
+ raise NotImplementedError
52
+ end
53
+ end
54
+ private_constant :AbstractInstrumenter
55
+
56
+ module GlobalTimer
57
+ extend AbstractInstrumenter
58
+
59
+ class << self
60
+ def monotonic_time
61
+ 0
62
+ end
63
+ alias_method :monotonic_time, :monotonic_time
64
+
65
+ private
66
+
67
+ def metric
68
+ TIMER_GLOBAL
69
+ end
70
+ end
71
+ end
72
+
73
+ module LocalTimer
74
+ extend AbstractInstrumenter
75
+
76
+ class << self
77
+ def monotonic_time
78
+ 0
79
+ end
80
+ alias_method :monotonic_time, :monotonic_time
81
+
82
+ private
83
+
84
+ def metric
85
+ TIMER_LOCAL
86
+ end
87
+ end
88
+ end
89
+
90
+ module WaitingThreads
91
+ extend AbstractInstrumenter
92
+
93
+ class << self
94
+ def count
95
+ 0
96
+ end
97
+ alias_method :count, :count
98
+
99
+ private
100
+
101
+ def metric
102
+ WAITING_THREADS
103
+ end
104
+ end
105
+ end
106
+
107
+ begin
108
+ require "gvltools/instrumentation"
109
+ rescue LoadError
110
+ # No native ext.
111
+ end
112
+
113
+ private_constant :Native
114
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gvltools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jean Boussier
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-01-17 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'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email:
29
+ - jean.boussier@gmail.com
30
+ executables: []
31
+ extensions:
32
+ - ext/gvltools/extconf.rb
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".rubocop.yml"
36
+ - CHANGELOG.md
37
+ - Gemfile
38
+ - Gemfile.lock
39
+ - LICENSE.txt
40
+ - README.md
41
+ - Rakefile
42
+ - ext/gvltools/extconf.rb
43
+ - ext/gvltools/instrumentation.c
44
+ - gvltools.gemspec
45
+ - lib/gvltools.rb
46
+ - lib/gvltools/version.rb
47
+ homepage: https://github.com/Shopify/gvltools
48
+ licenses:
49
+ - MIT
50
+ metadata:
51
+ allowed_push_host: https://rubygems.org
52
+ homepage_uri: https://github.com/Shopify/gvltools
53
+ source_code_uri: https://github.com/Shopify/gvltools
54
+ changelog_uri: https://github.com/Shopify/gvltools/blob/master/CHANGELOG.md
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 2.6.0
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubygems_version: 3.4.1
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: Set of GVL instrumentation tools
74
+ test_files: []