datadog 2.6.0 → 2.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23fdb98e800ed71c28238b4b61e7f1187b27833d8e98c14e0f60d43ce838d4cd
4
- data.tar.gz: ed99ae40d1e94aedcf27f8bcdeb70059c02ad116e8648621e37d22d5be1f69ad
3
+ metadata.gz: c42d47b95292bfbb455a015e67700b3b8e0be052d8cfbe6f0927b6bc6602899b
4
+ data.tar.gz: ebcdd9d8d306a0edb62276a4598b4c1c80c6eeaf813ccc9d52bfea464896ef74
5
5
  SHA512:
6
- metadata.gz: df3d8bc5efb89bb7a893b006484d3d826acf7beb57d6abbf8b25c2e9a277a95f7f681d6262d5dd08485aaf5526c677edf641711435e67674f1043c1fbf1ba55f
7
- data.tar.gz: 02b397ae85a8ced3f6112beb899826c86ba137be36c229a85150cdc708dadd937e9860775bdfd02d7475bb4f7c890b1019901ec146acb5ad21bdd192906aa339
6
+ metadata.gz: e071e16635f146c09550e0a835eefa1c61875339c9a64ce7283932fd646cc4401e7ea1acc0e8d0c7f7b60a20e519cce5f90eb23efdb8671b5372d02367f7bc95
7
+ data.tar.gz: 696bf465fe94fbfe76539a513b8d143373a42100ffd236b96317c5f3ffef1251d9ba3969048ad64cb87dc267dc1d7360bb1d3126a0ce9157fd3e53c5283d2130
data/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
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
+
5
20
  ## [2.6.0] - 2024-11-06
6
21
 
7
22
  ### Changed
@@ -3013,7 +3028,8 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
3013
3028
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3014
3029
 
3015
3030
 
3016
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.6.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
3017
3033
  [2.6.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.5.0...v2.6.0
3018
3034
  [2.5.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.4.0...v2.5.0
3019
3035
  [2.4.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.3.0...v2.4.0
@@ -4458,6 +4474,9 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
4458
4474
  [#4033]: https://github.com/DataDog/dd-trace-rb/issues/4033
4459
4475
  [#4065]: https://github.com/DataDog/dd-trace-rb/issues/4065
4460
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
4461
4480
  [@AdrianLC]: https://github.com/AdrianLC
4462
4481
  [@Azure7111]: https://github.com/Azure7111
4463
4482
  [@BabyGroot]: https://github.com/BabyGroot
@@ -58,16 +58,18 @@ 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,
70
- .use_alt_stack = true, // NOTE: This is a no-op in libdatadog 14.0; should be fixed in a future version
72
+ .use_alt_stack = true,
71
73
  .endpoint = endpoint,
72
74
  .resolve_frames = DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER,
73
75
  .timeout_ms = FIX2INT(upload_timeout_seconds) * 1000,
@@ -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 = '~> 14.0.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.
@@ -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