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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c1aa5227f68791f80fe6deb7f5a8b53e5adff15a7e443f78788f827888945c83
4
- data.tar.gz: 7931209b3685744b1aa17f709766d9a5da91b5b34987a8ac975032ebee0a7caa
3
+ metadata.gz: c42d47b95292bfbb455a015e67700b3b8e0be052d8cfbe6f0927b6bc6602899b
4
+ data.tar.gz: ebcdd9d8d306a0edb62276a4598b4c1c80c6eeaf813ccc9d52bfea464896ef74
5
5
  SHA512:
6
- metadata.gz: '081740cc882d48f62d18f8a823696339346ad574f4ac5c67cd64430ee37f347ec33d6c536e3790f3697ea3a6cb72f2275a6b5b14808e48a4246cc20786fcc20b'
7
- data.tar.gz: c51018d1655343c234e45d72ab5dc9a27ebc465e253462eec483faf862e3c6fde0bbba6d4f3c1ca6656a6d7c33525a5942481a69beee7a7d889cbbe75c900ff7
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.5.0...master
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 so the crash handler must not overwrite it.
61
+ // @ivoanjo: The Ruby VM already uses an alt stack to detect stack overflows.
62
62
  //
63
- // @ivoanjo: Specifically, with `create_alt_stack = true` I saw a segfault, such as Ruby 2.6's bug with
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
- .timeout_secs = FIX2INT(upload_timeout_seconds),
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
- ddog_crasht_init_with_receiver(config, receiver_config, metadata) :
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 = '~> 13.1.0.1.0'
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.
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'libddwaf'
4
-
5
3
  module Datadog
6
4
  module AppSec
7
5
  class Processor
@@ -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 takes effect when used together with `gc_enabled: true` and `experimental_heap_enabled: true`.
521
+ # Only has effect when used together with `gc_enabled: true` and `experimental_heap_enabled: true`.
522
522
  #
523
- # @default false
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 false
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 false
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
@@ -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
- iseq = code_tracker.iseqs_for_path_suffix(probe.file).first # steep:ignore
180
- unless iseq
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
- # If trace point is not targeted, we must verify that the invocation
221
- # is the file & line that we want, because untargeted trace points
222
- # are invoked for *each* line of Ruby executed.
223
- if iseq || tp.lineno == probe.line_no && probe.file_matches?(tp.path)
224
- if rate_limiter.nil? || rate_limiter.allow?
225
- # & is to stop steep complaints, block is always present here.
226
- block&.call(probe: probe, trace_point: tp, caller_locations: caller_locations)
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)
@@ -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
- !line_no.nil?
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