From 8d95b4ed0ee4df70af762a836be5a156da6b04a7 Mon Sep 17 00:00:00 2001 From: "Gabriele N. Tornetta" Date: Wed, 29 Oct 2025 13:09:20 +0000 Subject: [PATCH] test(symdb): add fork behaviour test We add a test to the Symbol DB test to ensure that we have the expected fork behaviour. By design, we want only the main process and the first fork child to emit Symbol DB payloads. --- ddtrace/internal/symbol_db/remoteconfig.py | 2 +- tests/internal/symbol_db/test_symbols.py | 34 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/ddtrace/internal/symbol_db/remoteconfig.py b/ddtrace/internal/symbol_db/remoteconfig.py index feccaa806c1..78eca1a087d 100644 --- a/ddtrace/internal/symbol_db/remoteconfig.py +++ b/ddtrace/internal/symbol_db/remoteconfig.py @@ -19,7 +19,7 @@ log = get_logger(__name__) -def _rc_callback(data: t.List[Payload], test_tracer=None): +def _rc_callback(data: t.Sequence[Payload]): if get_ancestor_runtime_id() is not None and has_forked(): log.debug("[PID %d] SymDB: Disabling Symbol DB in forked process", os.getpid()) # We assume that forking is being used for spawning child worker diff --git a/tests/internal/symbol_db/test_symbols.py b/tests/internal/symbol_db/test_symbols.py index bfd89a20c8f..55709ef25f5 100644 --- a/tests/internal/symbol_db/test_symbols.py +++ b/tests/internal/symbol_db/test_symbols.py @@ -286,3 +286,37 @@ def get_scope(contexts, name): scope = get_scope(contexts, "tests.submod.stuff") assert scope["scope_type"] == ScopeType.MODULE assert scope["name"] == "tests.submod.stuff" + + +@pytest.mark.subprocess(ddtrace_run=True) +def test_symbols_fork_uploads(): + """ + Test that we disable Symbol DB on processes that are not the main one nor + the first fork child. + """ + import os + + from ddtrace.internal import forksafe + from ddtrace.internal.remoteconfig import ConfigMetadata + from ddtrace.internal.remoteconfig import Payload + from ddtrace.internal.runtime import get_ancestor_runtime_id + from ddtrace.internal.symbol_db.remoteconfig import _rc_callback + from ddtrace.internal.symbol_db.symbols import SymbolDatabaseUploader + + SymbolDatabaseUploader.install() + + pids = [] + rc_data = [Payload(ConfigMetadata("test", "symdb", "hash", 0, 0), "test", None)] + + for _ in range(10): + if not (pid := os.fork()): + _rc_callback(rc_data) + assert SymbolDatabaseUploader.is_installed() != ( + get_ancestor_runtime_id() is not None and forksafe.has_forked() + ) + os._exit(0) + + pids.append(pid) + + for pid in pids: + os.waitpid(pid, 0)