Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork34k
Description
Bug report
Bug description:
I found an issue related to free-threaded build detection inLib/profiling/sampling/sample.py.
_FREE_THREADED_BUILD=sysconfig.get_config_var("Py_GIL_DISABLED")isnotNone
I am running a non-free-threaded Windows build, where Py_GIL_DISABLED = 0.
Minimal repro script
Here is a minimal script that demonstrates the issue:
importsysconfigimportprofiling.sampling.sampleasspprint("sp._FREE_THREADED_BUILD =",sp._FREE_THREADED_BUILD)print("sysconfig Py_GIL_DISABLED =",sysconfig.get_config_var("Py_GIL_DISABLED"))print("expression (is not None) =",sysconfig.get_config_var("Py_GIL_DISABLED")isnotNone)print("expression (bool) =",bool(sysconfig.get_config_var("Py_GIL_DISABLED")))print("expression (==1) =",sysconfig.get_config_var("Py_GIL_DISABLED")==1)print("expression (==0) =",sysconfig.get_config_var("Py_GIL_DISABLED")==0)print("sample module file =",sp.__file__)
Result and discussion
On my system, this produces:
sp._FREE_THREADED_BUILD = True<------!!! heresysconfig Py_GIL_DISABLED = 0expression (is not None) = Trueexpression (bool) = Falseexpression (==1) = Falseexpression (==0) = Truesample module file = d:\MyCode\cpython\Lib\profiling\sampling\sample.py
This appears to mis-detect the build type when Py_GIL_DISABLED is defined but set to 0. In that case, the variable is not None, so _FREE_THREADED_BUILD becomes True, even though the interpreter is not free-threaded.
Proposed Fix
Taking the following constraints into account:
- only_active_thread=False is the default behavior.
- only_active_thread=True is only meaningful when running in GIL profiling mode.
- only_active_thread and all_threads must not be True at the same time.
- Profiling mode must not be used together with all_threads=True.
- the original code took the branch where
_FREE_THREADED_BUILD = False, and since theRemoteUnwinderfunction did not accept anall_threadsparameter,all_threadsdefaulted to False.
I aimed to preserve the original semantics as much as possible while ensuring all test cases pass, so I believe this fix maybe appropriate. However, I find my code changes quite ugly.
diff --git a/Lib/profiling/sampling/sample.py b/Lib/profiling/sampling/sample.pyindex e73306ebf2..7b43916b0c 100644--- a/Lib/profiling/sampling/sample.py+++ b/Lib/profiling/sampling/sample.py@@ -41,7 +41,7 @@ def _pause_threads(unwinder, blocking): except ImportError: LiveStatsCollector = None-_FREE_THREADED_BUILD = sysconfig.get_config_var("Py_GIL_DISABLED") is not None+_FREE_THREADED_BUILD = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) # Minimum number of samples required before showing the TUI # If fewer samples are collected, we skip the TUI and just print a message MIN_SAMPLES_FOR_TUI = 200@@ -71,11 +71,18 @@ def _new_unwinder(self, native, gc, opcodes, skip_non_matching_threads): cache_frames=True, stats=self.collect_stats ) else:- unwinder = _remote_debugging.RemoteUnwinder(- self.pid, only_active_thread=bool(self.all_threads), mode=self.mode, native=native, gc=gc,- opcodes=opcodes, skip_non_matching_threads=skip_non_matching_threads,- cache_frames=True, stats=self.collect_stats- )+ if self.all_threads:+ unwinder = _remote_debugging.RemoteUnwinder(+ self.pid, all_threads=self.all_threads, mode=self.mode, native=native, gc=gc,+ opcodes=opcodes, skip_non_matching_threads=skip_non_matching_threads,+ cache_frames=True, stats=self.collect_stats+ )+ else:+ unwinder = _remote_debugging.RemoteUnwinder(+ self.pid, only_active_thread=bool(self.all_threads), mode=self.mode, native=native, gc=gc,+ opcodes=opcodes, skip_non_matching_threads=skip_non_matching_threads,+ cache_frames=True, stats=self.collect_stats+ ) return unwinder def sample(self, collector, duration_sec=None, *, async_aware=False):
CPython versions tested on:
CPython main branch
Operating systems tested on:
Windows