ddtrace 0.52.0 → 0.54.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +174 -11
  3. data/ddtrace.gemspec +6 -3
  4. data/docs/DevelopmentGuide.md +1 -6
  5. data/docs/GettingStarted.md +109 -18
  6. data/docs/ProfilingDevelopment.md +2 -2
  7. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +86 -0
  8. data/ext/ddtrace_profiling_native_extension/clock_id.h +4 -0
  9. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +52 -0
  10. data/ext/ddtrace_profiling_native_extension/clock_id_noop.c +14 -0
  11. data/ext/ddtrace_profiling_native_extension/extconf.rb +177 -8
  12. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +35 -0
  13. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +3 -0
  14. data/ext/ddtrace_profiling_native_extension/profiling.c +6 -1
  15. data/lib/datadog/ci/contrib/cucumber/formatter.rb +1 -0
  16. data/lib/datadog/ci/contrib/rspec/example.rb +1 -0
  17. data/lib/datadog/ci/contrib/rspec/integration.rb +2 -2
  18. data/lib/datadog/ci/ext/environment.rb +64 -22
  19. data/lib/datadog/ci/ext/test.rb +1 -0
  20. data/lib/datadog/ci/test.rb +5 -1
  21. data/lib/datadog/contrib.rb +2 -0
  22. data/lib/datadog/core/environment/vm_cache.rb +46 -0
  23. data/lib/ddtrace/buffer.rb +28 -16
  24. data/lib/ddtrace/configuration/agent_settings_resolver.rb +131 -53
  25. data/lib/ddtrace/configuration/components.rb +1 -1
  26. data/lib/ddtrace/configuration/settings.rb +13 -3
  27. data/lib/ddtrace/context.rb +10 -2
  28. data/lib/ddtrace/contrib/action_cable/instrumentation.rb +46 -0
  29. data/lib/ddtrace/contrib/action_cable/patcher.rb +1 -0
  30. data/lib/ddtrace/contrib/action_mailer/configuration/settings.rb +32 -0
  31. data/lib/ddtrace/contrib/action_mailer/event.rb +50 -0
  32. data/lib/ddtrace/contrib/action_mailer/events/deliver.rb +54 -0
  33. data/lib/ddtrace/contrib/action_mailer/events/process.rb +41 -0
  34. data/lib/ddtrace/contrib/action_mailer/events.rb +31 -0
  35. data/lib/ddtrace/contrib/action_mailer/ext.rb +32 -0
  36. data/lib/ddtrace/contrib/action_mailer/integration.rb +45 -0
  37. data/lib/ddtrace/contrib/action_mailer/patcher.rb +27 -0
  38. data/lib/ddtrace/contrib/active_job/configuration/settings.rb +33 -0
  39. data/lib/ddtrace/contrib/active_job/event.rb +54 -0
  40. data/lib/ddtrace/contrib/active_job/events/discard.rb +46 -0
  41. data/lib/ddtrace/contrib/active_job/events/enqueue.rb +45 -0
  42. data/lib/ddtrace/contrib/active_job/events/enqueue_at.rb +45 -0
  43. data/lib/ddtrace/contrib/active_job/events/enqueue_retry.rb +47 -0
  44. data/lib/ddtrace/contrib/active_job/events/perform.rb +45 -0
  45. data/lib/ddtrace/contrib/active_job/events/retry_stopped.rb +46 -0
  46. data/lib/ddtrace/contrib/active_job/events.rb +39 -0
  47. data/lib/ddtrace/contrib/active_job/ext.rb +32 -0
  48. data/lib/ddtrace/contrib/active_job/integration.rb +46 -0
  49. data/lib/ddtrace/contrib/active_job/log_injection.rb +21 -0
  50. data/lib/ddtrace/contrib/active_job/patcher.rb +33 -0
  51. data/lib/ddtrace/contrib/auto_instrument.rb +0 -1
  52. data/lib/ddtrace/contrib/delayed_job/plugin.rb +2 -2
  53. data/lib/ddtrace/contrib/mongodb/instrumentation.rb +1 -1
  54. data/lib/ddtrace/contrib/mongodb/integration.rb +5 -0
  55. data/lib/ddtrace/contrib/rails/auto_instrument_railtie.rb +0 -1
  56. data/lib/ddtrace/contrib/rails/configuration/settings.rb +7 -0
  57. data/lib/ddtrace/contrib/rails/framework.rb +24 -1
  58. data/lib/ddtrace/contrib/rails/patcher.rb +19 -10
  59. data/lib/ddtrace/contrib/redis/instrumentation.rb +90 -0
  60. data/lib/ddtrace/contrib/redis/patcher.rb +2 -84
  61. data/lib/ddtrace/contrib/registerable.rb +0 -1
  62. data/lib/ddtrace/contrib/resque/integration.rb +1 -5
  63. data/lib/ddtrace/contrib/sidekiq/ext.rb +3 -0
  64. data/lib/ddtrace/contrib/sidekiq/integration.rb +10 -0
  65. data/lib/ddtrace/contrib/sidekiq/patcher.rb +26 -0
  66. data/lib/ddtrace/contrib/sidekiq/server_internal_tracer/heartbeat.rb +30 -0
  67. data/lib/ddtrace/contrib/sidekiq/server_internal_tracer/job_fetch.rb +30 -0
  68. data/lib/ddtrace/contrib/sidekiq/server_internal_tracer/scheduled_push.rb +29 -0
  69. data/lib/ddtrace/contrib/sinatra/env.rb +2 -1
  70. data/lib/ddtrace/contrib/sinatra/tracer.rb +15 -2
  71. data/lib/ddtrace/ext/git.rb +12 -0
  72. data/lib/ddtrace/ext/priority.rb +6 -4
  73. data/lib/ddtrace/ext/profiling.rb +8 -11
  74. data/lib/ddtrace/ext/runtime.rb +3 -0
  75. data/lib/ddtrace/ext/transport.rb +11 -0
  76. data/lib/ddtrace/metrics.rb +2 -2
  77. data/lib/ddtrace/profiling/collectors/stack.rb +112 -72
  78. data/lib/ddtrace/profiling/encoding/profile.rb +10 -2
  79. data/lib/ddtrace/profiling/events/stack.rb +13 -13
  80. data/lib/ddtrace/profiling/native_extension.rb +23 -1
  81. data/lib/ddtrace/profiling/pprof/builder.rb +8 -2
  82. data/lib/ddtrace/profiling/pprof/converter.rb +22 -9
  83. data/lib/ddtrace/profiling/pprof/stack_sample.rb +32 -9
  84. data/lib/ddtrace/profiling/pprof/template.rb +2 -2
  85. data/lib/ddtrace/profiling/scheduler.rb +20 -4
  86. data/lib/ddtrace/profiling/tasks/setup.rb +21 -13
  87. data/lib/ddtrace/profiling/trace_identifiers/ddtrace.rb +10 -9
  88. data/lib/ddtrace/profiling/trace_identifiers/helper.rb +5 -5
  89. data/lib/ddtrace/profiling/transport/http/api/endpoint.rb +8 -15
  90. data/lib/ddtrace/profiling/transport/http.rb +8 -17
  91. data/lib/ddtrace/profiling.rb +0 -2
  92. data/lib/ddtrace/runtime/metrics.rb +14 -0
  93. data/lib/ddtrace/sampler.rb +18 -8
  94. data/lib/ddtrace/sampling/rule_sampler.rb +13 -1
  95. data/lib/ddtrace/span.rb +7 -19
  96. data/lib/ddtrace/tracer.rb +1 -1
  97. data/lib/ddtrace/transport/http/adapters/net.rb +13 -3
  98. data/lib/ddtrace/transport/http/adapters/test.rb +4 -2
  99. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +23 -12
  100. data/lib/ddtrace/transport/http/builder.rb +13 -6
  101. data/lib/ddtrace/transport/http.rb +5 -11
  102. data/lib/ddtrace/utils/time.rb +11 -6
  103. data/lib/ddtrace/version.rb +2 -2
  104. data/lib/ddtrace/workers/{loop.rb → interval_loop.rb} +0 -16
  105. data/lib/ddtrace/workers/polling.rb +1 -1
  106. metadata +40 -10
  107. data/lib/ddtrace/profiling/ext/cpu.rb +0 -67
  108. data/lib/ddtrace/profiling/ext/cthread.rb +0 -156
@@ -1,5 +1,6 @@
1
1
  # typed: false
2
2
  require 'net/http'
3
+ require 'ddtrace/ext/transport'
3
4
  require 'ddtrace/transport/http/adapters/net'
4
5
 
5
6
  module Datadog
@@ -8,21 +9,29 @@ module Datadog
8
9
  module Adapters
9
10
  # Adapter for Unix sockets
10
11
  class UnixSocket < Adapters::Net
11
- DEFAULT_TIMEOUT = 1
12
-
13
12
  attr_reader \
14
- :filepath,
13
+ :filepath, # DEV(1.0): Rename to `uds_path`
15
14
  :timeout
16
15
 
17
- def initialize(filepath, options = {})
18
- @filepath = filepath
19
- @timeout = options.fetch(:timeout, DEFAULT_TIMEOUT)
16
+ alias_method :uds_path, :filepath
17
+
18
+ # @deprecated Positional parameters are deprecated. Use named parameters instead.
19
+ def initialize(uds_path = nil, **options)
20
+ @filepath = uds_path || options.fetch(:uds_path)
21
+ @timeout = options[:timeout] || Ext::Transport::UnixSocket::DEFAULT_TIMEOUT_SECONDS
22
+ end
23
+
24
+ def self.build(agent_settings)
25
+ new(
26
+ uds_path: agent_settings.uds_path,
27
+ timeout: agent_settings.timeout_seconds,
28
+ )
20
29
  end
21
30
 
22
31
  def open(&block)
23
32
  # Open connection
24
33
  connection = HTTP.new(
25
- filepath,
34
+ uds_path,
26
35
  read_timeout: timeout,
27
36
  continue_timeout: timeout
28
37
  )
@@ -31,7 +40,7 @@ module Datadog
31
40
  end
32
41
 
33
42
  def url
34
- "http+unix://#{filepath}?timeout=#{timeout}"
43
+ "http+unix://#{uds_path}?timeout=#{timeout}"
35
44
  end
36
45
 
37
46
  # Re-implements Net:HTTP with underlying Unix socket
@@ -39,19 +48,21 @@ module Datadog
39
48
  DEFAULT_TIMEOUT = 1
40
49
 
41
50
  attr_reader \
42
- :filepath,
51
+ :filepath, # DEV(1.0): Rename to `uds_path`
43
52
  :unix_socket
44
53
 
45
- def initialize(filepath, options = {})
54
+ alias_method :uds_path, :filepath
55
+
56
+ def initialize(uds_path, options = {})
46
57
  super('localhost', 80)
47
- @filepath = filepath
58
+ @filepath = uds_path
48
59
  @read_timeout = options.fetch(:read_timeout, DEFAULT_TIMEOUT)
49
60
  @continue_timeout = options.fetch(:continue_timeout, DEFAULT_TIMEOUT)
50
61
  @debug_output = options[:debug_output] if options.key?(:debug_output)
51
62
  end
52
63
 
53
64
  def connect
54
- @unix_socket = UNIXSocket.open(filepath)
65
+ @unix_socket = UNIXSocket.open(uds_path)
55
66
  @socket = ::Net::BufferedIO.new(@unix_socket).tap do |socket|
56
67
  socket.read_timeout = @read_timeout
57
68
  socket.continue_timeout = @continue_timeout
@@ -1,4 +1,5 @@
1
1
  # typed: true
2
+ require 'ddtrace/configuration/agent_settings_resolver'
2
3
  require 'ddtrace/transport/http/adapters/registry'
3
4
  require 'ddtrace/transport/http/api/map'
4
5
  require 'ddtrace/transport/http/api/instance'
@@ -33,14 +34,20 @@ module Datadog
33
34
  yield(self) if block_given?
34
35
  end
35
36
 
36
- def adapter(type, *args)
37
- @default_adapter = if type.is_a?(Symbol)
38
- registry_klass = REGISTRY.get(type)
39
- raise UnknownAdapterError, type if registry_klass.nil?
37
+ def adapter(config, *args, **kwargs)
38
+ @default_adapter = case config
39
+ when Configuration::AgentSettingsResolver::AgentSettings
40
+ registry_klass = REGISTRY.get(config.adapter)
41
+ raise UnknownAdapterError, config.adapter if registry_klass.nil?
40
42
 
41
- registry_klass.new(*args)
43
+ registry_klass.build(config)
44
+ when Symbol
45
+ registry_klass = REGISTRY.get(config)
46
+ raise UnknownAdapterError, config if registry_klass.nil?
47
+
48
+ registry_klass.new(*args, **kwargs)
42
49
  else
43
- type
50
+ config
44
51
  end
45
52
  end
46
53
 
@@ -30,13 +30,7 @@ module Datadog
30
30
  # Pass a block to override any settings.
31
31
  def default(agent_settings: Datadog::Configuration::AgentSettingsResolver::ENVIRONMENT_AGENT_SETTINGS, **options)
32
32
  new do |transport|
33
- transport.adapter(
34
- default_adapter,
35
- agent_settings.hostname,
36
- agent_settings.port,
37
- timeout: agent_settings.timeout_seconds,
38
- ssl: agent_settings.ssl
39
- )
33
+ transport.adapter(agent_settings)
40
34
  transport.headers default_headers
41
35
 
42
36
  if agent_settings.deprecated_for_removal_transport_configuration_options
@@ -80,7 +74,7 @@ module Datadog
80
74
  end
81
75
 
82
76
  def default_adapter
83
- :net_http
77
+ Ext::Transport::HTTP::ADAPTER
84
78
  end
85
79
 
86
80
  def default_hostname(logger: Datadog.logger)
@@ -111,9 +105,9 @@ module Datadog
111
105
  end
112
106
 
113
107
  # Add adapters to registry
114
- Builder::REGISTRY.set(Adapters::Net, :net_http)
115
- Builder::REGISTRY.set(Adapters::Test, :test)
116
- Builder::REGISTRY.set(Adapters::UnixSocket, :unix)
108
+ Builder::REGISTRY.set(Adapters::Net, Ext::Transport::HTTP::ADAPTER)
109
+ Builder::REGISTRY.set(Adapters::Test, Ext::Transport::Test::ADAPTER)
110
+ Builder::REGISTRY.set(Adapters::UnixSocket, Ext::Transport::UnixSocket::ADAPTER)
117
111
  end
118
112
  end
119
113
  end
@@ -7,13 +7,12 @@ module Datadog
7
7
 
8
8
  module_function
9
9
 
10
- # Current monotonic time.
11
- # Falls back to `now` if monotonic clock
12
- # is not available.
10
+ # Current monotonic time
13
11
  #
14
- # @return [Float] in seconds, since some unspecified starting point
15
- def get_time
16
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
12
+ # @param unit [Symbol] unit for the resulting value, same as ::Process#clock_gettime, defaults to :float_second
13
+ # @return [Numeric] timestamp in the requested unit, since some unspecified starting point
14
+ def get_time(unit = :float_second)
15
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
17
16
  end
18
17
 
19
18
  # Current wall time.
@@ -41,6 +40,12 @@ module Datadog
41
40
  after = get_time
42
41
  after - before
43
42
  end
43
+
44
+ def as_utc_epoch_ns(time)
45
+ # we use #to_r instead of #to_f because Float doesn't have enough precision to represent exact nanoseconds, see
46
+ # https://rubyapi.org/3.0/o/time#method-i-to_f
47
+ (time.to_r * 1_000_000_000).to_i
48
+ end
44
49
  end
45
50
  end
46
51
  end
@@ -2,8 +2,8 @@
2
2
  module Datadog
3
3
  module VERSION
4
4
  MAJOR = 0
5
- MINOR = 52
6
- PATCH = 0
5
+ MINOR = 54
6
+ PATCH = 2
7
7
  PRE = nil
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
@@ -60,15 +60,6 @@ module Datadog
60
60
  @loop_wait_time = value
61
61
  end
62
62
 
63
- def reset_loop_wait_time
64
- self.loop_wait_time = loop_base_interval
65
- end
66
-
67
- # Should the loop "back off" when there's no work?
68
- def loop_back_off?
69
- false
70
- end
71
-
72
63
  def loop_back_off!
73
64
  self.loop_wait_time = [loop_wait_time * BACK_OFF_RATIO, BACK_OFF_MAX].min
74
65
  end
@@ -106,13 +97,6 @@ module Datadog
106
97
  # There's work to do...
107
98
  # Run the task
108
99
  yield
109
-
110
- # Reset the wait interval
111
- reset_loop_wait_time if loop_back_off?
112
- elsif loop_back_off?
113
- # There's no work to do...
114
- # Back off the wait interval a bit
115
- loop_back_off!
116
100
  end
117
101
 
118
102
  # Wait for an interval, unless shutdown has been signaled.
@@ -1,6 +1,6 @@
1
1
  # typed: false
2
2
  require 'ddtrace/workers/async'
3
- require 'ddtrace/workers/loop'
3
+ require 'ddtrace/workers/interval_loop'
4
4
 
5
5
  module Datadog
6
6
  module Workers
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddtrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.52.0
4
+ version: 0.54.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-09 00:00:00.000000000 Z
11
+ date: 2022-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -25,19 +25,19 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: ffi
28
+ name: debase-ruby_core_source
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - "<="
32
32
  - !ruby/object:Gem::Version
33
- version: '1.0'
33
+ version: 0.10.14
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - "<="
39
39
  - !ruby/object:Gem::Version
40
- version: '1.0'
40
+ version: 0.10.14
41
41
  description: |
42
42
  ddtrace is Datadog’s tracing client for Ruby. It is used to trace requests
43
43
  as they flow across web servers, databases and microservices so that developers
@@ -66,7 +66,13 @@ files:
66
66
  - docs/DevelopmentGuide.md
67
67
  - docs/GettingStarted.md
68
68
  - docs/ProfilingDevelopment.md
69
+ - ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md
70
+ - ext/ddtrace_profiling_native_extension/clock_id.h
71
+ - ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c
72
+ - ext/ddtrace_profiling_native_extension/clock_id_noop.c
69
73
  - ext/ddtrace_profiling_native_extension/extconf.rb
74
+ - ext/ddtrace_profiling_native_extension/private_vm_api_access.c
75
+ - ext/ddtrace_profiling_native_extension/private_vm_api_access.h
70
76
  - ext/ddtrace_profiling_native_extension/profiling.c
71
77
  - lib/datadog/ci.rb
72
78
  - lib/datadog/ci/configuration/components.rb
@@ -99,6 +105,7 @@ files:
99
105
  - lib/datadog/core/environment/socket.rb
100
106
  - lib/datadog/core/environment/thread_count.rb
101
107
  - lib/datadog/core/environment/variable_helpers.rb
108
+ - lib/datadog/core/environment/vm_cache.rb
102
109
  - lib/ddtrace.rb
103
110
  - lib/ddtrace/analytics.rb
104
111
  - lib/ddtrace/auto_instrument.rb
@@ -130,6 +137,14 @@ files:
130
137
  - lib/ddtrace/contrib/action_cable/instrumentation.rb
131
138
  - lib/ddtrace/contrib/action_cable/integration.rb
132
139
  - lib/ddtrace/contrib/action_cable/patcher.rb
140
+ - lib/ddtrace/contrib/action_mailer/configuration/settings.rb
141
+ - lib/ddtrace/contrib/action_mailer/event.rb
142
+ - lib/ddtrace/contrib/action_mailer/events.rb
143
+ - lib/ddtrace/contrib/action_mailer/events/deliver.rb
144
+ - lib/ddtrace/contrib/action_mailer/events/process.rb
145
+ - lib/ddtrace/contrib/action_mailer/ext.rb
146
+ - lib/ddtrace/contrib/action_mailer/integration.rb
147
+ - lib/ddtrace/contrib/action_mailer/patcher.rb
133
148
  - lib/ddtrace/contrib/action_pack/action_controller/instrumentation.rb
134
149
  - lib/ddtrace/contrib/action_pack/action_controller/patcher.rb
135
150
  - lib/ddtrace/contrib/action_pack/configuration/settings.rb
@@ -148,6 +163,19 @@ files:
148
163
  - lib/ddtrace/contrib/action_view/integration.rb
149
164
  - lib/ddtrace/contrib/action_view/patcher.rb
150
165
  - lib/ddtrace/contrib/action_view/utils.rb
166
+ - lib/ddtrace/contrib/active_job/configuration/settings.rb
167
+ - lib/ddtrace/contrib/active_job/event.rb
168
+ - lib/ddtrace/contrib/active_job/events.rb
169
+ - lib/ddtrace/contrib/active_job/events/discard.rb
170
+ - lib/ddtrace/contrib/active_job/events/enqueue.rb
171
+ - lib/ddtrace/contrib/active_job/events/enqueue_at.rb
172
+ - lib/ddtrace/contrib/active_job/events/enqueue_retry.rb
173
+ - lib/ddtrace/contrib/active_job/events/perform.rb
174
+ - lib/ddtrace/contrib/active_job/events/retry_stopped.rb
175
+ - lib/ddtrace/contrib/active_job/ext.rb
176
+ - lib/ddtrace/contrib/active_job/integration.rb
177
+ - lib/ddtrace/contrib/active_job/log_injection.rb
178
+ - lib/ddtrace/contrib/active_job/patcher.rb
151
179
  - lib/ddtrace/contrib/active_model_serializers/configuration/settings.rb
152
180
  - lib/ddtrace/contrib/active_model_serializers/event.rb
153
181
  - lib/ddtrace/contrib/active_model_serializers/events.rb
@@ -352,6 +380,7 @@ files:
352
380
  - lib/ddtrace/contrib/redis/configuration/resolver.rb
353
381
  - lib/ddtrace/contrib/redis/configuration/settings.rb
354
382
  - lib/ddtrace/contrib/redis/ext.rb
383
+ - lib/ddtrace/contrib/redis/instrumentation.rb
355
384
  - lib/ddtrace/contrib/redis/integration.rb
356
385
  - lib/ddtrace/contrib/redis/patcher.rb
357
386
  - lib/ddtrace/contrib/redis/quantize.rb
@@ -392,6 +421,9 @@ files:
392
421
  - lib/ddtrace/contrib/sidekiq/ext.rb
393
422
  - lib/ddtrace/contrib/sidekiq/integration.rb
394
423
  - lib/ddtrace/contrib/sidekiq/patcher.rb
424
+ - lib/ddtrace/contrib/sidekiq/server_internal_tracer/heartbeat.rb
425
+ - lib/ddtrace/contrib/sidekiq/server_internal_tracer/job_fetch.rb
426
+ - lib/ddtrace/contrib/sidekiq/server_internal_tracer/scheduled_push.rb
395
427
  - lib/ddtrace/contrib/sidekiq/server_tracer.rb
396
428
  - lib/ddtrace/contrib/sidekiq/tracing.rb
397
429
  - lib/ddtrace/contrib/sinatra/configuration/settings.rb
@@ -480,8 +512,6 @@ files:
480
512
  - lib/ddtrace/profiling/event.rb
481
513
  - lib/ddtrace/profiling/events/stack.rb
482
514
  - lib/ddtrace/profiling/exporter.rb
483
- - lib/ddtrace/profiling/ext/cpu.rb
484
- - lib/ddtrace/profiling/ext/cthread.rb
485
515
  - lib/ddtrace/profiling/ext/forking.rb
486
516
  - lib/ddtrace/profiling/flush.rb
487
517
  - lib/ddtrace/profiling/native_extension.rb
@@ -581,7 +611,7 @@ files:
581
611
  - lib/ddtrace/worker.rb
582
612
  - lib/ddtrace/workers.rb
583
613
  - lib/ddtrace/workers/async.rb
584
- - lib/ddtrace/workers/loop.rb
614
+ - lib/ddtrace/workers/interval_loop.rb
585
615
  - lib/ddtrace/workers/polling.rb
586
616
  - lib/ddtrace/workers/queue.rb
587
617
  - lib/ddtrace/workers/runtime_metrics.rb
@@ -1,67 +0,0 @@
1
- # typed: true
2
- module Datadog
3
- module Profiling
4
- module Ext
5
- # Monkey patches Ruby's `Thread` with our `Ext::CThread` to enable CPU-time profiling
6
- module CPU
7
- # We cannot apply our CPU extension if a broken rollbar is around because that can cause customer apps to fail
8
- # with a SystemStackError: stack level too deep.
9
- #
10
- # This occurs whenever our extensions to Thread are applied BEFORE rollbar applies its own. This happens
11
- # because a loop forms: our extension tries to call Thread#initialize, but it's intercepted by rollbar, which
12
- # then tries to call the original Thread#initialize as well, but instead alls our extension, leading to stack
13
- # exhaustion.
14
- #
15
- # See https://github.com/rollbar/rollbar-gem/pull/1018 for more details on the issue
16
- ROLLBAR_INCOMPATIBLE_VERSIONS = Gem::Requirement.new('<= 3.1.1')
17
-
18
- def self.supported?
19
- unsupported_reason.nil?
20
- end
21
-
22
- def self.apply!
23
- return false unless supported?
24
-
25
- # Applying CThread to Thread will ensure any new threads
26
- # will provide a thread/clock ID for CPU timing.
27
- require 'ddtrace/profiling/ext/cthread'
28
- ::Thread.prepend(Profiling::Ext::CThread)
29
- ::Thread.singleton_class.prepend(Datadog::Profiling::Ext::WrapThreadStartFork)
30
- end
31
-
32
- def self.unsupported_reason
33
- # NOTE: Only the first matching reason is returned, so try to keep a nice order on reasons -- e.g. tell users
34
- # first that they can't use this on macOS before telling them that they have the wrong ffi version
35
-
36
- if RUBY_ENGINE == 'jruby'
37
- 'JRuby is not supported'
38
- elsif RUBY_PLATFORM.include?('darwin')
39
- 'Feature requires Linux; macOS is not supported'
40
- elsif RUBY_PLATFORM =~ /(mswin|mingw)/
41
- 'Feature requires Linux; Windows is not supported'
42
- elsif !RUBY_PLATFORM.include?('linux')
43
- "Feature requires Linux; #{RUBY_PLATFORM} is not supported"
44
- elsif Gem::Specification.find_all_by_name('rollbar', ROLLBAR_INCOMPATIBLE_VERSIONS).any?
45
- 'You have an incompatible rollbar gem version installed; ensure that you have rollbar >= 3.1.2 by ' \
46
- "adding `gem 'rollbar', '>= 3.1.2'` to your Gemfile or gems.rb file. " \
47
- 'See https://github.com/rollbar/rollbar-gem/pull/1018 for details'
48
- elsif Gem::Specification.find_all_by_name('logging').any? && logging_inherit_context_enabled?
49
- 'The `logging` gem is installed and its thread inherit context feature is enabled. ' \
50
- "Please add LOGGING_INHERIT_CONTEXT=false to your application's environment variables to disable the " \
51
- 'conflicting `logging` gem feature. ' \
52
- 'See https://github.com/TwP/logging/pull/230 for details'
53
- end
54
- end
55
-
56
- private_class_method def self.logging_inherit_context_enabled?
57
- # The logging gem provides a mechanism to disable the conflicting behavior, see
58
- # https://github.com/TwP/logging/blob/ae9872d093833b2a5a34cbe1faa4e895a81f6845/lib/logging/diagnostic_context.rb#L418
59
- # Here we check if the behavior is enabled
60
- inherit_context_configuration = ENV['LOGGING_INHERIT_CONTEXT']
61
-
62
- inherit_context_configuration.nil? || !%w[false no 0].include?(inherit_context_configuration.downcase)
63
- end
64
- end
65
- end
66
- end
67
- end
@@ -1,156 +0,0 @@
1
- # typed: false
2
- require 'ffi'
3
-
4
- module Datadog
5
- module Profiling
6
- module Ext
7
- # C-struct for retrieving clock ID from pthread
8
- class CClockId < FFI::Struct
9
- layout :value, :int
10
- end
11
-
12
- # Enables interfacing with pthread via FFI
13
- module NativePthread
14
- extend FFI::Library
15
- ffi_lib ['pthread', 'libpthread.so.0']
16
- attach_function :pthread_self, [], :ulong
17
- attach_function :pthread_getcpuclockid, [:ulong, CClockId], :int
18
-
19
- # NOTE: Only returns thread ID for thread that evaluates this call.
20
- # a.k.a. evaluating `get_pthread_thread_id(thread_a)` from within
21
- # `thread_b` will return `thread_b`'s thread ID, not `thread_a`'s.
22
- def self.get_pthread_thread_id(thread)
23
- return unless ::Thread.current == thread
24
-
25
- pthread_self
26
- end
27
-
28
- def self.get_clock_id(thread, pthread_id)
29
- return unless ::Thread.current == thread && pthread_id
30
-
31
- clock = CClockId.new
32
- clock[:value] = 0
33
- pthread_getcpuclockid(pthread_id, clock).zero? ? clock[:value] : nil
34
- end
35
- end
36
-
37
- # Extension used to enable CPU-time profiling via use of pthread's `getcpuclockid`.
38
- module CThread
39
- def self.prepended(base)
40
- # Threads that have already been created, will not have resolved
41
- # a thread/clock ID. This is because these IDs can only be resolved
42
- # from within the thread's execution context, which we do not control.
43
- #
44
- # We can mitigate this for the current thread via #update_native_ids,
45
- # since we are currently running within its execution context. We cannot
46
- # do this for any other threads that may have been created already.
47
- # (This is why it's important that CThread is applied before anything else runs.)
48
- base.current.send(:update_native_ids) if base.current.is_a?(CThread)
49
- end
50
-
51
- # Process::Waiter crash workaround:
52
- #
53
- # This is a workaround for a Ruby VM segfault (usually something like
54
- # "[BUG] Segmentation fault at 0x0000000000000008") in the affected Ruby versions.
55
- # See https://bugs.ruby-lang.org/issues/17807 and the regression tests added to this module's specs for details.
56
- #
57
- # In those Ruby versions, there's a very special subclass of `Thread` called `Process::Waiter` that causes VM
58
- # crashes whenever something tries to read its instance variables. This subclass of thread only shows up when
59
- # the `Process.detach` API gets used.
60
- # In this module's specs you can find crash regression tests that include a way of reproducing it.
61
- #
62
- # The workaround is to use `defined?` to check first if the instance variable exists. This seems to be fine
63
- # with Ruby.
64
- # Note that this crash doesn't affect `@foo ||=` nor instance variable writes (after the first write ever of any
65
- # instance variable on a `Process::Waiter`, then further reads and writes to that or any other instance are OK;
66
- # it looks like there's some lazily-created structure that is missing and did not get created).
67
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3') &&
68
- Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7')
69
- attr_reader :pthread_thread_id
70
- else
71
- def pthread_thread_id
72
- defined?(@pthread_thread_id) && @pthread_thread_id
73
- end
74
- end
75
-
76
- def initialize(*args)
77
- @pid = ::Process.pid
78
- @pthread_thread_id = nil
79
- @clock_id = nil
80
-
81
- # Wrap the work block with our own
82
- # so we can retrieve the native thread ID within the thread's context.
83
- wrapped_block = proc do |*t_args|
84
- # Set native thread ID & clock ID
85
- update_native_ids
86
- yield(*t_args)
87
- end
88
- wrapped_block.ruby2_keywords if wrapped_block.respond_to?(:ruby2_keywords, true)
89
-
90
- super(*args, &wrapped_block)
91
- end
92
- ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
93
-
94
- def cpu_time(unit = :float_second)
95
- ::Process.clock_gettime(clock_id, unit) if clock_id
96
- end
97
-
98
- def cpu_time_instrumentation_installed?
99
- # If this thread was started before this module was added to Thread OR if something caused the initialize
100
- # method above not to be properly called on new threads, this instance variable is never defined (never set to
101
- # any value at all, including nil).
102
- #
103
- # Thus, we can use @clock_id as a canary to detect a thread that has missing instrumentation, because we
104
- # know that in initialize above we always set this variable to nil.
105
- defined?(@clock_id) != nil
106
- end
107
-
108
- private
109
-
110
- def clock_id
111
- update_native_ids if forked?
112
- defined?(@clock_id) && @clock_id
113
- end
114
-
115
- def forked?
116
- ::Process.pid != (@pid ||= nil)
117
- end
118
-
119
- def update_native_ids
120
- # Can only resolve if invoked from same thread
121
- return unless ::Thread.current == self
122
-
123
- @pid = ::Process.pid
124
- @pthread_thread_id = NativePthread.get_pthread_thread_id(self)
125
- @clock_id = NativePthread.get_clock_id(self, @pthread_thread_id)
126
- end
127
- end
128
-
129
- # Threads in Ruby can be started by creating a new instance of `Thread` (or a subclass) OR by calling
130
- # `start`/`fork` on `Thread` (or a subclass).
131
- #
132
- # This module intercepts calls to `start`/`fork`, ensuring that the `update_native_ids` operation is correctly
133
- # called once the new thread starts.
134
- #
135
- # Note that unlike CThread above, this module should be prepended to the `Thread`'s singleton class, not to
136
- # the class.
137
- module WrapThreadStartFork
138
- def start(*args)
139
- # Wrap the work block with our own
140
- # so we can retrieve the native thread ID within the thread's context.
141
- wrapped_block = proc do |*t_args|
142
- # Set native thread ID & clock ID
143
- ::Thread.current.send(:update_native_ids)
144
- yield(*t_args)
145
- end
146
- wrapped_block.ruby2_keywords if wrapped_block.respond_to?(:ruby2_keywords, true)
147
-
148
- super(*args, &wrapped_block)
149
- end
150
- ruby2_keywords :start if respond_to?(:ruby2_keywords, true)
151
-
152
- alias fork start
153
- end
154
- end
155
- end
156
- end