datadog 2.1.0 → 2.3.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.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -1
  3. data/ext/datadog_profiling_loader/extconf.rb +15 -15
  4. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  5. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  6. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  7. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +132 -44
  8. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +49 -26
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  10. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  11. data/ext/datadog_profiling_native_extension/collectors_stack.c +90 -37
  12. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  13. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +81 -19
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  15. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +110 -0
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +57 -0
  17. data/ext/datadog_profiling_native_extension/extconf.rb +69 -62
  18. data/ext/datadog_profiling_native_extension/heap_recorder.c +34 -6
  19. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  20. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  21. data/ext/datadog_profiling_native_extension/http_transport.c +3 -3
  22. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  23. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  24. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -126
  25. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -138
  26. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -11
  27. data/ext/datadog_profiling_native_extension/profiling.c +0 -2
  28. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  29. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  30. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
  31. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  32. data/ext/datadog_profiling_native_extension/stack_recorder.c +27 -8
  33. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
  34. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  35. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  36. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +20 -7
  37. data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
  38. data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
  39. data/ext/libdatadog_api/extconf.rb +108 -0
  40. data/ext/libdatadog_api/macos_development.md +26 -0
  41. data/ext/libdatadog_extconf_helpers.rb +130 -0
  42. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
  43. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
  44. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
  45. data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
  46. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  47. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  48. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  49. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
  50. data/lib/datadog/appsec/extensions.rb +1 -0
  51. data/lib/datadog/appsec/processor/actions.rb +1 -1
  52. data/lib/datadog/appsec/response.rb +15 -1
  53. data/lib/datadog/appsec.rb +1 -0
  54. data/lib/datadog/core/configuration/components.rb +17 -12
  55. data/lib/datadog/core/configuration/settings.rb +93 -7
  56. data/lib/datadog/core/configuration.rb +3 -17
  57. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  58. data/lib/datadog/core/crashtracking/component.rb +111 -0
  59. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  60. data/lib/datadog/core/deprecations.rb +58 -0
  61. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  62. data/lib/datadog/core/environment/yjit.rb +5 -0
  63. data/lib/datadog/core/runtime/ext.rb +1 -0
  64. data/lib/datadog/core/runtime/metrics.rb +6 -0
  65. data/lib/datadog/core/telemetry/component.rb +154 -0
  66. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  67. data/lib/datadog/core/telemetry/event.rb +132 -26
  68. data/lib/datadog/core/telemetry/ext.rb +3 -0
  69. data/lib/datadog/core/telemetry/http/adapters/net.rb +11 -13
  70. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  71. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  72. data/lib/datadog/core/telemetry/logging.rb +35 -0
  73. data/lib/datadog/core/telemetry/metric.rb +167 -0
  74. data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
  75. data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
  76. data/lib/datadog/core/telemetry/request.rb +1 -1
  77. data/lib/datadog/core/telemetry/worker.rb +173 -0
  78. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  79. data/lib/datadog/core/utils/only_once_successful.rb +76 -0
  80. data/lib/datadog/core.rb +2 -19
  81. data/lib/datadog/kit/appsec/events.rb +2 -4
  82. data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
  83. data/lib/datadog/opentelemetry/sdk/span_processor.rb +15 -2
  84. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  85. data/lib/datadog/profiling/collectors/code_provenance.rb +24 -11
  86. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
  87. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  88. data/lib/datadog/profiling/collectors/info.rb +3 -3
  89. data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
  90. data/lib/datadog/profiling/component.rb +85 -90
  91. data/lib/datadog/profiling/exporter.rb +3 -3
  92. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
  93. data/lib/datadog/profiling/ext.rb +21 -21
  94. data/lib/datadog/profiling/flush.rb +1 -1
  95. data/lib/datadog/profiling/http_transport.rb +8 -6
  96. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  97. data/lib/datadog/profiling/preload.rb +1 -1
  98. data/lib/datadog/profiling/profiler.rb +5 -8
  99. data/lib/datadog/profiling/scheduler.rb +31 -25
  100. data/lib/datadog/profiling/tag_builder.rb +2 -2
  101. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  102. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  103. data/lib/datadog/profiling.rb +5 -5
  104. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  105. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
  106. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
  107. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
  108. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
  109. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
  110. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
  111. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
  112. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  113. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  114. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  115. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  116. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  117. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  118. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  119. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  120. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  121. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  122. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +2 -1
  123. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
  124. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
  125. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
  126. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
  127. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
  128. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
  129. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
  130. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
  131. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
  132. data/lib/datadog/tracing/contrib/analytics.rb +5 -0
  133. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  134. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
  135. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
  136. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
  137. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +28 -0
  138. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
  139. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
  140. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
  141. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
  142. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
  143. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
  144. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
  145. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
  146. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
  147. data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
  148. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  149. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  150. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  151. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  152. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  153. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  154. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  155. data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
  156. data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
  157. data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
  158. data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
  159. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  160. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  161. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  162. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  163. data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
  164. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  165. data/lib/datadog/tracing/distributed/propagation.rb +9 -2
  166. data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
  167. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  168. data/lib/datadog/tracing/metadata/ext.rb +4 -0
  169. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  170. data/lib/datadog/tracing/span.rb +9 -2
  171. data/lib/datadog/tracing/span_event.rb +41 -0
  172. data/lib/datadog/tracing/span_operation.rb +9 -4
  173. data/lib/datadog/tracing/trace_operation.rb +7 -3
  174. data/lib/datadog/tracing/trace_segment.rb +4 -1
  175. data/lib/datadog/tracing/tracer.rb +9 -2
  176. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  177. data/lib/datadog/tracing.rb +5 -1
  178. data/lib/datadog/version.rb +2 -2
  179. metadata +43 -12
  180. data/lib/datadog/core/telemetry/client.rb +0 -95
  181. data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
  182. data/lib/datadog/profiling/crashtracker.rb +0 -91
  183. data/lib/datadog/profiling/ext/forking.rb +0 -98
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
- require 'json'
3
+ require "set"
4
+ require "json"
5
5
 
6
6
  module Datadog
7
7
  module Profiling
@@ -14,7 +14,7 @@ module Datadog
14
14
  #
15
15
  # This class acts both as a collector (collecting data) as well as a recorder (records/serializes it)
16
16
  class CodeProvenance
17
- def initialize(standard_library_path: RbConfig::CONFIG.fetch('rubylibdir'))
17
+ def initialize(standard_library_path: RbConfig::CONFIG.fetch("rubylibdir"))
18
18
  @libraries_by_name = {}
19
19
  @libraries_by_path = {}
20
20
  @seen_files = Set.new
@@ -22,8 +22,8 @@ module Datadog
22
22
 
23
23
  record_library(
24
24
  Library.new(
25
- kind: 'standard library',
26
- name: 'stdlib',
25
+ kind: "standard library",
26
+ name: "stdlib",
27
27
  version: RUBY_VERSION,
28
28
  path: standard_library_path,
29
29
  )
@@ -79,7 +79,7 @@ module Datadog
79
79
  loaded_specs.each do |spec|
80
80
  next if libraries_by_name.key?(spec.name)
81
81
 
82
- record_library(Library.new(kind: 'library', name: spec.name, version: spec.version, path: spec.gem_dir))
82
+ record_library(Library.new(kind: "library", name: spec.name, version: spec.version, path: spec.gem_dir))
83
83
  recorded_library = true
84
84
  end
85
85
 
@@ -92,25 +92,38 @@ module Datadog
92
92
 
93
93
  seen_files << file_path
94
94
 
95
- _, found_library = libraries_by_path.find { |library_path, _| file_path.start_with?(library_path) }
96
- seen_libraries << found_library if found_library
95
+ # NOTE: Don't use .find, it allocates a lot more memory (see commit that added this note for details)
96
+ libraries_by_path.any? do |library_path, library|
97
+ seen_libraries << library if file_path.start_with?(library_path)
98
+ end
97
99
  end
98
100
  end
99
101
 
100
102
  # Represents metadata we have for a ruby gem
103
+ #
104
+ # Important note: This class gets encoded to JSON with the built-in JSON gem. But, we've found that in some
105
+ # buggy cases, some Ruby gems monkey patch the built-in JSON gem and forget to call #to_json, and instead
106
+ # encode this class instance-field-by-instance-field.
107
+ #
108
+ # Thus, this class was setup to match the JSON output. Take this into consideration if you are adding new
109
+ # fields. (Also, we have a spec for this)
101
110
  class Library
102
- attr_reader :kind, :name, :version, :path
111
+ attr_reader :kind, :name, :version
103
112
 
104
113
  def initialize(kind:, name:, version:, path:)
105
114
  @kind = kind.freeze
106
115
  @name = name.dup.freeze
107
116
  @version = version.to_s.dup.freeze
108
- @path = path.dup.freeze
117
+ @paths = [path.dup.freeze].freeze
109
118
  freeze
110
119
  end
111
120
 
112
121
  def to_json(arg = nil)
113
- { kind: @kind, name: @name, version: @version, paths: [@path] }.to_json(arg)
122
+ {kind: @kind, name: @name, version: @version, paths: @paths}.to_json(arg)
123
+ end
124
+
125
+ def path
126
+ @paths.first
114
127
  end
115
128
  end
116
129
  end
@@ -21,6 +21,7 @@ module Datadog
21
21
  thread_context_collector:,
22
22
  dynamic_sampling_rate_overhead_target_percentage:,
23
23
  allocation_profiling_enabled:,
24
+ allocation_counting_enabled:,
24
25
  # **NOTE**: This should only be used for testing; disabling the dynamic sampling rate will increase the
25
26
  # profiler overhead!
26
27
  dynamic_sampling_rate_enabled: true,
@@ -29,7 +30,7 @@ module Datadog
29
30
  )
30
31
  unless dynamic_sampling_rate_enabled
31
32
  Datadog.logger.warn(
32
- 'Profiling dynamic sampling rate disabled. This should only be used for testing, and will increase overhead!'
33
+ "Profiling dynamic sampling rate disabled. This should only be used for testing, and will increase overhead!"
33
34
  )
34
35
  end
35
36
 
@@ -42,6 +43,7 @@ module Datadog
42
43
  dynamic_sampling_rate_enabled,
43
44
  dynamic_sampling_rate_overhead_target_percentage,
44
45
  allocation_profiling_enabled,
46
+ allocation_counting_enabled,
45
47
  skip_idle_samples_for_testing,
46
48
  )
47
49
  @worker_thread = nil
@@ -54,27 +56,25 @@ module Datadog
54
56
 
55
57
  def start(on_failure_proc: nil)
56
58
  @start_stop_mutex.synchronize do
57
- return if @worker_thread && @worker_thread.alive?
59
+ return if @worker_thread&.alive?
58
60
 
59
61
  Datadog.logger.debug { "Starting thread for: #{self}" }
60
62
 
61
63
  @idle_sampling_helper.start
62
64
 
63
65
  @worker_thread = Thread.new do
64
- begin
65
- Thread.current.name = self.class.name
66
-
67
- self.class._native_sampling_loop(self)
68
-
69
- Datadog.logger.debug('CpuAndWallTimeWorker thread stopping cleanly')
70
- rescue Exception => e # rubocop:disable Lint/RescueException
71
- @failure_exception = e
72
- Datadog.logger.warn(
73
- 'CpuAndWallTimeWorker thread error. ' \
74
- "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
75
- )
76
- on_failure_proc&.call
77
- end
66
+ Thread.current.name = self.class.name
67
+
68
+ self.class._native_sampling_loop(self)
69
+
70
+ Datadog.logger.debug("CpuAndWallTimeWorker thread stopping cleanly")
71
+ rescue Exception => e # rubocop:disable Lint/RescueException
72
+ @failure_exception = e
73
+ Datadog.logger.warn(
74
+ "CpuAndWallTimeWorker thread error. " \
75
+ "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
76
+ )
77
+ on_failure_proc&.call
78
78
  end
79
79
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
80
80
  @worker_thread.thread_variable_set(:fork_safe, true)
@@ -85,7 +85,7 @@ module Datadog
85
85
 
86
86
  def stop
87
87
  @start_stop_mutex.synchronize do
88
- Datadog.logger.debug('Requesting CpuAndWallTimeWorker thread shut down')
88
+ Datadog.logger.debug("Requesting CpuAndWallTimeWorker thread shut down")
89
89
 
90
90
  @idle_sampling_helper.stop
91
91
 
@@ -21,7 +21,7 @@ module Datadog
21
21
 
22
22
  def start
23
23
  @start_stop_mutex.synchronize do
24
- return if @worker_thread && @worker_thread.alive?
24
+ return if @worker_thread&.alive?
25
25
 
26
26
  Datadog.logger.debug { "Starting thread for: #{self}" }
27
27
 
@@ -30,19 +30,17 @@ module Datadog
30
30
  self.class._native_reset(self)
31
31
 
32
32
  @worker_thread = Thread.new do
33
- begin
34
- Thread.current.name = self.class.name
33
+ Thread.current.name = self.class.name
35
34
 
36
- self.class._native_idle_sampling_loop(self)
35
+ self.class._native_idle_sampling_loop(self)
37
36
 
38
- Datadog.logger.debug('IdleSamplingHelper thread stopping cleanly')
39
- rescue Exception => e # rubocop:disable Lint/RescueException
40
- @failure_exception = e
41
- Datadog.logger.warn(
42
- 'IdleSamplingHelper thread error. ' \
43
- "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
44
- )
45
- end
37
+ Datadog.logger.debug("IdleSamplingHelper thread stopping cleanly")
38
+ rescue Exception => e # rubocop:disable Lint/RescueException
39
+ @failure_exception = e
40
+ Datadog.logger.warn(
41
+ "IdleSamplingHelper thread error. " \
42
+ "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
43
+ )
46
44
  end
47
45
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
48
46
  @worker_thread.thread_variable_set(:fork_safe, true)
@@ -53,7 +51,7 @@ module Datadog
53
51
 
54
52
  def stop
55
53
  @start_stop_mutex.synchronize do
56
- Datadog.logger.debug('Requesting IdleSamplingHelper thread shut down')
54
+ Datadog.logger.debug("Requesting IdleSamplingHelper thread shut down")
57
55
 
58
56
  return unless @worker_thread
59
57
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
- require 'time'
3
+ require "set"
4
+ require "time"
5
5
 
6
6
  module Datadog
7
7
  module Profiling
@@ -61,7 +61,7 @@ module Datadog
61
61
 
62
62
  def collect_profiler_info(settings)
63
63
  unless @profiler_info
64
- lib_datadog_gem = ::Gem.loaded_specs['libdatadog']
64
+ lib_datadog_gem = ::Gem.loaded_specs["libdatadog"]
65
65
  @profiler_info = {
66
66
  # TODO: If profiling is extracted and its version diverges from the datadog gem, this is inaccurate.
67
67
  # Update if this ever occurs.
@@ -48,12 +48,14 @@ module Datadog
48
48
  private
49
49
 
50
50
  def safely_extract_context_key_from(tracer)
51
- provider = tracer && tracer.respond_to?(:provider) && tracer.provider
51
+ return unless tracer
52
+
53
+ provider = tracer.respond_to?(:provider) && tracer.provider
52
54
 
53
55
  return unless provider
54
56
 
55
57
  context = provider.instance_variable_get(:@context)
56
- context && context.instance_variable_get(:@key)
58
+ context&.instance_variable_get(:@key)
57
59
  end
58
60
  end
59
61
  end
@@ -8,7 +8,7 @@ module Datadog
8
8
  # * Code Hotspots panel in the trace viewer, as well as scoping a profile down to a span
9
9
  # * Endpoint aggregation in the profiler UX, including normalization (resource per endpoint call)
10
10
  def self.build_profiler_component(settings:, agent_settings:, optional_tracer:) # rubocop:disable Metrics/MethodLength
11
- return [nil, { profiling_enabled: false }] unless settings.profiling.enabled
11
+ return [nil, {profiling_enabled: false}] unless settings.profiling.enabled
12
12
 
13
13
  # Workaround for weird dependency direction: the Core::Configuration::Components class currently has a
14
14
  # dependency on individual products, in this case the Profiler.
@@ -27,9 +27,9 @@ module Datadog
27
27
  # On the other hand, if datadog/core is loaded by a different product and no general `require 'datadog'` is
28
28
  # done, then profiling may not be loaded, and thus to avoid this issue we do a require here (which is a
29
29
  # no-op if profiling is already loaded).
30
- require_relative '../profiling'
30
+ require_relative "../profiling"
31
31
 
32
- return [nil, { profiling_enabled: false }] unless Profiling.supported?
32
+ return [nil, {profiling_enabled: false}] unless Profiling.supported?
33
33
 
34
34
  # Activate forking extensions
35
35
  Profiling::Tasks::Setup.new.run
@@ -47,7 +47,7 @@ module Datadog
47
47
  upload_period_seconds = [60, settings.profiling.advanced.upload_period_seconds].max
48
48
 
49
49
  recorder = Datadog::Profiling::StackRecorder.new(
50
- cpu_time_enabled: RUBY_PLATFORM.include?('linux'), # Only supported on Linux currently
50
+ cpu_time_enabled: RUBY_PLATFORM.include?("linux"), # Only supported on Linux currently
51
51
  alloc_samples_enabled: allocation_profiling_enabled,
52
52
  heap_samples_enabled: heap_profiling_enabled,
53
53
  heap_size_enabled: heap_size_profiling_enabled,
@@ -61,6 +61,7 @@ module Datadog
61
61
  thread_context_collector: thread_context_collector,
62
62
  dynamic_sampling_rate_overhead_target_percentage: overhead_target_percentage,
63
63
  allocation_profiling_enabled: allocation_profiling_enabled,
64
+ allocation_counting_enabled: settings.profiling.advanced.allocation_counting_enabled,
64
65
  )
65
66
 
66
67
  internal_metadata = {
@@ -72,10 +73,13 @@ module Datadog
72
73
  exporter = build_profiler_exporter(settings, recorder, worker, internal_metadata: internal_metadata)
73
74
  transport = build_profiler_transport(settings, agent_settings)
74
75
  scheduler = Profiling::Scheduler.new(exporter: exporter, transport: transport, interval: upload_period_seconds)
75
- crashtracker = build_crashtracker(settings, transport)
76
- profiler = Profiling::Profiler.new(worker: worker, scheduler: scheduler, optional_crashtracker: crashtracker)
76
+ profiler = Profiling::Profiler.new(worker: worker, scheduler: scheduler)
77
77
 
78
- [profiler, { profiling_enabled: true }]
78
+ if dir_interruption_workaround_enabled?(settings, no_signals_workaround_enabled)
79
+ Datadog::Profiling::Ext::DirMonkeyPatches.apply!
80
+ end
81
+
82
+ [profiler, {profiling_enabled: true}]
79
83
  end
80
84
 
81
85
  private_class_method def self.build_thread_context_collector(settings, recorder, optional_tracer, timeline_enabled)
@@ -112,28 +116,6 @@ module Datadog
112
116
  )
113
117
  end
114
118
 
115
- private_class_method def self.build_crashtracker(settings, transport)
116
- return unless settings.profiling.advanced.experimental_crash_tracking_enabled
117
-
118
- # By default, the transport is an instance of HttpTransport, which validates the configuration and makes
119
- # it available for us to use here.
120
- # But we support overriding the transport with a user-specific one, which may e.g. write stuff to a file,
121
- # and thus can't really provide a valid configuration to talk to a Datadog agent. Thus, in this situation,
122
- # we can't use the crashtracker, even if enabled.
123
- unless transport.respond_to?(:exporter_configuration)
124
- Datadog.logger.warn(
125
- 'Cannot enable profiling crash tracking as a custom settings.profiling.exporter.transport is configured'
126
- )
127
- return
128
- end
129
-
130
- Datadog::Profiling::Crashtracker.new(
131
- exporter_configuration: transport.exporter_configuration,
132
- tags: Datadog::Profiling::TagBuilder.call(settings: settings),
133
- upload_timeout_seconds: settings.profiling.upload.timeout_seconds,
134
- )
135
- end
136
-
137
119
  private_class_method def self.enable_gc_profiling?(settings)
138
120
  return false unless settings.profiling.advanced.gc_enabled
139
121
 
@@ -142,19 +124,19 @@ module Datadog
142
124
  # that causes a segmentation fault during garbage collection of Ractors
143
125
  # (https://bugs.ruby-lang.org/issues/18464). We don't allow enabling gc profiling on such Rubies.
144
126
  # This bug is fixed on Ruby versions 3.1.4, 3.2.3 and 3.3.0.
145
- if RUBY_VERSION.start_with?('3.0.') ||
146
- (RUBY_VERSION.start_with?('3.1.') && RUBY_VERSION < '3.1.4') ||
147
- (RUBY_VERSION.start_with?('3.2.') && RUBY_VERSION < '3.2.3')
127
+ if RUBY_VERSION.start_with?("3.0.") ||
128
+ (RUBY_VERSION.start_with?("3.1.") && RUBY_VERSION < "3.1.4") ||
129
+ (RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3")
148
130
  Datadog.logger.warn(
149
- "Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling GC profiling would cause "\
150
- 'crashes (https://bugs.ruby-lang.org/issues/18464). GC profiling has been disabled.'
131
+ "Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling GC profiling would cause " \
132
+ "crashes (https://bugs.ruby-lang.org/issues/18464). GC profiling has been disabled."
151
133
  )
152
134
  return false
153
- elsif RUBY_VERSION.start_with?('3.')
135
+ elsif RUBY_VERSION.start_with?("3.")
154
136
  Datadog.logger.debug(
155
- 'In all known versions of Ruby 3.x, using Ractors may result in GC profiling unexpectedly ' \
156
- 'stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your ' \
157
- 'application stability or performance. This does not happen if Ractors are not used.'
137
+ "In all known versions of Ruby 3.x, using Ractors may result in GC profiling unexpectedly " \
138
+ "stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
139
+ "application stability or performance. This does not happen if Ractors are not used."
158
140
  )
159
141
  end
160
142
 
@@ -178,11 +160,11 @@ module Datadog
178
160
  # Ruby 3.2.0 to 3.2.2 have a bug in the newobj tracepoint (https://bugs.ruby-lang.org/issues/19482,
179
161
  # https://github.com/ruby/ruby/pull/7464) that makes this crash in any configuration. This bug is
180
162
  # fixed on Ruby versions 3.2.3 and 3.3.0.
181
- if RUBY_VERSION.start_with?('3.2.') && RUBY_VERSION < '3.2.3'
163
+ if RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3"
182
164
  Datadog.logger.warn(
183
- 'Allocation profiling is not supported in Ruby versions 3.2.0, 3.2.1 and 3.2.2 and will be forcibly '\
184
- 'disabled. This is due to a VM bug that can lead to crashes (https://bugs.ruby-lang.org/issues/19482). '\
185
- 'Other Ruby versions do not suffer from this issue.'
165
+ "Allocation profiling is not supported in Ruby versions 3.2.0, 3.2.1 and 3.2.2 and will be forcibly " \
166
+ "disabled. This is due to a VM bug that can lead to crashes (https://bugs.ruby-lang.org/issues/19482). " \
167
+ "Other Ruby versions do not suffer from this issue."
186
168
  )
187
169
  return false
188
170
  end
@@ -192,26 +174,26 @@ module Datadog
192
174
  # that causes a segmentation fault during garbage collection of Ractors
193
175
  # (https://bugs.ruby-lang.org/issues/18464). We don't recommend using this feature on such Rubies.
194
176
  # This bug is fixed on Ruby versions 3.1.4, 3.2.3 and 3.3.0.
195
- if RUBY_VERSION.start_with?('3.0.') ||
196
- (RUBY_VERSION.start_with?('3.1.') && RUBY_VERSION < '3.1.4') ||
197
- (RUBY_VERSION.start_with?('3.2.') && RUBY_VERSION < '3.2.3')
177
+ if RUBY_VERSION.start_with?("3.0.") ||
178
+ (RUBY_VERSION.start_with?("3.1.") && RUBY_VERSION < "3.1.4") ||
179
+ (RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3")
198
180
  Datadog.logger.warn(
199
- "Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling allocation profiling while using "\
200
- 'Ractors may cause unexpected issues, including crashes (https://bugs.ruby-lang.org/issues/18464). '\
201
- 'This does not happen if Ractors are not used.'
181
+ "Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling allocation profiling while using " \
182
+ "Ractors may cause unexpected issues, including crashes (https://bugs.ruby-lang.org/issues/18464). " \
183
+ "This does not happen if Ractors are not used."
202
184
  )
203
185
  # ANNOYANCE - Only with Ractors
204
186
  # On all known versions of Ruby 3.x, due to https://bugs.ruby-lang.org/issues/19112, when a ractor gets
205
187
  # garbage collected, Ruby will disable all active tracepoints, which this feature internally relies on.
206
- elsif RUBY_VERSION.start_with?('3.')
188
+ elsif RUBY_VERSION.start_with?("3.")
207
189
  Datadog.logger.warn(
208
- 'In all known versions of Ruby 3.x, using Ractors may result in allocation profiling unexpectedly ' \
209
- 'stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your ' \
210
- 'application stability or performance. This does not happen if Ractors are not used.'
190
+ "In all known versions of Ruby 3.x, using Ractors may result in allocation profiling unexpectedly " \
191
+ "stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
192
+ "application stability or performance. This does not happen if Ractors are not used."
211
193
  )
212
194
  end
213
195
 
214
- Datadog.logger.debug('Enabled allocation profiling')
196
+ Datadog.logger.debug("Enabled allocation profiling")
215
197
 
216
198
  true
217
199
  end
@@ -221,33 +203,33 @@ module Datadog
221
203
 
222
204
  return false unless heap_profiling_enabled
223
205
 
224
- if RUBY_VERSION.start_with?('2.') && RUBY_VERSION < '2.7'
206
+ if RUBY_VERSION.start_with?("2.") && RUBY_VERSION < "2.7"
225
207
  Datadog.logger.warn(
226
- 'Heap profiling currently relies on features introduced in Ruby 2.7 and will be forcibly disabled. '\
227
- 'Please upgrade to Ruby >= 2.7 in order to use this feature.'
208
+ "Heap profiling currently relies on features introduced in Ruby 2.7 and will be forcibly disabled. " \
209
+ "Please upgrade to Ruby >= 2.7 in order to use this feature."
228
210
  )
229
211
  return false
230
212
  end
231
213
 
232
- if RUBY_VERSION < '3.1'
214
+ if RUBY_VERSION < "3.1"
233
215
  Datadog.logger.debug(
234
216
  "Current Ruby version (#{RUBY_VERSION}) supports forced object recycling which has a bug that the " \
235
- 'heap profiler is forced to work around to remain accurate. This workaround requires force-setting '\
217
+ "heap profiler is forced to work around to remain accurate. This workaround requires force-setting " \
236
218
  "the SEEN_OBJ_ID flag on objects that should have it but don't. Full details can be found in " \
237
- 'https://github.com/DataDog/dd-trace-rb/pull/3360. This workaround should be safe but can be ' \
238
- 'bypassed by disabling the heap profiler or upgrading to Ruby >= 3.1 where forced object recycling ' \
239
- 'was completely removed (https://bugs.ruby-lang.org/issues/18290).'
219
+ "https://github.com/DataDog/dd-trace-rb/pull/3360. This workaround should be safe but can be " \
220
+ "bypassed by disabling the heap profiler or upgrading to Ruby >= 3.1 where forced object recycling " \
221
+ "was completely removed (https://bugs.ruby-lang.org/issues/18290)."
240
222
  )
241
223
  end
242
224
 
243
225
  unless allocation_profiling_enabled
244
226
  raise ArgumentError,
245
- 'Heap profiling requires allocation profiling to be enabled'
227
+ "Heap profiling requires allocation profiling to be enabled"
246
228
  end
247
229
 
248
230
  Datadog.logger.warn(
249
231
  "Enabled experimental heap profiling: heap_sample_rate=#{heap_sample_rate}. This is experimental, not " \
250
- 'recommended, and will increase overhead!'
232
+ "recommended, and will increase overhead!"
251
233
  )
252
234
 
253
235
  true
@@ -259,7 +241,7 @@ module Datadog
259
241
  return false unless heap_profiling_enabled && heap_size_profiling_enabled
260
242
 
261
243
  Datadog.logger.warn(
262
- 'Enabled experimental heap size profiling. This is experimental, not recommended, and will increase overhead!'
244
+ "Enabled experimental heap size profiling. This is experimental, not recommended, and will increase overhead!"
263
245
  )
264
246
 
265
247
  true
@@ -267,12 +249,12 @@ module Datadog
267
249
 
268
250
  private_class_method def self.no_signals_workaround_enabled?(settings) # rubocop:disable Metrics/MethodLength
269
251
  setting_value = settings.profiling.advanced.no_signals_workaround_enabled
270
- legacy_ruby_that_should_use_workaround = RUBY_VERSION.start_with?('2.5.')
252
+ legacy_ruby_that_should_use_workaround = RUBY_VERSION.start_with?("2.5.")
271
253
 
272
254
  unless [true, false, :auto].include?(setting_value)
273
255
  Datadog.logger.error(
274
256
  "Ignoring invalid value for profiling no_signals_workaround_enabled setting: #{setting_value.inspect}. " \
275
- 'Valid options are `true`, `false` or (default) `:auto`.'
257
+ "Valid options are `true`, `false` or (default) `:auto`."
276
258
  )
277
259
 
278
260
  setting_value = :auto
@@ -282,10 +264,10 @@ module Datadog
282
264
  if legacy_ruby_that_should_use_workaround
283
265
  Datadog.logger.warn(
284
266
  'The profiling "no signals" workaround has been disabled via configuration on a legacy Ruby version ' \
285
- '(< 2.6). This is not recommended ' \
286
- 'in production environments, as due to limitations in Ruby APIs, we suspect it may lead to crashes ' \
287
- 'in very rare situations. Please report any issues you run into to Datadog support or ' \
288
- 'via <https://github.com/datadog/dd-trace-rb/issues/new>!'
267
+ "(< 2.6). This is not recommended " \
268
+ "in production environments, as due to limitations in Ruby APIs, we suspect it may lead to crashes " \
269
+ "in very rare situations. Please report any issues you run into to Datadog support or " \
270
+ "via <https://github.com/datadog/dd-trace-rb/issues/new>!"
289
271
  )
290
272
  else
291
273
  Datadog.logger.warn('Profiling "no signals" workaround disabled via configuration')
@@ -307,30 +289,30 @@ module Datadog
307
289
  # We don't warn users in this situation because "upgrade your Ruby" is not a great warning
308
290
  return true if legacy_ruby_that_should_use_workaround
309
291
 
310
- if Gem.loaded_specs['mysql2'] && incompatible_libmysqlclient_version?(settings)
292
+ if Gem.loaded_specs["mysql2"] && incompatible_libmysqlclient_version?(settings)
311
293
  Datadog.logger.warn(
312
294
  'Enabling the profiling "no signals" workaround because an incompatible version of the mysql2 gem is ' \
313
- 'installed. Profiling data will have lower quality. ' \
314
- 'To fix this, upgrade the libmysqlclient in your OS image to version 8.0.0 or above.'
295
+ "installed. Profiling data will have lower quality. " \
296
+ "To fix this, upgrade the libmysqlclient in your OS image to version 8.0.0 or above."
315
297
  )
316
298
  return true
317
299
  end
318
300
 
319
- if Gem.loaded_specs['rugged']
301
+ if Gem.loaded_specs["rugged"]
320
302
  Datadog.logger.warn(
321
303
  'Enabling the profiling "no signals" workaround because the rugged gem is installed. ' \
322
- 'This is needed because some operations on this gem are currently incompatible with the normal working mode ' \
323
- 'of the profiler, as detailed in <https://github.com/datadog/dd-trace-rb/issues/2721>. ' \
324
- 'Profiling data will have lower quality.'
304
+ "This is needed because some operations on this gem are currently incompatible with the normal working mode " \
305
+ "of the profiler, as detailed in <https://github.com/datadog/dd-trace-rb/issues/2721>. " \
306
+ "Profiling data will have lower quality."
325
307
  )
326
308
  return true
327
309
  end
328
310
 
329
- if (defined?(::PhusionPassenger) || Gem.loaded_specs['passenger']) && incompatible_passenger_version?
311
+ if (defined?(::PhusionPassenger) || Gem.loaded_specs["passenger"]) && incompatible_passenger_version?
330
312
  Datadog.logger.warn(
331
313
  'Enabling the profiling "no signals" workaround because an incompatible version of the passenger gem is ' \
332
- 'installed. Profiling data will have lower quality.' \
333
- 'To fix this, upgrade the passenger gem to version 6.0.19 or above.'
314
+ "installed. Profiling data will have lower quality." \
315
+ "To fix this, upgrade the passenger gem to version 6.0.19 or above."
334
316
  )
335
317
  return true
336
318
  end
@@ -351,11 +333,11 @@ module Datadog
351
333
  return true if settings.profiling.advanced.skip_mysql2_check
352
334
 
353
335
  Datadog.logger.debug(
354
- 'Requiring `mysql2` to check if the `libmysqlclient` version it uses is compatible with profiling'
336
+ "Requiring `mysql2` to check if the `libmysqlclient` version it uses is compatible with profiling"
355
337
  )
356
338
 
357
339
  begin
358
- require 'mysql2'
340
+ require "mysql2"
359
341
 
360
342
  # The mysql2-aurora gem likes to monkey patch itself in replacement of Mysql2::Client, and uses
361
343
  # `method_missing` to delegate to the original BUT unfortunately does not implement `respond_to_missing?` and
@@ -376,18 +358,18 @@ module Datadog
376
358
  libmysqlclient_version = Gem::Version.new(info[:version])
377
359
 
378
360
  compatible =
379
- libmysqlclient_version >= Gem::Version.new('8.0.0') ||
361
+ libmysqlclient_version >= Gem::Version.new("8.0.0") ||
380
362
  looks_like_mariadb?(info, libmysqlclient_version)
381
363
 
382
364
  Datadog.logger.debug(
383
- "The `mysql2` gem is using #{compatible ? 'a compatible' : 'an incompatible'} version of " \
365
+ "The `mysql2` gem is using #{compatible ? "a compatible" : "an incompatible"} version of " \
384
366
  "the `libmysqlclient` library (#{libmysqlclient_version})"
385
367
  )
386
368
 
387
369
  !compatible
388
370
  rescue StandardError, LoadError => e
389
371
  Datadog.logger.warn(
390
- 'Failed to probe `mysql2` gem information. ' \
372
+ "Failed to probe `mysql2` gem information. " \
391
373
  "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
392
374
  )
393
375
 
@@ -397,8 +379,12 @@ module Datadog
397
379
 
398
380
  # See https://github.com/datadog/dd-trace-rb/issues/2976 for details.
399
381
  private_class_method def self.incompatible_passenger_version?
400
- if Gem.loaded_specs['passenger']
401
- Gem.loaded_specs['passenger'].version < Gem::Version.new('6.0.19')
382
+ first_compatible_version = Gem::Version.new("6.0.19")
383
+
384
+ if Gem.loaded_specs["passenger"]
385
+ Gem.loaded_specs["passenger"].version < first_compatible_version
386
+ elsif defined?(PhusionPassenger::VERSION_STRING)
387
+ Gem::Version.new(PhusionPassenger::VERSION_STRING) < first_compatible_version
402
388
  else
403
389
  true
404
390
  end
@@ -409,7 +395,7 @@ module Datadog
409
395
  overhead_target_percentage
410
396
  else
411
397
  Datadog.logger.error(
412
- 'Ignoring invalid value for profiling overhead_target_percentage setting: ' \
398
+ "Ignoring invalid value for profiling overhead_target_percentage setting: " \
413
399
  "#{overhead_target_percentage.inspect}. Falling back to default value."
414
400
  )
415
401
 
@@ -442,8 +428,17 @@ module Datadog
442
428
  header_version = Gem::Version.new(info[:header_version]) if info[:header_version]
443
429
 
444
430
  !!(header_version &&
445
- libmysqlclient_version < Gem::Version.new('5.0.0') &&
446
- header_version >= Gem::Version.new('10.0.0'))
431
+ libmysqlclient_version < Gem::Version.new("5.0.0") &&
432
+ header_version >= Gem::Version.new("10.0.0"))
433
+ end
434
+
435
+ private_class_method def self.dir_interruption_workaround_enabled?(settings, no_signals_workaround_enabled)
436
+ return false if no_signals_workaround_enabled
437
+
438
+ # NOTE: In the future this method will evolve to check for Ruby versions affected and not apply the workaround
439
+ # when it's not needed but currently all known Ruby versions are affected.
440
+
441
+ settings.profiling.advanced.dir_interruption_workaround_enabled
447
442
  end
448
443
  end
449
444
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'ext'
4
- require_relative 'tag_builder'
3
+ require_relative "ext"
4
+ require_relative "tag_builder"
5
5
 
6
6
  module Datadog
7
7
  module Profiling
@@ -61,7 +61,7 @@ module Datadog
61
61
  @last_flush_finish_at = finish
62
62
 
63
63
  if duration_below_threshold?(start, finish)
64
- Datadog.logger.debug('Skipped exporting profiling events as profile duration is below minimum')
64
+ Datadog.logger.debug("Skipped exporting profiling events as profile duration is below minimum")
65
65
  return
66
66
  end
67
67