appsignal 3.1.3 → 3.1.4

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: 16fff6fc92481e9164ad7611fdb1de6d0ebee1d28906ebc95d7fe10100d69cfd
4
- data.tar.gz: 790ec312c3349ee2b6889d8fca238e6cea7daa537c71cf81b7438152c2a2a2cb
3
+ metadata.gz: c21e4e80dd54b8a0f6619a40b7c046e6bc132634ce26666b281fdd4298c72c3f
4
+ data.tar.gz: 53743646f5cf821ab45559817cf65c2bf9ed483184725fca2413b3606fad6e1e
5
5
  SHA512:
6
- metadata.gz: c20ef4e52d38822a25880f8be5d88ea1f703b7c165813cdcd3d959383c03ff668f47a9c0bab41fe904eb8bf746911d4eb445f90a1804227847172bdc45e80c38
7
- data.tar.gz: c5705bfeca7ec423ee1888572f84ef70fd15abdb5501cd0c9e73c2d5855b498d7e03ac8c50ab1aa88137d3335376cd2d0b767503c0ec17d9b2c7b209cd04e80f
6
+ metadata.gz: 36a34d74cf16e5f274e3485471833f516e34227546017d14f24d70fc75ca824b68261a7048924391135a3c0d815d7ef605b60ac19396bdeb9b9b46f1d4911daa
7
+ data.tar.gz: b03a309a6c8649397e6f5d9b62a65324e4b4e39a3a1eee77b6cb60280758838212e2de7ca3eb28c8f10b3b59cb5b02ca7d88347cc9f3995be0308bf5249cae70
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 3.1.4
4
+
5
+ ### Added
6
+
7
+ - [ffe49cfe](https://github.com/appsignal/appsignal-ruby/commit/ffe49cfe94f5269e59d6f168a73114f7a3914f79) patch - Support temporarily disabling GC profiling without reporting inaccurate `gc_time` metric durations. The MRI probe's `gc_time` will not report any value when the `GC::Profiler.enabled?` returns `false`.
8
+
9
+ ### Changed
10
+
11
+ - [af7e666c](https://github.com/appsignal/appsignal-ruby/commit/af7e666cf173ec1f42e9cf3fce2ab6c8e658440c) patch - Listen if the Ruby Garbage Collection profiler is enabled and collect how long the GC is running for the Ruby VM magic dashboard. An app will need to call `GC::Profiler.enable` to enable the GC profiler. Do not enable this in production environments, or at least not for long, because this can negatively impact performance of apps.
12
+
13
+ ### Fixed
14
+
15
+ - [b3a163be](https://github.com/appsignal/appsignal-ruby/commit/b3a163be154796e1f358c5061eaee99845c960ee) patch - Fix the MRI probe using the Garbage Collection profiler instead of the NilProfiler when garbage collection instrumentation is not enabled for MRI probe. This caused unnecessary overhead.
16
+
3
17
  ## 3.1.3
4
18
 
5
19
  ### Added
@@ -15,7 +15,6 @@ module Appsignal
15
15
  :debug => false,
16
16
  :dns_servers => [],
17
17
  :enable_allocation_tracking => true,
18
- :enable_gc_instrumentation => false,
19
18
  :enable_host_metrics => true,
20
19
  :enable_minutely_probes => true,
21
20
  :enable_statsd => true,
@@ -63,7 +62,6 @@ module Appsignal
63
62
  "APPSIGNAL_DEBUG" => :debug,
64
63
  "APPSIGNAL_DNS_SERVERS" => :dns_servers,
65
64
  "APPSIGNAL_ENABLE_ALLOCATION_TRACKING" => :enable_allocation_tracking,
66
- "APPSIGNAL_ENABLE_GC_INSTRUMENTATION" => :enable_gc_instrumentation,
67
65
  "APPSIGNAL_ENABLE_HOST_METRICS" => :enable_host_metrics,
68
66
  "APPSIGNAL_ENABLE_MINUTELY_PROBES" => :enable_minutely_probes,
69
67
  "APPSIGNAL_ENABLE_STATSD" => :enable_statsd,
@@ -114,7 +112,6 @@ module Appsignal
114
112
  APPSIGNAL_ACTIVE
115
113
  APPSIGNAL_DEBUG
116
114
  APPSIGNAL_ENABLE_ALLOCATION_TRACKING
117
- APPSIGNAL_ENABLE_GC_INSTRUMENTATION
118
115
  APPSIGNAL_ENABLE_HOST_METRICS
119
116
  APPSIGNAL_ENABLE_MINUTELY_PROBES
120
117
  APPSIGNAL_ENABLE_STATSD
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ # @api private
5
+ module GarbageCollection
6
+ # Return the GC profiler wrapper.
7
+ #
8
+ # Returns {Profiler} if the Ruby Garbage Collection profiler is enabled.
9
+ # This is checked by calling `GC::Profiler.enabled?`.
10
+ #
11
+ # GC profiling is disabled by default due to the overhead it causes. Do not
12
+ # enable this in production for long periods of time.
13
+ def self.profiler
14
+ # Cached instances so it doesn't create a new object every time this
15
+ # method is called. Especially necessary for the {Profiler} because a new
16
+ # instance will have a new internal time counter.
17
+ @real_profiler ||= Profiler.new
18
+ @nil_profiler ||= NilProfiler.new
19
+
20
+ enabled? ? @real_profiler : @nil_profiler
21
+ end
22
+
23
+ # Check if Garbage Collection is enabled at the moment.
24
+ #
25
+ # @return [Boolean]
26
+ def self.enabled?
27
+ GC::Profiler.enabled?
28
+ end
29
+
30
+ # Unset the currently cached profilers.
31
+ #
32
+ # @return [void]
33
+ def self.clear_profiler!
34
+ @real_profiler = nil
35
+ @nil_profiler = nil
36
+ end
37
+
38
+ # A wrapper around Ruby's `GC::Profiler` that tracks garbage collection
39
+ # time, while clearing `GC::Profiler`'s total_time to make sure it doesn't
40
+ # leak memory by keeping garbage collection run samples in memory.
41
+ class Profiler
42
+ def self.lock
43
+ @lock ||= Mutex.new
44
+ end
45
+
46
+ def initialize
47
+ @total_time = 0
48
+ end
49
+
50
+ # Whenever {#total_time} is called, the current `GC::Profiler#total_time`
51
+ # gets added to `@total_time`, after which `GC::Profiler.clear` is called
52
+ # to prevent it from leaking memory. A class-level lock is used to make
53
+ # sure garbage collection time is never counted more than once.
54
+ #
55
+ # Whenever `@total_time` gets above two billion milliseconds (about 23
56
+ # days), it's reset to make sure the result fits in a signed 32-bit
57
+ # integer.
58
+ #
59
+ # @return [Integer]
60
+ def total_time
61
+ lock.synchronize do
62
+ @total_time += (internal_profiler.total_time * 1000).round
63
+ internal_profiler.clear
64
+ end
65
+
66
+ @total_time = 0 if @total_time > 2_000_000_000
67
+
68
+ @total_time
69
+ end
70
+
71
+ private
72
+
73
+ def internal_profiler
74
+ GC::Profiler
75
+ end
76
+
77
+ def lock
78
+ self.class.lock
79
+ end
80
+ end
81
+
82
+ # A dummy profiler that always returns 0 as the total time. Used when GC
83
+ # profiler is disabled.
84
+ class NilProfiler
85
+ def total_time
86
+ 0
87
+ end
88
+ end
89
+ end
90
+ end
@@ -8,7 +8,7 @@ module Appsignal
8
8
  defined?(::RubyVM) && ::RubyVM.respond_to?(:stat)
9
9
  end
10
10
 
11
- def initialize(appsignal: Appsignal, gc_profiler: Appsignal::GarbageCollectionProfiler.new)
11
+ def initialize(appsignal: Appsignal, gc_profiler: Appsignal::GarbageCollection.profiler)
12
12
  Appsignal.logger.debug("Initializing VM probe")
13
13
  @appsignal = appsignal
14
14
  @gc_profiler = gc_profiler
@@ -31,8 +31,10 @@ module Appsignal
31
31
  )
32
32
 
33
33
  set_gauge("thread_count", Thread.list.size)
34
- gauge_delta(:gc_time, @gc_profiler.total_time) do |gc_time|
35
- set_gauge("gc_time", gc_time) if gc_time > 0
34
+ if Appsignal::GarbageCollection.enabled?
35
+ gauge_delta(:gc_time, @gc_profiler.total_time) do |gc_time|
36
+ set_gauge("gc_time", gc_time) if gc_time > 0
37
+ end
36
38
  end
37
39
 
38
40
  gc_stats = GC.stat
@@ -66,11 +66,6 @@ module Appsignal
66
66
  def clear_current_transaction!
67
67
  Thread.current[:appsignal_transaction] = nil
68
68
  end
69
-
70
- def garbage_collection_profiler
71
- @garbage_collection_profiler ||=
72
- Appsignal.config[:enable_gc_instrumentation] ? Appsignal::GarbageCollectionProfiler.new : NilGarbageCollectionProfiler.new
73
- end
74
69
  end
75
70
 
76
71
  attr_reader :ext, :transaction_id, :action, :namespace, :request, :paused, :tags, :options, :discarded, :breadcrumbs
@@ -103,7 +98,7 @@ module Appsignal
103
98
  @ext = Appsignal::Extension.start_transaction(
104
99
  @transaction_id,
105
100
  @namespace,
106
- self.class.garbage_collection_profiler.total_time
101
+ 0
107
102
  ) || Appsignal::Extension::MockTransaction.new
108
103
  end
109
104
 
@@ -117,9 +112,7 @@ module Appsignal
117
112
  "because it was manually discarded."
118
113
  return
119
114
  end
120
- if @ext.finish(self.class.garbage_collection_profiler.total_time)
121
- sample_data
122
- end
115
+ sample_data if @ext.finish(0)
123
116
  @ext.complete
124
117
  end
125
118
 
@@ -355,7 +348,7 @@ module Appsignal
355
348
 
356
349
  def start_event
357
350
  return if paused?
358
- @ext.start_event(self.class.garbage_collection_profiler.total_time)
351
+ @ext.start_event(0)
359
352
  end
360
353
 
361
354
  def finish_event(name, title, body, body_format = Appsignal::EventFormatter::DEFAULT)
@@ -365,7 +358,7 @@ module Appsignal
365
358
  title || BLANK,
366
359
  body || BLANK,
367
360
  body_format || Appsignal::EventFormatter::DEFAULT,
368
- self.class.garbage_collection_profiler.total_time
361
+ 0
369
362
  )
370
363
  end
371
364
 
@@ -377,7 +370,7 @@ module Appsignal
377
370
  body || BLANK,
378
371
  body_format || Appsignal::EventFormatter::DEFAULT,
379
372
  duration,
380
- self.class.garbage_collection_profiler.total_time
373
+ 0
381
374
  )
382
375
  end
383
376
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.1.3".freeze
4
+ VERSION = "3.1.4".freeze
5
5
  end
data/lib/appsignal.rb CHANGED
@@ -115,11 +115,6 @@ module Appsignal
115
115
  Appsignal::Environment.report_enabled("allocation_tracking")
116
116
  end
117
117
 
118
- if config[:enable_gc_instrumentation]
119
- GC::Profiler.enable
120
- Appsignal::Environment.report_enabled("gc_instrumentation")
121
- end
122
-
123
118
  Appsignal::Minutely.start if config[:enable_minutely_probes]
124
119
 
125
120
  collect_environment_metadata
@@ -298,7 +293,7 @@ require "appsignal/hooks"
298
293
  require "appsignal/probes"
299
294
  require "appsignal/marker"
300
295
  require "appsignal/minutely"
301
- require "appsignal/garbage_collection_profiler"
296
+ require "appsignal/garbage_collection"
302
297
  require "appsignal/integrations/railtie" if defined?(::Rails)
303
298
  require "appsignal/transaction"
304
299
  require "appsignal/version"
@@ -156,7 +156,6 @@ describe Appsignal::Config do
156
156
  :debug => false,
157
157
  :dns_servers => [],
158
158
  :enable_allocation_tracking => true,
159
- :enable_gc_instrumentation => false,
160
159
  :enable_host_metrics => true,
161
160
  :enable_minutely_probes => true,
162
161
  :enable_statsd => true,
@@ -1,11 +1,33 @@
1
- describe Appsignal::GarbageCollectionProfiler do
1
+ describe Appsignal::GarbageCollection do
2
+ describe ".profiler" do
3
+ before do
4
+ # Unset the internal memoized variable to avoid state leaking
5
+ described_class.clear_profiler!
6
+ end
7
+
8
+ context "when GC instrumentation is disabled" do
9
+ it "returns the NilProfiler" do
10
+ expect(described_class.profiler).to be_a(Appsignal::GarbageCollection::NilProfiler)
11
+ end
12
+ end
13
+
14
+ context "when GC profiling is enabled" do
15
+ before { GC::Profiler.enable }
16
+ after { GC::Profiler.disable }
17
+
18
+ it "returns the Profiler" do
19
+ expect(described_class.profiler).to be_a(Appsignal::GarbageCollection::Profiler)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ describe Appsignal::GarbageCollection::Profiler do
2
26
  let(:internal_profiler) { FakeGCProfiler.new }
3
27
  let(:profiler) { described_class.new }
4
28
 
5
29
  before do
6
- allow_any_instance_of(described_class)
7
- .to receive(:internal_profiler)
8
- .and_return(internal_profiler)
30
+ stub_const("GC::Profiler", internal_profiler)
9
31
  end
10
32
 
11
33
  context "on initialization" do
@@ -54,7 +76,7 @@ describe Appsignal::GarbageCollectionProfiler do
54
76
 
55
77
  2.times do
56
78
  threads << Thread.new do
57
- profiler = Appsignal::GarbageCollectionProfiler.new
79
+ profiler = Appsignal::GarbageCollection::Profiler.new
58
80
  results << profiler.total_time
59
81
  end
60
82
  end
@@ -65,7 +87,7 @@ describe Appsignal::GarbageCollectionProfiler do
65
87
  end
66
88
  end
67
89
 
68
- describe Appsignal::NilGarbageCollectionProfiler do
90
+ describe Appsignal::GarbageCollection::NilProfiler do
69
91
  let(:profiler) { described_class.new }
70
92
 
71
93
  describe "#total_time" do
@@ -39,6 +39,7 @@ describe Appsignal::Probes::MriProbe do
39
39
  let(:hostname) { nil }
40
40
  before do
41
41
  allow(gc_profiler_mock).to receive(:total_time)
42
+ allow(GC::Profiler).to receive(:enabled?).and_return(true)
42
43
  end
43
44
 
44
45
  it "should track vm metrics" do
@@ -70,6 +71,40 @@ describe Appsignal::Probes::MriProbe do
70
71
  end
71
72
  end
72
73
 
74
+ context "when GC profiling is disabled" do
75
+ it "does not report a gc_time metric" do
76
+ allow(GC::Profiler).to receive(:enabled?).and_return(false)
77
+ expect(gc_profiler_mock).to_not receive(:total_time)
78
+ probe.call # Normal call, create a cache
79
+ probe.call # Report delta value based on cached value
80
+ metrics = appsignal_mock.gauges.map { |(key)| key }
81
+ expect(metrics).to_not include("gc_time")
82
+ end
83
+
84
+ it "does not report a gc_time metric while temporarily disabled" do
85
+ # While enabled
86
+ allow(GC::Profiler).to receive(:enabled?).and_return(true)
87
+ expect(gc_profiler_mock).to receive(:total_time).and_return(10, 15)
88
+ probe.call # Normal call, create a cache
89
+ probe.call # Report delta value based on cached value
90
+ expect_gauges([["gc_time", 5]])
91
+
92
+ # While disabled
93
+ allow(GC::Profiler).to receive(:enabled?).and_return(false)
94
+ probe.call # Call twice to make sure any caches resets wouldn't mess up the assertion
95
+ probe.call
96
+ # Does not include any newly reported metrics
97
+ expect_gauges([["gc_time", 5]])
98
+
99
+ # When enabled after being disabled for a while, it only reports the
100
+ # newly reported time since it was renabled
101
+ allow(GC::Profiler).to receive(:enabled?).and_return(true)
102
+ expect(gc_profiler_mock).to receive(:total_time).and_return(25)
103
+ probe.call
104
+ expect_gauges([["gc_time", 5], ["gc_time", 10]])
105
+ end
106
+ end
107
+
73
108
  it "tracks GC run count" do
74
109
  expect(GC).to receive(:count).and_return(10, 15)
75
110
  expect(GC).to receive(:stat).and_return(
@@ -792,23 +792,6 @@ describe Appsignal::Transaction do
792
792
  end
793
793
  end
794
794
 
795
- describe "#garbage_collection_profiler" do
796
- before { Appsignal::Transaction.instance_variable_set(:@garbage_collection_profiler, nil) }
797
-
798
- it "returns the NilGarbageCollectionProfiler" do
799
- expect(Appsignal::Transaction.garbage_collection_profiler).to be_a(Appsignal::NilGarbageCollectionProfiler)
800
- end
801
-
802
- context "when gc profiling is enabled" do
803
- before { Appsignal.config.config_hash[:enable_gc_instrumentation] = true }
804
- after { Appsignal.config.config_hash[:enable_gc_instrumentation] = false }
805
-
806
- it "returns the GarbageCollectionProfiler" do
807
- expect(Appsignal::Transaction.garbage_collection_profiler).to be_a(Appsignal::GarbageCollectionProfiler)
808
- end
809
- end
810
- end
811
-
812
795
  describe "#start_event" do
813
796
  it "starts the event in the extension" do
814
797
  expect(transaction.ext).to receive(:start_event).with(0).and_call_original
@@ -827,11 +810,7 @@ describe Appsignal::Transaction do
827
810
  end
828
811
 
829
812
  describe "#finish_event" do
830
- let(:fake_gc_time) { 123 }
831
- before do
832
- expect(described_class.garbage_collection_profiler)
833
- .to receive(:total_time).at_least(:once).and_return(fake_gc_time)
834
- end
813
+ let(:fake_gc_time) { 0 }
835
814
 
836
815
  it "should finish the event in the extension" do
837
816
  expect(transaction.ext).to receive(:finish_event).with(
@@ -878,11 +857,7 @@ describe Appsignal::Transaction do
878
857
  end
879
858
 
880
859
  describe "#record_event" do
881
- let(:fake_gc_time) { 123 }
882
- before do
883
- expect(described_class.garbage_collection_profiler)
884
- .to receive(:total_time).at_least(:once).and_return(fake_gc_time)
885
- end
860
+ let(:fake_gc_time) { 0 }
886
861
 
887
862
  it "should record the event in the extension" do
888
863
  expect(transaction.ext).to receive(:record_event).with(
@@ -54,20 +54,12 @@ describe Appsignal do
54
54
  Appsignal.start
55
55
  end
56
56
 
57
- context "when allocation tracking and gc instrumentation have been enabled" do
57
+ context "when allocation tracking has been enabled" do
58
58
  before do
59
- allow(GC::Profiler).to receive(:enable)
60
59
  Appsignal.config.config_hash[:enable_allocation_tracking] = true
61
- Appsignal.config.config_hash[:enable_gc_instrumentation] = true
62
60
  capture_environment_metadata_report_calls
63
61
  end
64
62
 
65
- it "should enable Ruby's GC::Profiler" do
66
- expect(GC::Profiler).to receive(:enable)
67
- Appsignal.start
68
- expect_environment_metadata("ruby_gc_instrumentation_enabled", "true")
69
- end
70
-
71
63
  unless DependencyHelper.running_jruby?
72
64
  it "installs the allocation event hook" do
73
65
  expect(Appsignal::Extension).to receive(:install_allocation_event_hook)
@@ -78,29 +70,17 @@ describe Appsignal do
78
70
  end
79
71
  end
80
72
 
81
- context "when allocation tracking and gc instrumentation have been disabled" do
73
+ context "when allocation tracking has been disabled" do
82
74
  before do
83
75
  Appsignal.config.config_hash[:enable_allocation_tracking] = false
84
- Appsignal.config.config_hash[:enable_gc_instrumentation] = false
85
76
  capture_environment_metadata_report_calls
86
77
  end
87
78
 
88
- it "should not enable Ruby's GC::Profiler" do
89
- expect(GC::Profiler).not_to receive(:enable)
90
- Appsignal.start
91
- end
92
-
93
79
  it "should not install the allocation event hook" do
94
80
  expect(Appsignal::Minutely).not_to receive(:install_allocation_event_hook)
95
81
  Appsignal.start
96
82
  expect_not_environment_metadata("ruby_allocation_tracking_enabled")
97
83
  end
98
-
99
- it "should not add the gc probe to minutely" do
100
- expect(Appsignal::Minutely).not_to receive(:register_garbage_collection_probe)
101
- Appsignal.start
102
- expect_not_environment_metadata("ruby_gc_instrumentation_enabled")
103
- end
104
84
  end
105
85
 
106
86
  context "when minutely metrics has been enabled" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appsignal
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.3
4
+ version: 3.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Beekman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-08-04 00:00:00.000000000 Z
13
+ date: 2022-08-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -204,7 +204,7 @@ files:
204
204
  - lib/appsignal/event_formatter/sequel/sql_formatter.rb
205
205
  - lib/appsignal/extension.rb
206
206
  - lib/appsignal/extension/jruby.rb
207
- - lib/appsignal/garbage_collection_profiler.rb
207
+ - lib/appsignal/garbage_collection.rb
208
208
  - lib/appsignal/helpers/instrumentation.rb
209
209
  - lib/appsignal/helpers/metrics.rb
210
210
  - lib/appsignal/hooks.rb
@@ -305,7 +305,7 @@ files:
305
305
  - spec/lib/appsignal/extension/jruby_spec.rb
306
306
  - spec/lib/appsignal/extension_install_failure_spec.rb
307
307
  - spec/lib/appsignal/extension_spec.rb
308
- - spec/lib/appsignal/garbage_collection_profiler_spec.rb
308
+ - spec/lib/appsignal/garbage_collection_spec.rb
309
309
  - spec/lib/appsignal/hooks/action_cable_spec.rb
310
310
  - spec/lib/appsignal/hooks/action_mailer_spec.rb
311
311
  - spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb
@@ -457,7 +457,7 @@ test_files:
457
457
  - spec/lib/appsignal/extension/jruby_spec.rb
458
458
  - spec/lib/appsignal/extension_install_failure_spec.rb
459
459
  - spec/lib/appsignal/extension_spec.rb
460
- - spec/lib/appsignal/garbage_collection_profiler_spec.rb
460
+ - spec/lib/appsignal/garbage_collection_spec.rb
461
461
  - spec/lib/appsignal/hooks/action_cable_spec.rb
462
462
  - spec/lib/appsignal/hooks/action_mailer_spec.rb
463
463
  - spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Appsignal
4
- # {Appsignal::GarbageCollectionProfiler} wraps Ruby's `GC::Profiler` to be
5
- # able to track garbage collection time for multiple transactions, while
6
- # constantly clearing `GC::Profiler`'s total_time to make sure it doesn't
7
- # leak memory by keeping garbage collection run samples in memory.
8
- #
9
- # @api private
10
- class GarbageCollectionProfiler
11
- def self.lock
12
- @lock ||= Mutex.new
13
- end
14
-
15
- def initialize
16
- @total_time = 0
17
- end
18
-
19
- # Whenever {#total_time} is called, the current `GC::Profiler#total_time`
20
- # gets added to `@total_time`, after which `GC::Profiler.clear` is called
21
- # to prevent it from leaking memory. A class-level lock is used to make
22
- # sure garbage collection time is never counted more than once.
23
- #
24
- # Whenever `@total_time` gets above two billion milliseconds (about 23
25
- # days), it's reset to make sure the result fits in a signed 32-bit
26
- # integer.
27
- #
28
- # @return [Integer]
29
- def total_time
30
- lock.synchronize do
31
- @total_time += (internal_profiler.total_time * 1000).round
32
- internal_profiler.clear
33
- end
34
-
35
- @total_time = 0 if @total_time > 2_000_000_000
36
-
37
- @total_time
38
- end
39
-
40
- private
41
-
42
- def internal_profiler
43
- GC::Profiler
44
- end
45
-
46
- def lock
47
- self.class.lock
48
- end
49
- end
50
-
51
- # {Appsignal::NilGarbageCollectionProfiler} is a dummy profiler
52
- # that always returns 0 as the total time.
53
- # Used when we don't want any profile information
54
- #
55
- # @api private
56
- class NilGarbageCollectionProfiler
57
- def total_time
58
- 0
59
- end
60
- end
61
- end