datadog 2.5.0 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -2
- data/ext/libdatadog_api/crashtracker.c +8 -8
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/processor/context.rb +0 -2
- data/lib/datadog/core/configuration/settings.rb +4 -4
- data/lib/datadog/di/code_tracker.rb +30 -3
- data/lib/datadog/di/component.rb +108 -0
- data/lib/datadog/di/configuration/settings.rb +69 -44
- data/lib/datadog/di/contrib/active_record.rb +11 -0
- data/lib/datadog/di/error.rb +17 -0
- data/lib/datadog/di/instrumenter.rb +27 -11
- data/lib/datadog/di/probe.rb +23 -1
- data/lib/datadog/di/probe_manager.rb +246 -0
- data/lib/datadog/di/probe_notification_builder.rb +4 -12
- data/lib/datadog/di/probe_notifier_worker.rb +68 -41
- data/lib/datadog/di/serializer.rb +143 -95
- data/lib/datadog/di/transport.rb +22 -9
- data/lib/datadog/di.rb +49 -1
- data/lib/datadog/version.rb +1 -1
- metadata +10 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c42d47b95292bfbb455a015e67700b3b8e0be052d8cfbe6f0927b6bc6602899b
|
4
|
+
data.tar.gz: ebcdd9d8d306a0edb62276a4598b4c1c80c6eeaf813ccc9d52bfea464896ef74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e071e16635f146c09550e0a835eefa1c61875339c9a64ce7283932fd646cc4401e7ea1acc0e8d0c7f7b60a20e519cce5f90eb23efdb8671b5372d02367f7bc95
|
7
|
+
data.tar.gz: 696bf465fe94fbfe76539a513b8d143373a42100ffd236b96317c5f3ffef1251d9ba3969048ad64cb87dc267dc1d7360bb1d3126a0ce9157fd3e53c5283d2130
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [2.7.0] - 2024-11-13
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
* Profiling: Enable "heap clean after GC" profiler optimization by default ([#4085][])
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
* Enable crashtracking by default ([#4083][])
|
14
|
+
* Upgrade to `libdatadog` 14.1 ([#4082][])
|
15
|
+
|
16
|
+
### Fixed
|
17
|
+
|
18
|
+
* Fix `Process.waitall` hanging and stack overflow when crashtracking enabled ([#4082][])
|
19
|
+
|
20
|
+
## [2.6.0] - 2024-11-06
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
|
24
|
+
* Core: Upgrade to libdatadog 14.0 ([#4065][])
|
25
|
+
|
26
|
+
### Fixed
|
27
|
+
|
28
|
+
* AppSec: Remove unintentional libddwaf require ([#4078][])
|
29
|
+
|
5
30
|
## [2.5.0] - 2024-11-05
|
6
31
|
|
7
32
|
### Added
|
@@ -3003,7 +3028,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
|
|
3003
3028
|
Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
3004
3029
|
|
3005
3030
|
|
3006
|
-
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.
|
3031
|
+
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.7.0...master
|
3032
|
+
[2.7.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.6.0...v2.7.0
|
3033
|
+
[2.6.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.5.0...v2.6.0
|
3007
3034
|
[2.5.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.4.0...v2.5.0
|
3008
3035
|
[2.4.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.3.0...v2.4.0
|
3009
3036
|
[2.3.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.2.0...v2.3.0
|
@@ -4445,6 +4472,11 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4445
4472
|
[#4024]: https://github.com/DataDog/dd-trace-rb/issues/4024
|
4446
4473
|
[#4027]: https://github.com/DataDog/dd-trace-rb/issues/4027
|
4447
4474
|
[#4033]: https://github.com/DataDog/dd-trace-rb/issues/4033
|
4475
|
+
[#4065]: https://github.com/DataDog/dd-trace-rb/issues/4065
|
4476
|
+
[#4078]: https://github.com/DataDog/dd-trace-rb/issues/4078
|
4477
|
+
[#4082]: https://github.com/DataDog/dd-trace-rb/issues/4082
|
4478
|
+
[#4083]: https://github.com/DataDog/dd-trace-rb/issues/4083
|
4479
|
+
[#4085]: https://github.com/DataDog/dd-trace-rb/issues/4085
|
4448
4480
|
[@AdrianLC]: https://github.com/AdrianLC
|
4449
4481
|
[@Azure7111]: https://github.com/Azure7111
|
4450
4482
|
[@BabyGroot]: https://github.com/BabyGroot
|
@@ -4596,4 +4628,4 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4596
4628
|
[@y-yagi]: https://github.com/y-yagi
|
4597
4629
|
[@yujideveloper]: https://github.com/yujideveloper
|
4598
4630
|
[@yukimurasawa]: https://github.com/yukimurasawa
|
4599
|
-
[@zachmccormick]: https://github.com/zachmccormick
|
4631
|
+
[@zachmccormick]: https://github.com/zachmccormick
|
@@ -58,21 +58,21 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
58
58
|
|
59
59
|
ddog_crasht_Config config = {
|
60
60
|
.additional_files = {},
|
61
|
-
// The Ruby VM already uses an alt stack to detect stack overflows
|
61
|
+
// @ivoanjo: The Ruby VM already uses an alt stack to detect stack overflows.
|
62
62
|
//
|
63
|
-
//
|
63
|
+
// In libdatadog < 14 with `create_alt_stack = true` I saw a segfault, such as Ruby 2.6's bug with
|
64
64
|
// "Process.detach(fork { exit! }).instance_variable_get(:@foo)" being turned into a
|
65
65
|
// "-e:1:in `instance_variable_get': stack level too deep (SystemStackError)" by Ruby.
|
66
|
-
//
|
67
66
|
// The Ruby crash handler also seems to get confused when this option is enabled and
|
68
67
|
// "Process.kill('SEGV', Process.pid)" gets run.
|
68
|
+
//
|
69
|
+
// This actually changed in libdatadog 14, so I could see no issues with `create_alt_stack = true`, but not
|
70
|
+
// overridding what Ruby set up seems a saner default to keep anyway.
|
69
71
|
.create_alt_stack = false,
|
72
|
+
.use_alt_stack = true,
|
70
73
|
.endpoint = endpoint,
|
71
74
|
.resolve_frames = DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER,
|
72
|
-
.
|
73
|
-
// Waits for crash tracker to finish reporting the issue before letting the Ruby process die; see
|
74
|
-
// https://github.com/DataDog/libdatadog/pull/477 for details
|
75
|
-
.wait_for_receiver = true,
|
75
|
+
.timeout_ms = FIX2INT(upload_timeout_seconds) * 1000,
|
76
76
|
};
|
77
77
|
|
78
78
|
ddog_crasht_Metadata metadata = {
|
@@ -97,7 +97,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
97
97
|
|
98
98
|
ddog_crasht_Result result =
|
99
99
|
action == start_action ?
|
100
|
-
|
100
|
+
ddog_crasht_init(config, receiver_config, metadata) :
|
101
101
|
ddog_crasht_update_on_fork(config, receiver_config, metadata);
|
102
102
|
|
103
103
|
// Clean up before potentially raising any exceptions
|
@@ -8,7 +8,7 @@ module Datadog
|
|
8
8
|
module LibdatadogExtconfHelpers
|
9
9
|
# Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
|
10
10
|
# may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
|
11
|
-
LIBDATADOG_VERSION = '~>
|
11
|
+
LIBDATADOG_VERSION = '~> 14.1.0.1.0'
|
12
12
|
|
13
13
|
# Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
|
14
14
|
# libdatadog are moved after the extension gets compiled.
|
@@ -518,13 +518,13 @@ module Datadog
|
|
518
518
|
# Controls if the heap profiler should attempt to clean young objects after GC, rather than just at
|
519
519
|
# serialization time. This lowers memory usage and high percentile latency.
|
520
520
|
#
|
521
|
-
# Only
|
521
|
+
# Only has effect when used together with `gc_enabled: true` and `experimental_heap_enabled: true`.
|
522
522
|
#
|
523
|
-
# @default
|
523
|
+
# @default true
|
524
524
|
option :heap_clean_after_gc_enabled do |o|
|
525
525
|
o.type :bool
|
526
526
|
o.env 'DD_PROFILING_HEAP_CLEAN_AFTER_GC_ENABLED'
|
527
|
-
o.default
|
527
|
+
o.default true
|
528
528
|
end
|
529
529
|
end
|
530
530
|
|
@@ -927,7 +927,7 @@ module Datadog
|
|
927
927
|
# Enables reporting of information when Ruby VM crashes.
|
928
928
|
option :enabled do |o|
|
929
929
|
o.type :bool
|
930
|
-
o.default
|
930
|
+
o.default true
|
931
931
|
o.env 'DD_CRASHTRACKING_ENABLED'
|
932
932
|
end
|
933
933
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# rubocop:disable Lint/AssignmentInCondition
|
4
|
+
|
3
5
|
module Datadog
|
4
6
|
module DI
|
5
7
|
# Tracks loaded Ruby code by source file and maintains a map from
|
@@ -77,6 +79,26 @@ module Datadog
|
|
77
79
|
registry[path] = tp.instruction_sequence
|
78
80
|
end
|
79
81
|
end
|
82
|
+
|
83
|
+
DI.component&.probe_manager&.install_pending_line_probes(path)
|
84
|
+
|
85
|
+
# Since this method normally is called from customer applications,
|
86
|
+
# rescue any exceptions that might not be handled to not break said
|
87
|
+
# customer applications.
|
88
|
+
rescue => exc
|
89
|
+
# TODO we do not have DI.component defined yet, remove steep:ignore
|
90
|
+
# before release.
|
91
|
+
if component = DI.component # steep:ignore
|
92
|
+
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
93
|
+
component.logger.warn("Unhandled exception in script_compiled trace point: #{exc.class}: #{exc}")
|
94
|
+
component.telemetry&.report(exc, description: "Unhandled exception in script_compiled trace point")
|
95
|
+
# TODO test this path
|
96
|
+
else
|
97
|
+
# If we don't have a component, we cannot log anything properly.
|
98
|
+
# Do not just print a warning to avoid spamming customer logs.
|
99
|
+
# Don't reraise the exception either.
|
100
|
+
# TODO test this path
|
101
|
+
end
|
80
102
|
end
|
81
103
|
end
|
82
104
|
end
|
@@ -112,15 +134,18 @@ module Datadog
|
|
112
134
|
def iseqs_for_path_suffix(suffix)
|
113
135
|
registry_lock.synchronize do
|
114
136
|
exact = registry[suffix]
|
115
|
-
return [exact] if exact
|
137
|
+
return [suffix, exact] if exact
|
116
138
|
|
117
139
|
inexact = []
|
118
140
|
registry.each do |path, iseq|
|
119
141
|
if Utils.path_matches_suffix?(path, suffix)
|
120
|
-
inexact << iseq
|
142
|
+
inexact << [path, iseq]
|
121
143
|
end
|
122
144
|
end
|
123
|
-
inexact
|
145
|
+
if inexact.length > 1
|
146
|
+
raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix"
|
147
|
+
end
|
148
|
+
inexact.first
|
124
149
|
end
|
125
150
|
end
|
126
151
|
|
@@ -164,3 +189,5 @@ module Datadog
|
|
164
189
|
end
|
165
190
|
end
|
166
191
|
end
|
192
|
+
|
193
|
+
# rubocop:enable Lint/AssignmentInCondition
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module DI
|
5
|
+
# Component for dynamic instrumentation.
|
6
|
+
#
|
7
|
+
# Only one instance of the Component should ever be active;
|
8
|
+
# if configuration is changed, the old distance should be shut down
|
9
|
+
# prior to the new instance being created.
|
10
|
+
#
|
11
|
+
# The Component instance stores all state related to DI, for example
|
12
|
+
# which probes have been retrieved via remote config,
|
13
|
+
# intalled tracepoints and so on. Component will clean up all
|
14
|
+
# resources and installed tracepoints upon shutdown.
|
15
|
+
class Component
|
16
|
+
class << self
|
17
|
+
def build(settings, agent_settings, telemetry: nil)
|
18
|
+
return unless settings.respond_to?(:dynamic_instrumentation) && settings.dynamic_instrumentation.enabled
|
19
|
+
|
20
|
+
unless settings.respond_to?(:remote) && settings.remote.enabled
|
21
|
+
Datadog.logger.debug("Dynamic Instrumentation could not be enabled because Remote Configuration Management is not available. To enable Remote Configuration, see https://docs.datadoghq.com/agent/remote_config")
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
return unless environment_supported?(settings)
|
26
|
+
|
27
|
+
new(settings, agent_settings, Datadog.logger, code_tracker: DI.code_tracker, telemetry: telemetry)
|
28
|
+
end
|
29
|
+
|
30
|
+
def build!(settings, agent_settings, telemetry: nil)
|
31
|
+
unless settings.respond_to?(:dynamic_instrumentation) && settings.dynamic_instrumentation.enabled
|
32
|
+
raise "Requested DI component but DI is not enabled in settings"
|
33
|
+
end
|
34
|
+
|
35
|
+
unless settings.respond_to?(:remote) && settings.remote.enabled
|
36
|
+
raise "Requested DI component but remote config is not enabled in settings"
|
37
|
+
end
|
38
|
+
|
39
|
+
unless environment_supported?(settings)
|
40
|
+
raise "DI does not support the environment (development or Ruby version too low or not MRI)"
|
41
|
+
end
|
42
|
+
|
43
|
+
new(settings, agent_settings, Datadog.logger, code_tracker: DI.code_tracker, telemetry: telemetry)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Checks whether the runtime environment is supported by
|
47
|
+
# dynamic instrumentation. Currently we only require that, if Rails
|
48
|
+
# is used, that Rails environment is not development because
|
49
|
+
# DI does not currently support code unloading and reloading.
|
50
|
+
def environment_supported?(settings)
|
51
|
+
# TODO add tests?
|
52
|
+
unless settings.dynamic_instrumentation.internal.development
|
53
|
+
if Datadog::Core::Environment::Execution.development?
|
54
|
+
Datadog.logger.debug("Not enabling dynamic instrumentation because we are in development environment")
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
if RUBY_ENGINE != 'ruby' || RUBY_VERSION < '2.6'
|
59
|
+
Datadog.logger.debug("Not enabling dynamic instrumentation because of unsupported Ruby version")
|
60
|
+
return false
|
61
|
+
end
|
62
|
+
true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def initialize(settings, agent_settings, logger, code_tracker: nil, telemetry: nil)
|
67
|
+
@settings = settings
|
68
|
+
@agent_settings = agent_settings
|
69
|
+
@logger = logger
|
70
|
+
@telemetry = telemetry
|
71
|
+
@redactor = Redactor.new(settings)
|
72
|
+
@serializer = Serializer.new(settings, redactor, telemetry: telemetry)
|
73
|
+
@instrumenter = Instrumenter.new(settings, serializer, logger, code_tracker: code_tracker, telemetry: telemetry)
|
74
|
+
@transport = Transport.new(agent_settings)
|
75
|
+
@probe_notifier_worker = ProbeNotifierWorker.new(settings, transport, logger, telemetry: telemetry)
|
76
|
+
@probe_notification_builder = ProbeNotificationBuilder.new(settings, serializer)
|
77
|
+
@probe_manager = ProbeManager.new(settings, instrumenter, probe_notification_builder, probe_notifier_worker, logger, telemetry: telemetry)
|
78
|
+
probe_notifier_worker.start
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_reader :settings
|
82
|
+
attr_reader :agent_settings
|
83
|
+
attr_reader :logger
|
84
|
+
attr_reader :telemetry
|
85
|
+
attr_reader :instrumenter
|
86
|
+
attr_reader :transport
|
87
|
+
attr_reader :probe_notifier_worker
|
88
|
+
attr_reader :probe_notification_builder
|
89
|
+
attr_reader :probe_manager
|
90
|
+
attr_reader :redactor
|
91
|
+
attr_reader :serializer
|
92
|
+
|
93
|
+
# Shuts down dynamic instrumentation.
|
94
|
+
#
|
95
|
+
# Removes all code hooks and stops background threads.
|
96
|
+
#
|
97
|
+
# Does not clear out the code tracker, because it's only populated
|
98
|
+
# by code when code is compiled and therefore, if the code tracker
|
99
|
+
# was replaced by a new instance, the new instance of it wouldn't have
|
100
|
+
# any of the already loaded code tracked.
|
101
|
+
def shutdown!(replacement = nil)
|
102
|
+
probe_manager.clear_hooks
|
103
|
+
probe_manager.close
|
104
|
+
probe_notifier_worker.stop
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -12,8 +12,6 @@ module Datadog
|
|
12
12
|
|
13
13
|
def self.add_settings!(base)
|
14
14
|
base.class_eval do
|
15
|
-
# The setting has "internal" prefix to prevent it from being
|
16
|
-
# prematurely turned on by customers.
|
17
15
|
settings :dynamic_instrumentation do
|
18
16
|
option :enabled do |o|
|
19
17
|
o.type :bool
|
@@ -26,48 +24,6 @@ module Datadog
|
|
26
24
|
o.default false
|
27
25
|
end
|
28
26
|
|
29
|
-
# This option instructs dynamic instrumentation to use
|
30
|
-
# untargeted trace points when installing line probes and
|
31
|
-
# code tracking is not active.
|
32
|
-
# WARNING: untargeted trace points carry a massive performance
|
33
|
-
# penalty for the entire file in which a line probe is placed.
|
34
|
-
#
|
35
|
-
# If this option is set to false, which is the default,
|
36
|
-
# dynamic instrumentation will add probes that reference
|
37
|
-
# unknown files to the list of pending probes, and when
|
38
|
-
# the respective files are loaded, the line probes will be
|
39
|
-
# installed using targeted trace points. If the file in
|
40
|
-
# question is already loaded when the probe is received
|
41
|
-
# (for example, it is in a third-party library loaded during
|
42
|
-
# application boot), and code tracking was not active when
|
43
|
-
# the file was loaded, such files will not be instrumentable
|
44
|
-
# via line probes.
|
45
|
-
#
|
46
|
-
# If this option is set to true
|
47
|
-
#
|
48
|
-
# activated, DI will in
|
49
|
-
# activated or because the files being targeted have beenIf true and code tracking is not enabled, dynamic instrumentation
|
50
|
-
# will use untargeted trace points.
|
51
|
-
# If false and code tracking is not enabled, dynamic
|
52
|
-
# instrumentation will not instrument any files loaded
|
53
|
-
# WARNING: these trace points will greatly degrade performance
|
54
|
-
# of all code in the instrumented files.
|
55
|
-
option :untargeted_trace_points do |o|
|
56
|
-
o.type :bool
|
57
|
-
o.default false
|
58
|
-
end
|
59
|
-
|
60
|
-
# If true, all of the catch-all rescue blocks in DI
|
61
|
-
# will propagate the exceptions onward.
|
62
|
-
# WARNING: for internal Datadog use only - this will break
|
63
|
-
# the DI product and potentially the library in general in
|
64
|
-
# a multitude of ways, cause resource leakage, permanent
|
65
|
-
# performance decreases, etc.
|
66
|
-
option :propagate_all_exceptions do |o|
|
67
|
-
o.type :bool
|
68
|
-
o.default false
|
69
|
-
end
|
70
|
-
|
71
27
|
# An array of variable and key names to redact in addition to
|
72
28
|
# the built-in list of identifiers.
|
73
29
|
#
|
@@ -154,6 +110,75 @@ module Datadog
|
|
154
110
|
o.type :int
|
155
111
|
o.default 20
|
156
112
|
end
|
113
|
+
|
114
|
+
# Settings in the 'internal' group are for internal Datadog
|
115
|
+
# use only, and are needed to test dynamic instrumentation or
|
116
|
+
# experiment with features not released to customers.
|
117
|
+
settings :internal do
|
118
|
+
# This option instructs dynamic instrumentation to use
|
119
|
+
# untargeted trace points when installing line probes and
|
120
|
+
# code tracking is not active.
|
121
|
+
# WARNING: untargeted trace points carry a massive performance
|
122
|
+
# penalty for the entire file in which a line probe is placed.
|
123
|
+
#
|
124
|
+
# If this option is set to false, which is the default,
|
125
|
+
# dynamic instrumentation will add probes that reference
|
126
|
+
# unknown files to the list of pending probes, and when
|
127
|
+
# the respective files are loaded, the line probes will be
|
128
|
+
# installed using targeted trace points. If the file in
|
129
|
+
# question is already loaded when the probe is received
|
130
|
+
# (for example, it is in a third-party library loaded during
|
131
|
+
# application boot), and code tracking was not active when
|
132
|
+
# the file was loaded, such files will not be instrumentable
|
133
|
+
# via line probes.
|
134
|
+
#
|
135
|
+
# If this option is set to true, dynamic instrumentation will
|
136
|
+
# install untargeted trace points for all line probes,
|
137
|
+
# regardless of whether the referenced file is loaded.
|
138
|
+
# This permits instrumenting code which was loaded prior to
|
139
|
+
# code tracking being activated and instrumenting lines when
|
140
|
+
# code tracking is not activated at all. However, untargeted
|
141
|
+
# trace points are extremely slow and will greatly degrade
|
142
|
+
# performance of *all* code executed while they are installed,
|
143
|
+
# not just the instrumentation target.
|
144
|
+
option :untargeted_trace_points do |o|
|
145
|
+
o.type :bool
|
146
|
+
o.default false
|
147
|
+
end
|
148
|
+
|
149
|
+
# If true, all of the catch-all rescue blocks in DI
|
150
|
+
# will propagate the exceptions onward.
|
151
|
+
# WARNING: for internal Datadog use only - this will break
|
152
|
+
# the DI product and potentially the library in general in
|
153
|
+
# a multitude of ways, cause resource leakage, permanent
|
154
|
+
# performance decreases, etc.
|
155
|
+
option :propagate_all_exceptions do |o|
|
156
|
+
o.type :bool
|
157
|
+
o.default false
|
158
|
+
end
|
159
|
+
|
160
|
+
# Minimum interval, in seconds, between probe status and
|
161
|
+
# snapshot submissions to the agent. Probe notifier worker will
|
162
|
+
# batch together payloads submitted during each interval.
|
163
|
+
# A longer interval reduces the overhead imposed by dynamic
|
164
|
+
# instrumentation on the application, but also increases the
|
165
|
+
# time when application code cannot run (when the batches are
|
166
|
+
# being sent out by the probe notifier worker) and creates a
|
167
|
+
# possibility of dropping payloads if the queue gets too long.
|
168
|
+
option :min_send_interval do |o|
|
169
|
+
o.type :int
|
170
|
+
o.default 3
|
171
|
+
end
|
172
|
+
|
173
|
+
# Enable dynamic instrumentation in development environments.
|
174
|
+
# Currently DI does not fully implement support for code
|
175
|
+
# unloading and reloading, and is not supported in
|
176
|
+
# non-production environments.
|
177
|
+
option :development do |o|
|
178
|
+
o.type :bool
|
179
|
+
o.default false
|
180
|
+
end
|
181
|
+
end
|
157
182
|
end
|
158
183
|
end
|
159
184
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Datadog::DI::Serializer.register(condition: lambda { |value| ActiveRecord::Base === value }) do |serializer, value, name:, depth:| # steep:ignore
|
4
|
+
# steep thinks all of the arguments are nil here
|
5
|
+
# steep:ignore:start
|
6
|
+
value_to_serialize = {
|
7
|
+
attributes: value.attributes,
|
8
|
+
}
|
9
|
+
serializer.serialize_value(value_to_serialize, depth: depth ? depth - 1 : nil, type: value.class)
|
10
|
+
# steep:ignore:end
|
11
|
+
end
|
data/lib/datadog/di/error.rb
CHANGED
@@ -26,6 +26,23 @@ module Datadog
|
|
26
26
|
# that does not in fact exist anywhere (e.g. due to a misspelling).
|
27
27
|
class DITargetNotDefined < Error
|
28
28
|
end
|
29
|
+
|
30
|
+
# Raised when trying to install a probe whose installation failed
|
31
|
+
# earlier in the same process. This exception should contain the
|
32
|
+
# original exception report from initial installation attempt.
|
33
|
+
class ProbePreviouslyFailed < Error
|
34
|
+
end
|
35
|
+
|
36
|
+
# Raised when installing a line probe and multiple files match the
|
37
|
+
# specified path suffix.
|
38
|
+
# A probe must be installed into one file only, since UI only
|
39
|
+
# supports one instrumented location for a probe.
|
40
|
+
# If multiple files match, UI cannot properly render the data from
|
41
|
+
# all of them, and arbitrarily choosing one file may be not what the
|
42
|
+
# user intended. Instrumentation will fail when multiple files match
|
43
|
+
# and the user will need to make their suffix more precise.
|
44
|
+
class MultiplePathsMatch < Error
|
45
|
+
end
|
29
46
|
end
|
30
47
|
end
|
31
48
|
end
|
@@ -54,10 +54,11 @@ module Datadog
|
|
54
54
|
#
|
55
55
|
# @api private
|
56
56
|
class Instrumenter
|
57
|
-
def initialize(settings, serializer, logger, code_tracker: nil)
|
57
|
+
def initialize(settings, serializer, logger, code_tracker: nil, telemetry: nil)
|
58
58
|
@settings = settings
|
59
59
|
@serializer = serializer
|
60
60
|
@logger = logger
|
61
|
+
@telemetry = telemetry
|
61
62
|
@code_tracker = code_tracker
|
62
63
|
|
63
64
|
@lock = Mutex.new
|
@@ -66,6 +67,7 @@ module Datadog
|
|
66
67
|
attr_reader :settings
|
67
68
|
attr_reader :serializer
|
68
69
|
attr_reader :logger
|
70
|
+
attr_reader :telemetry
|
69
71
|
attr_reader :code_tracker
|
70
72
|
|
71
73
|
# This is a substitute for Thread::Backtrace::Location
|
@@ -172,12 +174,12 @@ module Datadog
|
|
172
174
|
# we use mock objects and the methods may be mocked with
|
173
175
|
# individual invocations, yielding different return values on
|
174
176
|
# different calls to the same method.
|
175
|
-
permit_untargeted_trace_points = settings.dynamic_instrumentation.untargeted_trace_points
|
177
|
+
permit_untargeted_trace_points = settings.dynamic_instrumentation.internal.untargeted_trace_points
|
176
178
|
|
177
179
|
iseq = nil
|
178
180
|
if code_tracker
|
179
|
-
|
180
|
-
unless
|
181
|
+
ret = code_tracker.iseqs_for_path_suffix(probe.file) # steep:ignore
|
182
|
+
unless ret
|
181
183
|
if permit_untargeted_trace_points
|
182
184
|
# Continue withoout targeting the trace point.
|
183
185
|
# This is going to cause a serious performance penalty for
|
@@ -204,6 +206,10 @@ module Datadog
|
|
204
206
|
raise Error::DITargetNotDefined, "File not in code tracker registry: #{probe.file}"
|
205
207
|
end
|
206
208
|
|
209
|
+
if ret
|
210
|
+
actual_path, iseq = ret
|
211
|
+
end
|
212
|
+
|
207
213
|
# If trace point is not targeted, we only need one trace point per file.
|
208
214
|
# Creating a trace point for each probe does work but the performance
|
209
215
|
# penalty will be taken for each trace point defined in the file.
|
@@ -217,18 +223,26 @@ module Datadog
|
|
217
223
|
# this optimization just yet and create a trace point for each probe.
|
218
224
|
|
219
225
|
tp = TracePoint.new(:line) do |tp|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
if
|
225
|
-
|
226
|
-
|
226
|
+
begin
|
227
|
+
# If trace point is not targeted, we must verify that the invocation
|
228
|
+
# is the file & line that we want, because untargeted trace points
|
229
|
+
# are invoked for *each* line of Ruby executed.
|
230
|
+
if iseq || tp.lineno == probe.line_no && probe.file_matches?(tp.path)
|
231
|
+
if rate_limiter.nil? || rate_limiter.allow?
|
232
|
+
# & is to stop steep complaints, block is always present here.
|
233
|
+
block&.call(probe: probe, trace_point: tp, caller_locations: caller_locations)
|
234
|
+
end
|
227
235
|
end
|
236
|
+
rescue => exc
|
237
|
+
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
238
|
+
logger.warn("Unhandled exception in line trace point: #{exc.class}: #{exc}")
|
239
|
+
telemetry&.report(exc, description: "Unhandled exception in line trace point")
|
240
|
+
# TODO test this path
|
228
241
|
end
|
229
242
|
rescue => exc
|
230
243
|
raise if settings.dynamic_instrumentation.propagate_all_exceptions
|
231
244
|
logger.warn("Unhandled exception in line trace point: #{exc.class}: #{exc}")
|
245
|
+
telemetry&.report(exc, description: "Unhandled exception in line trace point")
|
232
246
|
# TODO test this path
|
233
247
|
end
|
234
248
|
|
@@ -244,6 +258,8 @@ module Datadog
|
|
244
258
|
end
|
245
259
|
|
246
260
|
probe.instrumentation_trace_point = tp
|
261
|
+
# actual_path could be nil if we don't use targeted trace points.
|
262
|
+
probe.instrumented_path = actual_path
|
247
263
|
|
248
264
|
if iseq
|
249
265
|
tp.enable(target: iseq, target_line: line_no)
|
data/lib/datadog/di/probe.rb
CHANGED
@@ -47,6 +47,10 @@ module Datadog
|
|
47
47
|
raise ArgumentError, "Probe contains both line number and method name: #{id}"
|
48
48
|
end
|
49
49
|
|
50
|
+
if line_no && !file
|
51
|
+
raise ArgumentError, "Probe contains line number but not file: #{id}"
|
52
|
+
end
|
53
|
+
|
50
54
|
if type_name && !method_name || method_name && !type_name
|
51
55
|
raise ArgumentError, "Partial method probe definition: #{id}"
|
52
56
|
end
|
@@ -71,6 +75,8 @@ module Datadog
|
|
71
75
|
|
72
76
|
@rate_limit = rate_limit || (@capture_snapshot ? 1 : 5000)
|
73
77
|
@rate_limiter = Datadog::Core::TokenBucket.new(@rate_limit)
|
78
|
+
|
79
|
+
@emitting_notified = false
|
74
80
|
end
|
75
81
|
|
76
82
|
attr_reader :id
|
@@ -101,7 +107,10 @@ module Datadog
|
|
101
107
|
# method or for stack traversal purposes?), therefore we do not check
|
102
108
|
# for file name/path presence here and just consider the line number.
|
103
109
|
def line?
|
104
|
-
|
110
|
+
# Constructor checks that file is given if line number is given,
|
111
|
+
# but for safety, check again here since we somehow got a probe with
|
112
|
+
# a line number but no file in the wild.
|
113
|
+
!!(file && line_no)
|
105
114
|
end
|
106
115
|
|
107
116
|
# Returns whether the probe is a method probe.
|
@@ -157,6 +166,19 @@ module Datadog
|
|
157
166
|
# Line trace point for line probes. Normally this would be a targeted
|
158
167
|
# trace point.
|
159
168
|
attr_accessor :instrumentation_trace_point
|
169
|
+
|
170
|
+
# Actual path to the file instrumented by the probe, for line probes,
|
171
|
+
# when code tracking is available and line trace point is targeted.
|
172
|
+
# For untargeted line trace points instrumented path will be nil.
|
173
|
+
attr_accessor :instrumented_path
|
174
|
+
|
175
|
+
# TODO emitting_notified reads and writes should in theory be locked,
|
176
|
+
# however since DI is only implemented for MRI in practice the missing
|
177
|
+
# locking should not cause issues.
|
178
|
+
attr_writer :emitting_notified
|
179
|
+
def emitting_notified?
|
180
|
+
!!@emitting_notified
|
181
|
+
end
|
160
182
|
end
|
161
183
|
end
|
162
184
|
end
|