Skip to content

Commit ac949d1

Browse files
committed
DI: report an error when instrumenting a loaded file that is not in registry
1 parent 38fee9b commit ac949d1

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

lib/datadog/di/error.rb

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ class AgentCommunicationError < Error
2727
class DITargetNotDefined < Error
2828
end
2929

30+
# Attempting to instrument a line and the file containing the line
31+
# was loaded prior to code tracking being enabled.
32+
class DITargetNotInRegistry < Error
33+
end
34+
3035
# Raised when trying to install a probe whose installation failed
3136
# earlier in the same process. This exception should contain the
3237
# original exception report from initial installation attempt.

lib/datadog/di/instrumenter.rb

+21-1
Original file line numberDiff line numberDiff line change
@@ -224,18 +224,20 @@ def hook_line(probe, &block)
224224
#
225225
# If the requested file is not in code tracker's registry,
226226
# or the code tracker does not exist at all,
227-
# do not attempt to instrumnet now.
227+
# do not attempt to instrument now.
228228
# The caller should add the line to the list of pending lines
229229
# to instrument and install the hook when the file in
230230
# question is loaded (and hopefully, by then code tracking
231231
# is active, otherwise the line will never be instrumented.)
232+
raise_if_probe_in_loaded_features(probe)
232233
raise Error::DITargetNotDefined, "File not in code tracker registry: #{probe.file}"
233234
end
234235
end
235236
elsif !permit_untargeted_trace_points
236237
# Same as previous comment, if untargeted trace points are not
237238
# explicitly defined, and we do not have code tracking, do not
238239
# instrument the method.
240+
raise_if_probe_in_loaded_features(probe)
239241
raise Error::DITargetNotDefined, "File not in code tracker registry: #{probe.file}"
240242
end
241243

@@ -352,6 +354,24 @@ def unhook(probe)
352354

353355
attr_reader :lock
354356

357+
def raise_if_probe_in_loaded_features(probe)
358+
# If the probe file is in the list of loaded files
359+
# (as per $LOADED_FEATURES, using either exact or suffix match),
360+
# raise an error indicating that
361+
# code tracker is missing the loaded file because the file
362+
# won't be loaded again (DI only works in production environments
363+
# that do not normally reload code).
364+
if $LOADED_FEATURES.include?(probe.file)
365+
raise Error::DITargetNotInRegistry, "File loaded but is not in code tracker registry: #{probe.file}"
366+
end
367+
# Ths is an expensive check
368+
$LOADED_FEATURES.each do |path|
369+
if Utils.path_matches_suffix?(path, probe.file)
370+
raise Error::DITargetNotInRegistry, "File matching probe path (#{probe.file}) was loaded and is not in code tracker registry: #{path}"
371+
end
372+
end
373+
end
374+
355375
# TODO test that this resolves qualified names e.g. A::B
356376
def symbolize_class_name(cls_name)
357377
Object.const_get(cls_name)

spec/datadog/di/instrumenter_spec.rb

+14
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,20 @@
742742
expect(observed_calls.length).to eq 1
743743
expect(observed_calls.first).to be_a(Hash)
744744
end
745+
746+
context 'when instrumenting a line in loaded but not tracked file' do
747+
let(:probe) do
748+
Datadog::DI::Probe.new(file: 'hook_line.rb', line_no: 3,
749+
id: 1, type: :log)
750+
end
751+
752+
it 'raises DITargetNotInRegistry' do
753+
expect do
754+
instrumenter.hook_line(probe) do |payload|
755+
end
756+
end.to raise_error(Datadog::DI::Error::DITargetNotInRegistry, /File matching probe path.*was loaded and is not in code tracker registry/)
757+
end
758+
end
745759
end
746760

747761
context 'when method is recursive' do

0 commit comments

Comments
 (0)