Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(cos): include module and function on exit #11882

Merged
merged 4 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions ddtrace/debugging/_function/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from ddtrace.internal.module import origin
from ddtrace.internal.safety import _isinstance
from ddtrace.internal.utils.inspection import collect_code_objects
from ddtrace.internal.utils.inspection import functions_for_code
from ddtrace.internal.utils.inspection import linenos


Expand Down Expand Up @@ -141,13 +142,11 @@ def __init__(self, code: Optional[CodeType] = None, function: Optional[FunctionT
self.code = function.__code__ if function is not None else code

def resolve(self) -> FullyNamedFunction:
import gc

if self.function is not None:
return cast(FullyNamedFunction, self.function)

code = self.code
functions = [_ for _ in gc.get_referrers(code) if isinstance(_, FunctionType) and _.__code__ is code]
functions = functions_for_code(code)
n = len(functions)
if n == 0:
msg = f"Cannot resolve code object to function: {code}"
Expand Down Expand Up @@ -270,7 +269,11 @@ def __init__(self, module: ModuleType) -> None:
# If the module was already loaded we don't have its code object
seen_functions = set()
for _, fcp in self._fullname_index.items():
function = fcp.resolve()
try:
function = fcp.resolve()
except ValueError:
continue

if (
function not in seen_functions
and Path(cast(FunctionType, function).__code__.co_filename).resolve() == module_path
Expand Down Expand Up @@ -312,6 +315,8 @@ def by_name(self, qualname: str) -> FullyNamedFunction:
fullname = f"{self._module.__name__}.{qualname}"
try:
return self._fullname_index[fullname].resolve()
except ValueError:
pass
except KeyError:
if PYTHON_VERSION_INFO < (3, 11):
# Check if any code objects whose names match the last part of
Expand All @@ -336,7 +341,7 @@ def by_name(self, qualname: str) -> FullyNamedFunction:
return f
except ValueError:
pass
raise ValueError("Function '%s' not found" % fullname)
raise ValueError("Function '%s' not found" % fullname)

@classmethod
def from_module(cls, module: ModuleType) -> "FunctionDiscovery":
Expand Down
9 changes: 7 additions & 2 deletions ddtrace/debugging/_origin/span.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from ddtrace.internal import core
from ddtrace.internal.packages import is_user_code
from ddtrace.internal.safety import _isinstance
from ddtrace.internal.utils.inspection import functions_for_code
from ddtrace.internal.wrapping.context import WrappingContext
from ddtrace.settings.code_origin import config as co_config
from ddtrace.span import Span
Expand Down Expand Up @@ -216,8 +217,12 @@ def on_span_start(self, span: Span) -> None:

span.set_tag_str(f"_dd.code_origin.frames.{n}.file", filename)
span.set_tag_str(f"_dd.code_origin.frames.{n}.line", str(code.co_firstlineno))
# DEV: Without a function object we cannot infer the function
# and any potential class name.
try:
(f,) = functions_for_code(code)
span.set_tag_str(f"_dd.code_origin.frames.{n}.type", f.__module__)
span.set_tag_str(f"_dd.code_origin.frames.{n}.method", f.__qualname__)
except ValueError:
continue

# TODO[gab]: This will be enabled as part of the live debugger/distributed debugging
# if ld_config.enabled:
Expand Down
9 changes: 9 additions & 0 deletions ddtrace/internal/utils/inspection.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from collections import deque
from dis import findlinestarts
from functools import lru_cache
from functools import partial
from functools import singledispatch
from pathlib import Path
from types import CodeType
from types import FunctionType
from typing import Iterator
from typing import List
from typing import Set
from typing import cast

Expand Down Expand Up @@ -122,3 +124,10 @@ def collect_code_objects(code: CodeType) -> Iterator[CodeType]:
for new_code in (_ for _ in c.co_consts if isinstance(_, CodeType)):
yield new_code
q.append(new_code)


@lru_cache
P403n1x87 marked this conversation as resolved.
Show resolved Hide resolved
def functions_for_code(code: CodeType) -> List[FunctionType]:
import gc

return [_ for _ in gc.get_referrers(code) if isinstance(_, FunctionType) and _.__code__ is code]
Loading