appsignal 3.1.3 → 3.1.4

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