gvltools 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []