File tree Expand file tree Collapse file tree 5 files changed +70
-2
lines changed Expand file tree Collapse file tree 5 files changed +70
-2
lines changed Original file line number Diff line number Diff line change @@ -139,7 +139,10 @@ def resolve(self) -> FullyNamedFunction:
139139 msg = "Cannot resolve pair with no code object"
140140 raise ValueError (msg )
141141
142- if self .function is not None :
142+ # Check that the function we have cached is not a wrapper layer that
143+ # has been unwrapped. In this case we need to resolve the new function
144+ # from the code object.
145+ if (_ := self .function ) is not None and _ .__name__ != "<unwrapped>" :
143146 return cast (FullyNamedFunction , self .function )
144147
145148 code = self .code
Original file line number Diff line number Diff line change @@ -322,6 +322,13 @@ def unwrap(wf, wrapper):
322322 # current one.
323323 f = cast (FunctionType , wf )
324324 f .__code__ = inner .__code__
325+
326+ # Mark the function as unwrapped via its name. There might be references
327+ # to this function elsewhere and this would signal that the function has
328+ # been unwrapped and that another function object is referencing the
329+ # original code object.
330+ inner .__name__ = "<unwrapped>"
331+
325332 try :
326333 # Update the link to the next layer.
327334 inner_wf = cast (WrappedFunction , inner )
Original file line number Diff line number Diff line change @@ -394,11 +394,29 @@ def wrap(self) -> None:
394394 if self ._trampoline is not None :
395395 return
396396
397+ # If the function is already universally wrapped so it's less expensive
398+ # to do the normal wrapping.
399+ if _UniversalWrappingContext .is_wrapped (self .__wrapped__ ):
400+ super ().wrap ()
401+ return
402+
397403 def trampoline (_ , args , kwargs ):
398404 with tl :
399405 f = t .cast (WrappedFunction , self .__wrapped__ )
400406 if is_wrapped_with (self .__wrapped__ , trampoline ):
407+ # If the wrapped function was instrumented with a
408+ # wrapping context before the first invocation we need
409+ # to carry that over to the original function when we
410+ # remove the trampoline.
411+ try :
412+ inner = f .__dd_wrapped__
413+ except AttributeError :
414+ inner = None
401415 f = unwrap (f , trampoline )
416+ try :
417+ f .__dd_context_wrapped__ = inner .__dd_context_wrapped__
418+ except AttributeError :
419+ pass
402420 super (LazyWrappingContext , self ).wrap ()
403421 return f (* args , ** kwargs )
404422
Original file line number Diff line number Diff line change @@ -177,7 +177,7 @@ def assert_single_snapshot(self):
177177
178178 assert len (self .test_queue ) == 1
179179
180- yield self .test_queue [ 0 ]
180+ yield self .test_queue . pop ( 0 )
181181
182182
183183@contextmanager
Original file line number Diff line number Diff line change @@ -642,6 +642,46 @@ def test_debugger_line_probe_on_lazy_wrapped_function(stuff):
642642 assert snapshot .probe .probe_id == "line-probe-lazy-wrapping"
643643
644644
645+ def test_debugger_function_probe_on_lazy_wrapped_function (stuff ):
646+ from ddtrace .internal .wrapping .context import LazyWrappingContext
647+
648+ class LWC (LazyWrappingContext ):
649+ entered = False
650+
651+ def __enter__ (self ):
652+ self .entered = True
653+ return super ().__enter__ ()
654+
655+ (c := LWC (stuff .throwexcstuff )).wrap ()
656+
657+ probe = create_snapshot_function_probe (
658+ probe_id = "function-probe-lazy-wrapping" ,
659+ module = "tests.submod.stuff" ,
660+ func_qname = "throwexcstuff" ,
661+ rate = float ("inf" ),
662+ )
663+
664+ with debugger () as d :
665+ # Test that we can re-instrument the function correctly and that we
666+ # don't accidentally instrument the temporary trampoline instead.
667+ for _ in range (10 ):
668+ d .add_probes (probe )
669+
670+ try :
671+ stuff .throwexcstuff ()
672+ except Exception :
673+ pass
674+
675+ d .remove_probes (probe )
676+
677+ assert c .entered
678+
679+ c .entered = False
680+
681+ with d .assert_single_snapshot () as snapshot :
682+ assert snapshot .probe .probe_id == "function-probe-lazy-wrapping"
683+
684+
645685def test_probe_status_logging (remote_config_worker , stuff ):
646686 assert remoteconfig_poller .status == ServiceStatus .STOPPED
647687
You can’t perform that action at this time.
0 commit comments