https://github.com/python/cpython/commit/298bef3a0043dfea9cf7ffe319183c4b8f0a24f5
commit: 298bef3a0043dfea9cf7ffe319183c4b8f0a24f5
branch: main
author: stratakis <[email protected]>
committer: vstinner <[email protected]>
date: 2026-08-07T14:15:36Z
summary:
gh-98894: Fix dtrace tests in shared builds (#153372)
Generate SystemTap probe definitions targeting libpython for shared
builds and use centralized USDT probe object discovery for readelf
and BPFTrace.
files:
M Lib/test/dtracedata/call_stack.stp
M Lib/test/dtracedata/gc.stp
M Lib/test/test_dtrace.py
diff --git a/Lib/test/dtracedata/call_stack.stp
b/Lib/test/dtracedata/call_stack.stp
index 54082c202f66aa4..d4455fc8489af20 100644
--- a/Lib/test/dtracedata/call_stack.stp
+++ b/Lib/test/dtracedata/call_stack.stp
@@ -10,7 +10,7 @@ function basename:string(path:string)
return last_token;
}
-probe process.mark("function__entry")
+probe @PYTHON_SYSTEMTAP_PROBE@("function__entry")
{
funcname = user_string($arg2);
@@ -19,7 +19,8 @@ probe process.mark("function__entry")
}
}
-probe process.mark("function__entry"), process.mark("function__return")
+probe @PYTHON_SYSTEMTAP_PROBE@("function__entry"),
+ @PYTHON_SYSTEMTAP_PROBE@("function__return")
{
filename = user_string($arg1);
funcname = user_string($arg2);
@@ -31,7 +32,7 @@ probe process.mark("function__entry"),
process.mark("function__return")
}
}
-probe process.mark("function__return")
+probe @PYTHON_SYSTEMTAP_PROBE@("function__return")
{
funcname = user_string($arg2);
diff --git a/Lib/test/dtracedata/gc.stp b/Lib/test/dtracedata/gc.stp
index 162c6d3a2209b98..11d2715e6c721a8 100644
--- a/Lib/test/dtracedata/gc.stp
+++ b/Lib/test/dtracedata/gc.stp
@@ -1,6 +1,6 @@
global tracing
-probe process.mark("function__entry")
+probe @PYTHON_SYSTEMTAP_PROBE@("function__entry")
{
funcname = user_string($arg2);
@@ -9,14 +9,15 @@ probe process.mark("function__entry")
}
}
-probe process.mark("gc__start"), process.mark("gc__done")
+probe @PYTHON_SYSTEMTAP_PROBE@("gc__start"),
+ @PYTHON_SYSTEMTAP_PROBE@("gc__done")
{
if (tracing) {
printf("%d\t%s:%ld\n", gettimeofday_us(), $$name, $arg1);
}
}
-probe process.mark("function__return")
+probe @PYTHON_SYSTEMTAP_PROBE@("function__return")
{
funcname = user_string($arg2);
diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py
index 30731b8f90ac14d..4967a18053057b3 100644
--- a/Lib/test/test_dtrace.py
+++ b/Lib/test/test_dtrace.py
@@ -6,11 +6,13 @@
import subprocess
import sys
import sysconfig
+import tempfile
import types
import unittest
from test import support
from test.support import findfile, MS_WINDOWS
+from test.support import os_helper
if not support.has_subprocess_support:
@@ -25,6 +27,31 @@ def abspath(filename):
return os.path.abspath(findfile(filename, subdir="dtracedata"))
+def get_probe_binary():
+ binary = sys.executable
+ if sysconfig.get_config_var("Py_ENABLE_SHARED"):
+ lib_dir = sysconfig.get_config_var("LIBDIR")
+ if not lib_dir or sysconfig.is_python_build():
+ lib_dir = os.path.abspath(os.path.dirname(sys.executable))
+
+ lib_names = []
+ for name in (
+ sysconfig.get_config_var("INSTSONAME"),
+ sysconfig.get_config_var("LDLIBRARY"),
+ ):
+ if name and name not in lib_names:
+ lib_names.append(name)
+
+ if lib_dir:
+ for name in lib_names:
+ libpython_path = os.path.join(lib_dir, name)
+ if os.path.exists(libpython_path):
+ binary = libpython_path
+ break
+
+ return binary
+
+
def normalize_trace_output(output):
"""Normalize DTrace output for comparison.
@@ -180,6 +207,45 @@ class DTraceBackend(TraceBackend):
class SystemTapBackend(TraceBackend):
EXTENSION = ".stp"
COMMAND = ["stap", "-g"]
+ PROBE_PLACEHOLDER = "@PYTHON_SYSTEMTAP_PROBE@"
+
+ @staticmethod
+ def quote_systemtap_string(value):
+ return value.replace("\\", "\\\\").replace('"', '\\"')
+
+ def python_probe(self):
+ executable = self.quote_systemtap_string(sys.executable)
+ probe_binary = get_probe_binary()
+ if probe_binary == sys.executable:
+ return f'process("{executable}").mark'
+
+ # Python built with --enable-shared
+ probe_binary = self.quote_systemtap_string(probe_binary)
+ return f'process("{executable}").library("{probe_binary}").mark'
+
+ def render_script(self, filename):
+ with open(filename) as fp:
+ script = fp.read()
+
+ return script.replace(self.PROBE_PLACEHOLDER, self.python_probe())
+
+ def trace(self, script_file, subcommand=None, *, timeout=None,
+ check_returncode=False):
+ with tempfile.NamedTemporaryFile(
+ mode="w", encoding="utf-8", suffix=self.EXTENSION, delete=False
+ ) as script:
+ script.write(self.render_script(script_file))
+ generated_script_file = script.name
+
+ try:
+ return super().trace(
+ generated_script_file,
+ subcommand,
+ timeout=timeout,
+ check_returncode=check_returncode,
+ )
+ finally:
+ os_helper.unlink(generated_script_file)
class BPFTraceBackend(TraceBackend):
@@ -273,7 +339,7 @@ def run_case(self, name, optimize_python=None):
python_flags.extend(["-O"] * optimize_python)
subcommand = [sys.executable] + python_flags + [python_file]
- program = self.PROGRAMS[name].format(python=sys.executable)
+ program = self.PROGRAMS[name].format(python=get_probe_binary())
try:
proc = create_process_group(
@@ -312,7 +378,7 @@ def run_case(self, name, optimize_python=None):
def assert_usable(self):
# Check if bpftrace is available and can attach to USDT probes
- program = f'usdt:{sys.executable}:python:function__entry {{
printf("probe: success\\n"); exit(); }}'
+ program = f'usdt:{get_probe_binary()}:python:function__entry {{
printf("probe: success\\n"); exit(); }}'
try:
proc = create_process_group(
["bpftrace", "-e", program, "-c",
@@ -455,28 +521,7 @@ def get_readelf_version():
return int(match.group(1)), int(match.group(2))
def get_readelf_output(self):
- binary = sys.executable
- if sysconfig.get_config_var("Py_ENABLE_SHARED"):
- lib_dir = sysconfig.get_config_var("LIBDIR")
- if not lib_dir or sysconfig.is_python_build():
- lib_dir = os.path.abspath(os.path.dirname(sys.executable))
-
- lib_names = []
- for name in (
- sysconfig.get_config_var("INSTSONAME"),
- sysconfig.get_config_var("LDLIBRARY"),
- ):
- if name and name not in lib_names:
- lib_names.append(name)
-
- if lib_dir:
- for name in lib_names:
- libpython_path = os.path.join(lib_dir, name)
- if os.path.exists(libpython_path):
- binary = libpython_path
- break
-
- return run_readelf(["readelf", "-n", binary])
+ return run_readelf(["readelf", "-n", get_probe_binary()])
def test_check_probes(self):
readelf_output = self.get_readelf_output()
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]