ddtrace 1.17.0 → 1.19.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +85 -2
  3. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +3 -0
  4. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +67 -52
  5. data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.c +22 -14
  6. data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.h +4 -0
  7. data/ext/ddtrace_profiling_native_extension/collectors_gc_profiling_helper.c +156 -0
  8. data/ext/ddtrace_profiling_native_extension/collectors_gc_profiling_helper.h +5 -0
  9. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +43 -102
  10. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +10 -3
  11. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.c +167 -125
  12. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.h +2 -1
  13. data/ext/ddtrace_profiling_native_extension/extconf.rb +44 -10
  14. data/ext/ddtrace_profiling_native_extension/heap_recorder.c +970 -0
  15. data/ext/ddtrace_profiling_native_extension/heap_recorder.h +155 -0
  16. data/ext/ddtrace_profiling_native_extension/helpers.h +2 -0
  17. data/ext/ddtrace_profiling_native_extension/http_transport.c +5 -2
  18. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.c +20 -0
  19. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +11 -0
  20. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +83 -18
  21. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +6 -0
  22. data/ext/ddtrace_profiling_native_extension/profiling.c +2 -0
  23. data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +147 -0
  24. data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +28 -0
  25. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +330 -13
  26. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +3 -0
  27. data/lib/datadog/appsec/component.rb +4 -1
  28. data/lib/datadog/appsec/configuration/settings.rb +4 -0
  29. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +2 -0
  30. data/lib/datadog/appsec/processor/rule_loader.rb +60 -0
  31. data/lib/datadog/appsec/remote.rb +12 -9
  32. data/lib/datadog/core/configuration/settings.rb +139 -22
  33. data/lib/datadog/core/configuration.rb +4 -0
  34. data/lib/datadog/core/remote/worker.rb +1 -0
  35. data/lib/datadog/core/telemetry/collector.rb +10 -0
  36. data/lib/datadog/core/telemetry/event.rb +2 -1
  37. data/lib/datadog/core/telemetry/ext.rb +3 -0
  38. data/lib/datadog/core/telemetry/v1/app_event.rb +8 -1
  39. data/lib/datadog/core/telemetry/v1/install_signature.rb +38 -0
  40. data/lib/datadog/core/workers/async.rb +1 -0
  41. data/lib/datadog/kit/enable_core_dumps.rb +5 -6
  42. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +7 -11
  43. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -0
  44. data/lib/datadog/profiling/component.rb +210 -18
  45. data/lib/datadog/profiling/scheduler.rb +4 -6
  46. data/lib/datadog/profiling/stack_recorder.rb +13 -2
  47. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +4 -0
  48. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -1
  49. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  50. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +24 -0
  51. data/lib/datadog/tracing/contrib/rails/auto_instrument_railtie.rb +0 -2
  52. data/lib/datadog/tracing/workers.rb +1 -0
  53. data/lib/ddtrace/version.rb +1 -1
  54. metadata +11 -6
@@ -314,32 +314,85 @@ module Datadog
314
314
 
315
315
  # Can be used to enable/disable the Datadog::Profiling.allocation_count feature.
316
316
  #
317
- # This feature is safe and enabled by default on Ruby 2.x, but has a few caveats on Ruby 3.x.
317
+ # This feature is now controlled via {:experimental_allocation_enabled}
318
+ option :allocation_counting_enabled do |o|
319
+ o.after_set do
320
+ Datadog.logger.warn(
321
+ 'The profiling.advanced.allocation_counting_enabled setting has been deprecated for removal and no ' \
322
+ 'longer does anything. Please remove it from your Datadog.configure block. ' \
323
+ 'Allocation counting is now controlled by the `experimental_allocation_enabled` setting instead.'
324
+ )
325
+ end
326
+ end
327
+
328
+ # Can be used to enable/disable collection of allocation profiles.
329
+ #
330
+ # This feature is alpha and disabled by default
318
331
  #
319
- # Caveat 1 (severe):
320
- # On Ruby versions 3.0 (all), 3.1.0 to 3.1.3, and 3.2.0 to 3.2.2 this is disabled by default because it
321
- # can trigger a VM bug that causes a segmentation fault during garbage collection of Ractors
322
- # (https://bugs.ruby-lang.org/issues/18464). We don't recommend using this feature on such Rubies.
323
- # This bug is fixed on Ruby versions 3.1.4, 3.2.3 and 3.3.0.
332
+ # @warn This feature is not supported/safe in all Rubies. Details in {Datadog::Profiling::Component} but
333
+ # in summary, this should be supported on Ruby 2.x, 3.1.4+, 3.2.3+ and 3.3.0+. Enabling it on
334
+ # unsupported Rubies may result in unexpected behaviour, including crashes.
324
335
  #
325
- # Caveat 2 (annoyance):
326
- # On all known versions of Ruby 3.x, due to https://bugs.ruby-lang.org/issues/19112, when a ractor gets
327
- # garbage collected, Ruby will disable all active tracepoints, which this feature internally relies on.
328
- # Thus this feature is only usable if you're not using Ractors.
336
+ # @default `DD_PROFILING_EXPERIMENTAL_ALLOCATION_ENABLED` environment variable as a boolean, otherwise `false`
337
+ option :experimental_allocation_enabled do |o|
338
+ o.type :bool
339
+ o.env 'DD_PROFILING_EXPERIMENTAL_ALLOCATION_ENABLED'
340
+ o.default false
341
+ end
342
+
343
+ # Can be used to enable/disable the collection of heap profiles.
329
344
  #
330
- # Caveat 3 (severe):
331
- # Ruby 3.2.0 to 3.2.2 have a bug in the newobj tracepoint (https://bugs.ruby-lang.org/issues/19482,
332
- # https://github.com/ruby/ruby/pull/7464) so that's an extra reason why it's not safe on those Rubies.
333
- # This bug is fixed on Ruby versions 3.2.3 and 3.3.0.
345
+ # This feature is alpha and disabled by default
334
346
  #
335
- # @default `true` on Ruby 2.x and 3.1.4+, 3.2.3+ and 3.3.0+; `false` for Ruby 3.0 and unpatched Rubies.
336
- option :allocation_counting_enabled do |o|
337
- o.default do
338
- RUBY_VERSION.start_with?('2.') ||
339
- (RUBY_VERSION.start_with?('3.1.') && RUBY_VERSION >= '3.1.4') ||
340
- (RUBY_VERSION.start_with?('3.2.') && RUBY_VERSION >= '3.2.3') ||
341
- RUBY_VERSION >= '3.3.'
342
- end
347
+ # @warn To enable heap profiling you are required to also enable allocation profiling.
348
+ #
349
+ # @default `DD_PROFILING_EXPERIMENTAL_HEAP_ENABLED` environment variable as a boolean, otherwise `false`
350
+ option :experimental_heap_enabled do |o|
351
+ o.type :bool
352
+ o.env 'DD_PROFILING_EXPERIMENTAL_HEAP_ENABLED'
353
+ o.default false
354
+ end
355
+
356
+ # Can be used to enable/disable the collection of heap size profiles.
357
+ #
358
+ # This feature is alpha and enabled by default when heap profiling is enabled.
359
+ #
360
+ # @warn To enable heap size profiling you are required to also enable allocation and heap profiling.
361
+ #
362
+ # @default `DD_PROFILING_EXPERIMENTAL_HEAP_SIZE_ENABLED` environment variable as a boolean, otherwise
363
+ # whatever the value of DD_PROFILING_EXPERIMENTAL_HEAP_ENABLED is.
364
+ option :experimental_heap_size_enabled do |o|
365
+ o.type :bool
366
+ o.env 'DD_PROFILING_EXPERIMENTAL_HEAP_SIZE_ENABLED'
367
+ o.default true # This gets ANDed with experimental_heap_enabled in the profiler component.
368
+ end
369
+
370
+ # Can be used to configure the allocation sampling rate: a sample will be collected every x allocations.
371
+ #
372
+ # The lower the value, the more accuracy in allocation and heap tracking but the bigger the overhead. In
373
+ # particular, a value of 1 will sample ALL allocations.
374
+ #
375
+ # @default `DD_PROFILING_EXPERIMENTAL_ALLOCATION_SAMPLE_RATE` environment variable, otherwise `50`.
376
+ option :experimental_allocation_sample_rate do |o|
377
+ o.type :int
378
+ o.env 'DD_PROFILING_EXPERIMENTAL_ALLOCATION_SAMPLE_RATE'
379
+ o.default 50
380
+ end
381
+
382
+ # Can be used to configure the heap sampling rate: a heap sample will be collected for every x allocation
383
+ # samples.
384
+ #
385
+ # The lower the value, the more accuracy in heap tracking but the bigger the overhead. In particular, a
386
+ # value of 1 will track ALL allocations samples for heap profiles.
387
+ #
388
+ # The effective heap sampling rate in terms of allocations (not allocation samples) can be calculated via
389
+ # effective_heap_sample_rate = allocation_sample_rate * heap_sample_rate.
390
+ #
391
+ # @default `DD_PROFILING_EXPERIMENTAL_HEAP_SAMPLE_RATE` environment variable, otherwise `10`.
392
+ option :experimental_heap_sample_rate do |o|
393
+ o.type :int
394
+ o.env 'DD_PROFILING_EXPERIMENTAL_HEAP_SAMPLE_RATE'
395
+ o.default 10
343
396
  end
344
397
 
345
398
  # Can be used to disable checking which version of `libmysqlclient` is being used by the `mysql2` gem.
@@ -391,6 +444,34 @@ module Datadog
391
444
  end
392
445
  end
393
446
  end
447
+
448
+ # Configures how much wall-time overhead the profiler targets. The profiler will dynamically adjust the
449
+ # interval between samples it takes so as to try and maintain the property that it spends no longer than
450
+ # this amount of wall-clock time profiling. For example, with the default value of 2%, the profiler will
451
+ # try and cause no more than 1.2 seconds per minute of overhead. Decreasing this value will reduce the
452
+ # accuracy of the data collected. Increasing will impact the application.
453
+ #
454
+ # We do not recommend tweaking this value.
455
+ #
456
+ # This value should be a percentage i.e. a number between 0 and 100, not 0 and 1.
457
+ #
458
+ # @default `DD_PROFILING_OVERHEAD_TARGET_PERCENTAGE` as a float, otherwise 2.0
459
+ option :overhead_target_percentage do |o|
460
+ o.type :float
461
+ o.env 'DD_PROFILING_OVERHEAD_TARGET_PERCENTAGE'
462
+ o.default 2.0
463
+ end
464
+
465
+ # Controls how often the profiler reports data, in seconds. Cannot be lower than 60 seconds.
466
+ #
467
+ # We do not recommend tweaking this value.
468
+ #
469
+ # @default `DD_PROFILING_UPLOAD_PERIOD` environment variable, otherwise 60
470
+ option :upload_period_seconds do |o|
471
+ o.type :int
472
+ o.env 'DD_PROFILING_UPLOAD_PERIOD'
473
+ o.default 60
474
+ end
394
475
  end
395
476
 
396
477
  # @public_api
@@ -592,6 +673,42 @@ module Datadog
592
673
  o.env Core::Telemetry::Ext::ENV_HEARTBEAT_INTERVAL
593
674
  o.default 60.0
594
675
  end
676
+
677
+ # The install id of the application.
678
+ #
679
+ # This method is used internally, by library injection.
680
+ #
681
+ # @default `DD_INSTRUMENTATION_INSTALL_ID` environment variable, otherwise `nil`.
682
+ # @return [String,nil]
683
+ # @!visibility private
684
+ option :install_id do |o|
685
+ o.type :string, nilable: true
686
+ o.env Core::Telemetry::Ext::ENV_INSTALL_ID
687
+ end
688
+
689
+ # The install type of the application.
690
+ #
691
+ # This method is used internally, by library injection.
692
+ #
693
+ # @default `DD_INSTRUMENTATION_INSTALL_TYPE` environment variable, otherwise `nil`.
694
+ # @return [String,nil]
695
+ # @!visibility private
696
+ option :install_type do |o|
697
+ o.type :string, nilable: true
698
+ o.env Core::Telemetry::Ext::ENV_INSTALL_TYPE
699
+ end
700
+
701
+ # The install time of the application.
702
+ #
703
+ # This method is used internally, by library injection.
704
+ #
705
+ # @default `DD_INSTRUMENTATION_INSTALL_TIME` environment variable, otherwise `nil`.
706
+ # @return [String,nil]
707
+ # @!visibility private
708
+ option :install_time do |o|
709
+ o.type :string, nilable: true
710
+ o.env Core::Telemetry::Ext::ENV_INSTALL_TIME
711
+ end
595
712
  end
596
713
 
597
714
  # Remote configuration
@@ -273,6 +273,10 @@ module Datadog
273
273
  def handle_interrupt_shutdown!
274
274
  logger = Datadog.logger
275
275
  shutdown_thread = Thread.new { shutdown! }
276
+ unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
277
+ shutdown_thread.name = Datadog::Core::Configuration.name
278
+ end
279
+
276
280
  print_message_treshold_seconds = 0.2
277
281
 
278
282
  slow_shutdown = shutdown_thread.join(print_message_treshold_seconds).nil?
@@ -30,6 +30,7 @@ module Datadog
30
30
 
31
31
  thread = Thread.new { poll(@interval) }
32
32
  thread.name = self.class.name unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
33
+ thread.thread_variable_set(:fork_safe, true)
33
34
  @thr = thread
34
35
 
35
36
  @started = true
@@ -9,6 +9,7 @@ require_relative '../utils/hash'
9
9
  require_relative 'v1/application'
10
10
  require_relative 'v1/dependency'
11
11
  require_relative 'v1/host'
12
+ require_relative 'v1/install_signature'
12
13
  require_relative 'v1/integration'
13
14
  require_relative 'v1/product'
14
15
  require_relative '../transport/ext'
@@ -81,6 +82,15 @@ module Datadog
81
82
  )
82
83
  end
83
84
 
85
+ # Forms a telemetry app-started install_signature object
86
+ def install_signature
87
+ Telemetry::V1::InstallSignature.new(
88
+ install_id: Datadog.configuration.dig('telemetry', 'install_id'),
89
+ install_type: Datadog.configuration.dig('telemetry', 'install_type'),
90
+ install_time: Datadog.configuration.dig('telemetry', 'install_time'),
91
+ )
92
+ end
93
+
84
94
  # Forms a telemetry app-started integrations object
85
95
  def integrations
86
96
  Datadog.registry.map do |integration|
@@ -60,7 +60,8 @@ module Datadog
60
60
  dependencies: dependencies,
61
61
  integrations: integrations,
62
62
  configuration: configurations,
63
- additional_payload: additional_payload
63
+ additional_payload: additional_payload,
64
+ install_signature: install_signature
64
65
  )
65
66
  end
66
67
 
@@ -6,6 +6,9 @@ module Datadog
6
6
  module Ext
7
7
  ENV_ENABLED = 'DD_INSTRUMENTATION_TELEMETRY_ENABLED'
8
8
  ENV_HEARTBEAT_INTERVAL = 'DD_TELEMETRY_HEARTBEAT_INTERVAL'
9
+ ENV_INSTALL_ID = 'DD_INSTRUMENTATION_INSTALL_ID'
10
+ ENV_INSTALL_TYPE = 'DD_INSTRUMENTATION_INSTALL_TYPE'
11
+ ENV_INSTALL_TIME = 'DD_INSTRUMENTATION_INSTALL_TIME'
9
12
  end
10
13
  end
11
14
  end
@@ -10,18 +10,24 @@ module Datadog
10
10
  :additional_payload,
11
11
  :configuration,
12
12
  :dependencies,
13
+ :install_signature,
13
14
  :integrations
14
15
 
15
16
  # @param additional_payload [Array<Telemetry::V1::Configuration>] List of Additional payload to track (any key
16
17
  # value not mentioned and doesn't fit under a metric)
17
18
  # @param configuration [Array<Telemetry::V1::Configuration>] List of Tracer related configuration data
18
19
  # @param dependencies [Array<Telemetry::V1::Dependency>] List of all loaded modules requested by the app
20
+ # @param install_signature [Telemetry::V1::InstallSignature] Install signature data
19
21
  # @param integrations [Array<Telemetry::V1::Integration>] List of integrations that are available within the app
20
22
  # and applicable to be traced
21
- def initialize(additional_payload: nil, configuration: nil, dependencies: nil, integrations: nil)
23
+ def initialize(
24
+ additional_payload: nil, configuration: nil, dependencies: nil, install_signature: nil,
25
+ integrations: nil
26
+ )
22
27
  @additional_payload = additional_payload
23
28
  @configuration = configuration
24
29
  @dependencies = dependencies
30
+ @install_signature = install_signature
25
31
  @integrations = integrations
26
32
  end
27
33
 
@@ -30,6 +36,7 @@ module Datadog
30
36
  hash[:additional_payload] = map_hash(@additional_payload) if @additional_payload
31
37
  hash[:configuration] = map_hash(@configuration) if @configuration
32
38
  hash[:dependencies] = map_array(@dependencies) if @dependencies
39
+ hash[:install_signature] = @install_signature.to_h if @install_signature
33
40
  hash[:integrations] = map_array(@integrations) if @integrations
34
41
  end
35
42
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module Telemetry
6
+ module V1
7
+ # Describes attributes for install signature
8
+ class InstallSignature
9
+ using Core::Utils::Hash::Refinement
10
+
11
+ attr_reader \
12
+ :install_id,
13
+ :install_type,
14
+ :install_time
15
+
16
+ # @param id [String,nil] Install ID
17
+ # @param type [String,nil] Install type
18
+ # @param type [String,nil] Install time
19
+ def initialize(install_id:, install_type:, install_time:)
20
+ @install_id = install_id
21
+ @install_type = install_type
22
+ @install_time = install_time
23
+ end
24
+
25
+ def to_h
26
+ hash = {
27
+ install_id: @install_id,
28
+ install_type: @install_type,
29
+ install_time: @install_time
30
+ }
31
+ hash.compact!
32
+ hash
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -147,6 +147,7 @@ module Datadog
147
147
  # rubocop:enable Lint/RescueException
148
148
  end
149
149
  @worker.name = self.class.name unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
150
+ @worker.thread_variable_set(:fork_safe, true)
150
151
 
151
152
  nil
152
153
  end
@@ -16,11 +16,13 @@ module Datadog
16
16
  '(Could not open /proc/sys/kernel/core_pattern)'
17
17
  end
18
18
 
19
+ enabled_status = "Maximum size: #{maximum_size} Output pattern: '#{core_pattern}'"
20
+
19
21
  if maximum_size <= 0
20
22
  Kernel.warn("[ddtrace] Could not enable core dumps on crash, maximum size is #{maximum_size} (disabled).")
21
23
  return
22
24
  elsif maximum_size == current_size
23
- Kernel.warn('[ddtrace] Core dumps already enabled, nothing to do!')
25
+ Kernel.warn("[ddtrace] Core dumps already enabled, nothing to do. #{enabled_status}")
24
26
  return
25
27
  end
26
28
 
@@ -35,12 +37,9 @@ module Datadog
35
37
  end
36
38
 
37
39
  if current_size == 0
38
- Kernel.warn("[ddtrace] Enabled core dumps. Maximum size: #{maximum_size} Output pattern: '#{core_pattern}'")
40
+ Kernel.warn("[ddtrace] Enabled core dumps. #{enabled_status}")
39
41
  else
40
- Kernel.warn(
41
- "[ddtrace] Raised core dump limit. Old size: #{current_size} " \
42
- "Maximum size: #{maximum_size} Output pattern: '#{core_pattern}'"
43
- )
42
+ Kernel.warn("[ddtrace] Raised core dump limit. Old size: #{current_size} #{enabled_status}")
44
43
  end
45
44
  end
46
45
  end
@@ -15,14 +15,15 @@ module Datadog
15
15
 
16
16
  def initialize(
17
17
  gc_profiling_enabled:,
18
- allocation_counting_enabled:,
19
18
  no_signals_workaround_enabled:,
20
19
  thread_context_collector:,
21
- idle_sampling_helper: IdleSamplingHelper.new,
20
+ dynamic_sampling_rate_overhead_target_percentage:,
21
+ allocation_sample_every:,
22
+ allocation_profiling_enabled:,
22
23
  # **NOTE**: This should only be used for testing; disabling the dynamic sampling rate will increase the
23
24
  # profiler overhead!
24
25
  dynamic_sampling_rate_enabled: true,
25
- allocation_sample_every: 0 # Currently only for testing; Setting this to > 0 can add a lot of overhead!
26
+ idle_sampling_helper: IdleSamplingHelper.new
26
27
  )
27
28
  unless dynamic_sampling_rate_enabled
28
29
  Datadog.logger.warn(
@@ -30,22 +31,16 @@ module Datadog
30
31
  )
31
32
  end
32
33
 
33
- if allocation_counting_enabled && allocation_sample_every > 0
34
- Datadog.logger.warn(
35
- "Enabled experimental allocation profiling: allocation_sample_every=#{allocation_sample_every}. This is " \
36
- 'experimental, not recommended, and will increase overhead!'
37
- )
38
- end
39
-
40
34
  self.class._native_initialize(
41
35
  self,
42
36
  thread_context_collector,
43
37
  gc_profiling_enabled,
44
38
  idle_sampling_helper,
45
- allocation_counting_enabled,
46
39
  no_signals_workaround_enabled,
47
40
  dynamic_sampling_rate_enabled,
41
+ dynamic_sampling_rate_overhead_target_percentage,
48
42
  allocation_sample_every,
43
+ allocation_profiling_enabled,
49
44
  )
50
45
  @worker_thread = nil
51
46
  @failure_exception = nil
@@ -78,6 +73,7 @@ module Datadog
78
73
  end
79
74
  end
80
75
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
76
+ @worker_thread.thread_variable_set(:fork_safe, true)
81
77
  end
82
78
 
83
79
  true
@@ -43,6 +43,7 @@ module Datadog
43
43
  end
44
44
  end
45
45
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
46
+ @worker_thread.thread_variable_set(:fork_safe, true)
46
47
  end
47
48
 
48
49
  true