fiber-profiler 0.6.1 → 0.7.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
  SHA256:
3
- metadata.gz: 1a4ef2a5d3b1f833810953f991fbbe8aac342b9e757c5ebfadfd695d87036558
4
- data.tar.gz: '012498a134f5b2c22fad0761090514d577d3ee36143ef568c5e0617b74a46d24'
3
+ metadata.gz: 907a8aaa882e1eb3d59e994e89b6c7181b85e5a1795f7bddab05cae9bf32afe8
4
+ data.tar.gz: 412d12500a7bb74047b883669c70575360222fcf7df7a8b3fc6ecf5f73efc6b9
5
5
  SHA512:
6
- metadata.gz: 04c655663d123aa94f15f83d7b6a70ab9d81074fe18fb1d870f358756d19a7e91b4eaac4b0b832ed6c649397bf10e4e41b2342abfec1e7bb8e0c652f98630334
7
- data.tar.gz: 3529cf9a591bcfdd81dcf1a07d71401ab07322e475ec7a0b34b1d3d73080595cbc2b81419c1c7d9abe6341d163bcaaf1ac2017aed9e5d0d7af1847d15d704a88
6
+ metadata.gz: e5347ee515d48af4c75bbe85b32b8a71019ad8f48392a641d47e213012ed41bf2ee9a6112c11cbe46ecf66da23d9682d770ead22f73180eaa2950a636c00ce39
7
+ data.tar.gz: dceff8570643161dc70ec0b02bf72504cc5d554c4d5db201f6c9efdc63f64e0e6b90fa3b234a5b56174d67aab4d6cb98b4b8360250466aba46752fe9f5a5c1c6
checksums.yaml.gz.sig CHANGED
Binary file
@@ -12,6 +12,25 @@ $ bundle add fiber-profiler
12
12
 
13
13
  ## Usage
14
14
 
15
+ Select the profiling mode when starting your application:
16
+
17
+ ```bash
18
+ FIBER_PROFILER=watchdog bundle exec falcon serve
19
+ ```
20
+
21
+ With Async, including Falcon, the scheduler starts and stops the selected profiler automatically. Include `fiber-profiler` in your application's bundle; no initializer or per-request instrumentation is needed.
22
+
23
+ | Setting | Behaviour |
24
+ | --- | --- |
25
+ | `FIBER_PROFILER=watchdog` | Periodically sample stacks during sustained fiber execution. |
26
+ | `FIBER_PROFILER=capture` | Trace calls and report fibers that exceed the capture threshold. |
27
+ | `FIBER_PROFILER=false` | Disable the default profiler, even if the legacy capture flag is enabled. |
28
+ | `FIBER_PROFILER` unset | Preserve the existing `FIBER_PROFILER_CAPTURE=true` behaviour. |
29
+
30
+ Unknown modes raise `ArgumentError`. Set environment variables before loading the gem: the native capture settings are read when the extension loads. The new mode selector and watchdog settings are read when `Fiber::Profiler.default` is called.
31
+
32
+ ### Manual Instrumentation
33
+
15
34
  Instrument your code using the default profiler:
16
35
 
17
36
  ```ruby
@@ -21,14 +40,16 @@ require "fiber/profiler"
21
40
 
22
41
  profiler = Fiber::Profiler.default
23
42
 
24
- profiler&.start
25
-
26
- # Your application code:
27
- Fiber.new do
28
- sleep 0.1
29
- end.resume
30
-
31
- profiler&.stop
43
+ begin
44
+ profiler&.start
45
+
46
+ # Your application code:
47
+ Fiber.new do
48
+ sleep 0.1
49
+ end.resume
50
+ ensure
51
+ profiler&.stop
52
+ end
32
53
  ```
33
54
 
34
55
  Running this program will output the following:
@@ -41,10 +62,63 @@ Fiber stalled for 0.105 seconds
41
62
 
42
63
  ## Integration with Async
43
64
 
44
- The fiber profiler is optionally supported by `Async`. Simply enable the profiler using `FIBER_PROFILER_CAPTURE=true` to capture and report stalls.
65
+ The fiber profiler is optionally supported by `Async`. Enable either mode using `FIBER_PROFILER=watchdog` or `FIBER_PROFILER=capture`. The legacy `FIBER_PROFILER_CAPTURE=true` setting continues to enable capture mode when `FIBER_PROFILER` is unset.
66
+
67
+ Each scheduler obtains its own profiler. Watchdog binds to the thread when profiling starts, so schedulers in separate worker processes or threads are monitored independently. After `fork`, inherited profiling is stopped in the child; a new scheduler starts a new profiler normally.
68
+
69
+ ## Watchdog Mode
70
+
71
+ Watchdog uses a thread-specific `:fiber_switch` tracepoint to track which fiber is executing. A separate Ruby thread periodically captures the monitored thread's backtrace. It does not install method-call tracing hooks or require a heartbeat task.
72
+
73
+ By default, it samples every 100 ms, retains at most five recent stacks, and reports when the same fiber execution lasts at least 500 ms. A continued stall produces at most one report per additional threshold interval. Returning to the event loop or switching to another fiber resets the sample window. This measures individual uninterrupted fiber executions, rather than starvation of a scheduled heartbeat.
74
+
75
+ To reproduce a stall, save this as `stall.rb`:
76
+
77
+ ```ruby
78
+ require "async"
79
+
80
+ Sync do
81
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + 2
82
+ while Process.clock_gettime(Process::CLOCK_MONOTONIC) < deadline
83
+ end
84
+ end
85
+ ```
86
+
87
+ ```bash
88
+ FIBER_PROFILER=watchdog bundle exec ruby stall.rb
89
+ ```
90
+
91
+ The reports should point to the busy loop. Replacing the loop with `sleep(2)` allows Async to keep scheduling fibers and should produce no reports.
92
+
93
+ Terminal output contains readable stacks. Redirected output uses one JSON object per report, with `mode: "watchdog"`, process/thread/fiber identifiers, elapsed `duration`, and a bounded `samples` array. Each sample contains its elapsed time and `backtrace`. Duration is elapsed wall time since the fiber resumed, and a report is emitted while the fiber is still executing.
94
+
95
+ ### Configuration
96
+
97
+ | Variable | Default | Meaning |
98
+ | --- | --- | --- |
99
+ | `FIBER_PROFILER_WATCHDOG_STALL_THRESHOLD` | `0.5` | Minimum execution duration in seconds before reporting, and minimum interval between repeated reports. |
100
+ | `FIBER_PROFILER_WATCHDOG_SAMPLE_INTERVAL` | `0.1` | Delay in seconds between stack samples. |
101
+
102
+ Both values must be finite and positive. Actual sampling intervals depend on Ruby and operating-system scheduling. Capture-specific settings, including its sample rate, do not configure watchdog mode.
103
+
104
+ For explicit instrumentation, use `Fiber::Profiler::Watchdog.new(stall_threshold: 0.5, sample_interval: 0.1, max_samples: 5, output: $stderr)` and the same `start`/`stop` lifecycle as capture. The output must support writes from the watchdog thread. Stop the profiler before closing its output.
105
+
106
+ If sampling or reporting raises a `StandardError`, the watchdog disables its tracepoint and stops sampling. It retains the exception in `watchdog.error` and attempts one warning to standard error. Failure to write that warning is also contained. These errors do not escape through `stop` or replace application errors. Call `stop` normally to finish cleanup; a subsequent `start` clears the error and resumes monitoring.
107
+
108
+ ### Interpretation and Limits
109
+
110
+ Repeated frames identify code worth investigating: optimize expensive work, add cooperative yield points, or offload suitable work to a bounded thread pool. Sampling still has overhead; measure it for your workload.
111
+
112
+ The repository includes a [Falcon overhead benchmark](https://github.com/socketry/fiber-profiler/tree/main/benchmark/falcon) comparing disabled, watchdog, and capture modes. It measures throughput, latency, server CPU time, and Ruby allocations across repeated runs. Use it as a starting point for measuring your own application before enabling continuous monitoring.
113
+
114
+ The watchdog requires Ruby thread scheduling. Native code that holds the GVL without allowing other threads to run can prevent sampling entirely. Short stalls can occur between samples, and normal scheduler/OS delays can extend measured execution time. A report is a diagnostic lead, not proof that every sampled frame is expensive.
115
+
116
+ When profiling starts on a blocking event-loop fiber, that fiber is excluded from monitoring, including its normal idle waits. Other fibers, including blocking application fibers, are monitored. When profiling starts inside a non-blocking application fiber, blocking fibers are excluded because the event-loop fiber is not known. Start and stop profiling on the monitored thread.
45
117
 
46
118
  ## Default Environment Variables
47
119
 
120
+ The following settings apply to capture mode only.
121
+
48
122
  ### `FIBER_PROFILER_CAPTURE`
49
123
 
50
124
  Set to `true` to enable capturing of stalled fibers.
@@ -70,3 +144,5 @@ $ bundle exec bake input --file samples.ndjson fiber:profiler:analyze output
70
144
  ```
71
145
 
72
146
  This will aggregate all the call logs and generate a short summary, ordered by duration.
147
+
148
+ The analyzer consumes capture-mode call timings. Watchdog reports contain sampled backtraces instead of call timings and are not aggregated by this command.
@@ -184,7 +184,7 @@ static void Fiber_Profiler_Capture_free(void *ptr) {
184
184
  Fiber_Profiler_Stream_free(&capture->stream);
185
185
  Fiber_Profiler_Deque_free(&capture->calls);
186
186
 
187
- free(capture);
187
+ xfree(capture);
188
188
  }
189
189
 
190
190
  static size_t Fiber_Profiler_Capture_memsize(const void *ptr) {
@@ -271,7 +271,9 @@ VALUE Fiber_Profiler_Capture_initialize(int argc, VALUE *argv, VALUE self) {
271
271
 
272
272
  VALUE arguments[5] = {0};
273
273
  VALUE options = Qnil;
274
- rb_scan_args(argc, argv, ":", &options);
274
+ if (argc > 0) {
275
+ rb_scan_args(argc, argv, ":", &options);
276
+ }
275
277
  rb_get_kwargs(options, Fiber_Profiler_Capture_initialize_options, 0, 5, arguments);
276
278
 
277
279
  if (arguments[0] != Qundef) {
@@ -4,44 +4,11 @@
4
4
  # Copyright, 2025-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "native"
7
- require_relative "fork_handler"
7
+ require_relative "thread_local"
8
8
 
9
9
  module Fiber::Profiler
10
- # Thread-local storage for the active profiler capture.
11
- ::Thread.attr_accessor :fiber_profiler_capture
12
-
13
- # Hook into Process._fork to handle fork events automatically.
14
- ::Process.singleton_class.prepend(ForkHandler)
15
-
16
- # Private module that wraps start/stop to manage thread-local storage.
17
- module ThreadLocalCapture
18
- # Start profiling and store the capture in the current thread's thread-local storage.
19
- def start
20
- result = super
21
-
22
- if result
23
- Thread.current.fiber_profiler_capture = self
24
- end
25
-
26
- return result
27
- end
28
-
29
- # Stop profiling and clear the capture from the current thread's thread-local storage.
30
- def stop
31
- result = super
32
-
33
- if result
34
- Thread.current.fiber_profiler_capture = nil
35
- end
36
-
37
- return result
38
- end
39
- end
40
-
41
- private_constant :ThreadLocalCapture
42
-
43
10
  # Represents a running profiler capture.
44
11
  class Capture
45
- prepend ThreadLocalCapture
12
+ prepend ThreadLocalProfiler
46
13
  end
47
14
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025-2026, by Samuel Williams.
5
+
6
+ require_relative "fork_handler"
7
+
8
+ module Fiber::Profiler
9
+ # Thread-local storage for the active profiler, shared by both implementations.
10
+ ::Thread.attr_accessor :fiber_profiler_capture
11
+
12
+ ::Process.singleton_class.prepend(ForkHandler)
13
+
14
+ # Manages the active profiler so it can be stopped after a fork.
15
+ module ThreadLocalProfiler
16
+ # Start profiling on the current thread.
17
+ def start
18
+ result = super
19
+ Thread.current.fiber_profiler_capture = self if result
20
+ result
21
+ end
22
+
23
+ # Stop profiling and clear the current thread's reference.
24
+ def stop
25
+ super
26
+ ensure
27
+ if Thread.current.fiber_profiler_capture.equal?(self)
28
+ Thread.current.fiber_profiler_capture = nil
29
+ end
30
+ end
31
+ end
32
+
33
+ private_constant :ThreadLocalProfiler
34
+ end
@@ -7,6 +7,6 @@
7
7
  class Fiber
8
8
  # @namespace
9
9
  module Profiler
10
- VERSION = "0.6.1"
10
+ VERSION = "0.7.0"
11
11
  end
12
12
  end
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require_relative "version"
7
+ require_relative "thread_local"
8
+ require "json"
9
+
10
+ module Fiber::Profiler
11
+ # Samples stacks while a fiber runs without switching back to the event loop.
12
+ class Watchdog
13
+ prepend ThreadLocalProfiler
14
+
15
+ # Build a watchdog using environment configuration.
16
+ # @parameter env [Hash] The environment to read configuration from.
17
+ # @returns [Watchdog] The configured watchdog.
18
+ def self.default(env = ENV)
19
+ new(
20
+ stall_threshold: Float(env.fetch("FIBER_PROFILER_WATCHDOG_STALL_THRESHOLD", "0.5")),
21
+ sample_interval: Float(env.fetch("FIBER_PROFILER_WATCHDOG_SAMPLE_INTERVAL", "0.1"))
22
+ )
23
+ end
24
+
25
+ # Initialize a watchdog. Monitoring begins when {start} is called.
26
+ # @parameter stall_threshold [Float] Seconds without a fiber switch before reporting, and the minimum interval between reports for the same execution.
27
+ # @parameter sample_interval [Float] Seconds between stack samples.
28
+ # @parameter max_samples [Integer] Maximum number of recent stacks retained per execution.
29
+ # @parameter output [IO] The destination for stall reports.
30
+ def initialize(stall_threshold: 0.5, sample_interval: 0.1, max_samples: 5, output: $stderr)
31
+ @stall_threshold = Float(stall_threshold)
32
+ @sample_interval = Float(sample_interval)
33
+
34
+ unless @stall_threshold.finite? && @stall_threshold.positive? && @sample_interval.finite? && @sample_interval.positive?
35
+ raise ArgumentError, "Watchdog intervals must be finite and positive"
36
+ end
37
+ unless max_samples.is_a?(Integer) && max_samples.positive?
38
+ raise ArgumentError, "max_samples must be a positive integer"
39
+ end
40
+
41
+ @max_samples = max_samples
42
+ @output = output
43
+ @running = false
44
+ @stalls = 0
45
+ @error = nil
46
+ end
47
+
48
+ # @attribute [Float] The minimum execution duration before reporting a stall.
49
+ attr_reader :stall_threshold
50
+
51
+ # @attribute [Float] The delay between samples.
52
+ attr_reader :sample_interval
53
+
54
+ # @attribute [Integer] The number of reports written.
55
+ attr_reader :stalls
56
+
57
+ # @attribute [StandardError | Nil] The sampling or reporting failure, retained until monitoring starts again.
58
+ attr_reader :error
59
+
60
+ # Start monitoring application fibers on the calling thread.
61
+ # @returns [Watchdog | false] Self, or false if already running.
62
+ def start
63
+ return false if @running
64
+
65
+ @thread = Thread.current
66
+ @pid = Process.pid
67
+ @mutex = Mutex.new
68
+ @condition = ConditionVariable.new
69
+ @running = true
70
+ @execution = nil
71
+ @error = nil
72
+
73
+ # When started on the event loop, distinguish it from blocking application fibers:
74
+ @loop = Fiber.current if Fiber.current.blocking?
75
+ @tracepoint = TracePoint.new(:fiber_switch){record_execution}
76
+ @tracepoint.enable(target_thread: @thread)
77
+ record_execution
78
+
79
+ @watchdog = Thread.new{watch}
80
+ self
81
+ rescue Exception
82
+ stop
83
+ raise
84
+ end
85
+
86
+ # Stop monitoring and wait for the sampling thread to finish.
87
+ # @returns [Watchdog | false] Self, or false if already stopped.
88
+ def stop
89
+ return false unless @running
90
+
91
+ @tracepoint&.disable
92
+
93
+ if @pid == Process.pid
94
+ @mutex.synchronize do
95
+ @running = false
96
+ @condition.broadcast
97
+ end
98
+ @watchdog&.join
99
+ else
100
+ # The sampling thread does not survive fork; avoid inherited synchronization:
101
+ @running = false
102
+ end
103
+
104
+ self
105
+ ensure
106
+ @watchdog = @thread = @execution = @loop = @tracepoint = nil
107
+ end
108
+
109
+ private
110
+
111
+ def now
112
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
113
+ end
114
+
115
+ def record_execution
116
+ fiber = Fiber.current
117
+ idle = @loop ? fiber.equal?(@loop) : fiber.blocking?
118
+
119
+ @mutex.synchronize do
120
+ @execution = idle ? nil : [fiber, now]
121
+ end
122
+ end
123
+
124
+ def wait
125
+ @mutex.synchronize do
126
+ @condition.wait(@mutex, @sample_interval) if @running
127
+ @running
128
+ end
129
+ end
130
+
131
+ def watch
132
+ execution = nil
133
+ samples = []
134
+ next_report = @stall_threshold
135
+
136
+ while wait
137
+ current = @mutex.synchronize{@execution}
138
+ unless current.equal?(execution)
139
+ execution = current
140
+ samples.clear
141
+ next_report = @stall_threshold
142
+ end
143
+ next unless execution
144
+
145
+ backtrace = @thread.backtrace
146
+ duration = now - execution[1]
147
+
148
+ # Discard samples if the target switched fibers while capturing its stack:
149
+ next unless backtrace && @mutex.synchronize{@execution.equal?(execution)}
150
+
151
+ samples << {elapsed: duration, backtrace: backtrace}
152
+ samples.shift if samples.size > @max_samples
153
+
154
+ if duration >= next_report
155
+ report(execution[0], duration, samples)
156
+ @stalls += 1
157
+ next_report = duration + @stall_threshold
158
+ end
159
+ end
160
+ rescue StandardError => error
161
+ # A diagnostic failure must not escape into the monitored application:
162
+ @error = error
163
+ @tracepoint.disable
164
+ warn_failure(error)
165
+ end
166
+
167
+ def warn_failure(error)
168
+ warn "Fiber::Profiler::Watchdog disabled: #{error.class}: #{error.message}"
169
+ rescue StandardError
170
+ # The diagnostic destination may also be unavailable; retain the original error:
171
+ nil
172
+ end
173
+
174
+ def report(fiber, duration, samples)
175
+ if @output.respond_to?(:tty?) && @output.tty?
176
+ message = "## Fiber stalled for at least %.3f seconds (watchdog, pid=%d, thread=%d, fiber=%d)\n" % [duration, @pid, @thread.object_id, fiber.object_id]
177
+ message << samples.map{|sample| sample[:backtrace].join("\n")}.join("\n\n") << "\n"
178
+ else
179
+ message = JSON.generate(mode: "watchdog", pid: @pid, thread_id: @thread.object_id, fiber_id: fiber.object_id, duration: duration, samples: samples) << "\n"
180
+ end
181
+
182
+ @output.write(message)
183
+ end
184
+ end
185
+ end
@@ -9,11 +9,25 @@ require_relative "profiler/capture"
9
9
  module Fiber::Profiler
10
10
  # The default profiler to use, if any.
11
11
  #
12
- # Use the `FIBER_PROFILER_CAPTURE=true` environment variable to enable profiling.
12
+ # Set `FIBER_PROFILER` to `watchdog`, `capture`, or `false`. When unset,
13
+ # the legacy `FIBER_PROFILER_CAPTURE=true` setting enables capture mode.
13
14
  #
14
- # @returns [Capture | Nil]
15
+ # @returns [Capture | Watchdog | Nil]
16
+ # @raises [ArgumentError] If the requested mode is unknown.
15
17
  def self.default
16
- Capture.default
18
+ case mode = ENV["FIBER_PROFILER"]
19
+ when nil
20
+ Capture.default
21
+ when "capture"
22
+ Capture.new
23
+ when "watchdog"
24
+ require_relative "profiler/watchdog"
25
+ Watchdog.default
26
+ when "false"
27
+ nil
28
+ else
29
+ raise ArgumentError, "Unknown FIBER_PROFILER mode: #{mode.inspect} (expected watchdog, capture, or false)"
30
+ end
17
31
  end
18
32
 
19
33
  # Execute the given block with the {default} profiler, if any.
data/readme.md CHANGED
@@ -18,6 +18,10 @@ Please see the [project documentation](https://socketry.github.io/fiber-profiler
18
18
 
19
19
  Please see the [project releases](https://socketry.github.io/fiber-profiler/releases/index) for all releases.
20
20
 
21
+ ### v0.7.0
22
+
23
+ - Sign releases with the Socketry Ruby Gems certificate.
24
+
21
25
  ### v0.6.0
22
26
 
23
27
  - Fixed compatibility with `Process.fork`.
@@ -50,12 +54,14 @@ bundle exec sus
50
54
 
51
55
  ### Making Releases
52
56
 
53
- To make a new release:
57
+ To prepare a release branch and open a pull request from an up-to-date `main`:
54
58
 
55
59
  ``` shell
56
- bundle exec bake gem:release:patch # or minor or major
60
+ bundle exec bake gem:github:release:patch # or minor or major
57
61
  ```
58
62
 
63
+ After the release PR is merged, GitHub Actions automatically publishes the verified gem through the `rubygems` environment. See [bake-gem-github](https://github.com/socketry/bake-gem-github) for setup, remote releases, and recovery.
64
+
59
65
  ### Developer Certificate of Origin
60
66
 
61
67
  In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
data/release.cert ADDED
@@ -0,0 +1,24 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEFDCCAnygAwIBAgIBATANBgkqhkiG9w0BAQsFADAwMREwDwYDVQQKDAhTb2Nr
3
+ ZXRyeTEbMBkGA1UEAwwSU29ja2V0cnkgUnVieSBHZW1zMB4XDTI2MDkyMTA3NTgx
4
+ NFoXDTI3MDkyMTA3NTgxNFowMDERMA8GA1UECgwIU29ja2V0cnkxGzAZBgNVBAMM
5
+ ElNvY2tldHJ5IFJ1YnkgR2VtczCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoC
6
+ ggGBAM/QgjVgDzDo/xJEQoFvAzcVFP7msnswkhPJB2UDsbxXCZmers5jV512TM5s
7
+ NwLHfzJiC4DcI5ax9ZYKM9Q+dS21YYagNqjtg3YPyDqR6phoibEA0VoMuInUQW68
8
+ i5OkCJzYKGD2pYYVZnuFNqyM2ECUvXh/fBmvPoHbncAHhWPaCBH8mQJ8sRNc6+RV
9
+ SoZZu1Yo/aW1zQ+SpZwad7s1ZmigfDNHKhJ22KwZHk+/5Zw1QXVfcajjswV0Mm2P
10
+ irgjKN3fyNVkwlly63EBlR4VydT+g9QtJtqh7ee0ThhI+v4wYovf4dbLNcEBTHu1
11
+ x1pGbXGx/2fBAST5eVwaXIx1V8VU22AJcBk1H2o5/1F0HfbD2HQjmFOA112KUwBW
12
+ 9TtspZD3g6rCQFX53XvL9h6j2y1ukl7s5AdEzOe/x6r1MO8tSDtgcYoF89wgF/88
13
+ Q/Eski0FckQ+znfESZcFcpzs2sPDoYW6SNVX4tZcNty85Y6WBHfHKio14x63PeyC
14
+ CG1gMwIDAQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU
15
+ SuMc0x24Xshtaa8SQVkBr2Z5WzIwDQYJKoZIhvcNAQELBQADggGBABI62ey5lDWm
16
+ j8jsTQaNBMBb7LbKS3XkBwyL1UNGEwlicp399rJDeuNgVSHqBCZ/nE4yleNjgW9Y
17
+ N6DKdxXCvXmFUoHvUINeAvThHxmlEGhSfXl1x55xHEig0rEP4jgdjnYdQI4fqeOM
18
+ zIDN0F3ruArke4Y62SQgWVN7vspdAA41hBIRl1vsvs0KWG8DIVQ8cmR+TG6zjOmK
19
+ iUiUZGNPnStV1O1xW83c+Ba4Am0krP6/forxDPhRvTehqkVYvMdPfYcICNb5IM58
20
+ 5vaYvuj5AtfZuZWN0F/0KtSphhNC17rh6h8QmuYdMJ1clvlrkiEaO1UjPVwjgtxC
21
+ x/ARh5X6q0/YTXl4r7EfSJEeqW7qhgu9EUj3mGIPHYdX0Dqih/NgK20F3GvQtTfB
22
+ 7sse0duKBHXONYVwKAsceUVvqtCfyF608bCilCkbhw3QaInRj+BkRQKJ5Tuj2PZR
23
+ 76a/hwaV2wWn5RtAFBaUQzQdo/xTqJ7IkwaFjOZe/QyvyoRjCK9Rdg==
24
+ -----END CERTIFICATE-----
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v0.7.0
4
+
5
+ - Sign releases with the Socketry Ruby Gems certificate.
6
+
3
7
  ## v0.6.0
4
8
 
5
9
  - Fixed compatibility with `Process.fork`.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fiber-profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -9,35 +9,45 @@ bindir: bin
9
9
  cert_chain:
10
10
  - |
11
11
  -----BEGIN CERTIFICATE-----
12
- MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
13
- ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
14
- CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
15
- MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
16
- MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
17
- bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
18
- igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
19
- 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
20
- sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
21
- e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
22
- XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
23
- RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
24
- tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
25
- zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
26
- xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
27
- BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
28
- aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
29
- aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
30
- 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
- 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
- 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
- 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
- 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
- eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
36
- 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
- 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
38
34
  -----END CERTIFICATE-----
39
35
  date: 1980-01-02 00:00:00.000000000 Z
40
- dependencies: []
36
+ dependencies:
37
+ - !ruby/object:Gem::Dependency
38
+ name: json
39
+ requirement: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
41
51
  executables: []
42
52
  extensions:
43
53
  - ext/extconf.rb
@@ -60,9 +70,12 @@ files:
60
70
  - lib/fiber/profiler/capture.rb
61
71
  - lib/fiber/profiler/fork_handler.rb
62
72
  - lib/fiber/profiler/native.rb
73
+ - lib/fiber/profiler/thread_local.rb
63
74
  - lib/fiber/profiler/version.rb
75
+ - lib/fiber/profiler/watchdog.rb
64
76
  - license.md
65
77
  - readme.md
78
+ - release.cert
66
79
  - releases.md
67
80
  homepage: https://github.com/socketry/fiber-profiler
68
81
  licenses:
@@ -84,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
97
  - !ruby/object:Gem::Version
85
98
  version: '0'
86
99
  requirements: []
87
- rubygems_version: 4.0.6
100
+ rubygems_version: 4.0.21
88
101
  specification_version: 4
89
102
  summary: A fiber stall profiler.
90
103
  test_files: []
metadata.gz.sig CHANGED
Binary file