From dd0ec01bf42f14f41ffaf061310603904148f269 Mon Sep 17 00:00:00 2001 From: DCchoudhury15 Date: Mon, 5 Jan 2026 04:36:39 +0000 Subject: [PATCH 1/3] gh-143423: Fix free-threaded build detection in sampling profiler --- Lib/profiling/sampling/sample.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Lib/profiling/sampling/sample.py b/Lib/profiling/sampling/sample.py index e73306ebf290e7..f9e94236700184 100644 --- a/Lib/profiling/sampling/sample.py +++ b/Lib/profiling/sampling/sample.py @@ -41,7 +41,9 @@ def _pause_threads(unwinder, blocking): except ImportError: LiveStatsCollector = None -_FREE_THREADED_BUILD = sysconfig.get_config_var("Py_GIL_DISABLED") is not None +# FIX: Use bool() to correctly detect 0 as False on Windows non-free-threaded builds +_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 +73,19 @@ 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 - ) + # FIX: Properly handle all_threads vs only_active_thread parameters + 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): From e87439e3877dd79704d1194a9b98dbeb6b17cb23 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 05:31:07 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2026-01-05-05-31-05.gh-issue-143423.X7YdnR.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-01-05-05-31-05.gh-issue-143423.X7YdnR.rst diff --git a/Misc/NEWS.d/next/Library/2026-01-05-05-31-05.gh-issue-143423.X7YdnR.rst b/Misc/NEWS.d/next/Library/2026-01-05-05-31-05.gh-issue-143423.X7YdnR.rst new file mode 100644 index 00000000000000..d9276dfd400a5d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-05-05-31-05.gh-issue-143423.X7YdnR.rst @@ -0,0 +1 @@ +Fix free-threaded build detection in the sampling profiler when Py_GIL_DISABLED is set to 0. From e684ada5519a2024636c828cfdbd061f701c040c Mon Sep 17 00:00:00 2001 From: DCchoudhury15 Date: Thu, 8 Jan 2026 16:37:39 +0000 Subject: [PATCH 3/3] Refactor RemoteUnwinder to use kwargs for thread parameters per review --- Lib/profiling/sampling/sample.py | 39 ++++++++++++++------------------ 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/Lib/profiling/sampling/sample.py b/Lib/profiling/sampling/sample.py index f9e94236700184..f657849e72bb62 100644 --- a/Lib/profiling/sampling/sample.py +++ b/Lib/profiling/sampling/sample.py @@ -41,8 +41,7 @@ def _pause_threads(unwinder, blocking): except ImportError: LiveStatsCollector = None -# FIX: Use bool() to correctly detect 0 as False on Windows non-free-threaded builds -_FREE_THREADED_BUILD = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) +_FREE_THREADED_BUILD = sysconfig.get_config_var("Py_GIL_DISABLED") is not None # Minimum number of samples required before showing the TUI # If fewer samples are collected, we skip the TUI and just print a message @@ -66,27 +65,23 @@ def __init__(self, pid, sample_interval_usec, all_threads, *, mode=PROFILING_MOD self.realtime_stats = False def _new_unwinder(self, native, gc, opcodes, skip_non_matching_threads): - if _FREE_THREADED_BUILD: - 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 - ) + kwargs = {} + if _FREE_THREADED_BUILD or self.all_threads: + kwargs['all_threads'] = self.all_threads else: - # FIX: Properly handle all_threads vs only_active_thread parameters - 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 + kwargs['only_active_thread'] = bool(self.all_threads) + + return _remote_debugging.RemoteUnwinder( + self.pid, + mode=self.mode, + native=native, + gc=gc, + opcodes=opcodes, + skip_non_matching_threads=skip_non_matching_threads, + cache_frames=True, + stats=self.collect_stats, + **kwargs + ) def sample(self, collector, duration_sec=None, *, async_aware=False): sample_interval_sec = self.sample_interval_usec / 1_000_000