Skip to content

Commit c49b3dc

Browse files
authored
[3.15] Revert backports merged during the 3.15.0 release freeze on 5 October (#158873)
1 parent a904b39 commit c49b3dc

54 files changed

Lines changed: 331 additions & 1808 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/build.yml‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -605,9 +605,6 @@ jobs:
605605
- check-name: Undefined behavior
606606
sanitizer: UBSan
607607
free-threading: false
608-
- check-name: Memory
609-
sanitizer: MSan
610-
free-threading: false
611608
uses: ./.github/workflows/reusable-san.yml
612609
with:
613610
sanitizer: ${{ matrix.sanitizer }}

‎.github/workflows/reusable-san.yml‎

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
|| ''
6161
}}
6262
- name: UBSan option setup
63-
if: inputs.sanitizer == 'UBSan'
63+
if: inputs.sanitizer != 'TSan'
6464
run: >-
6565
echo
6666
"UBSAN_OPTIONS=${SAN_LOG_OPTION}
@@ -69,20 +69,6 @@ jobs:
6969
>> "$GITHUB_ENV"
7070
env:
7171
SAN_LOG_OPTION: log_path=${{ github.workspace }}/san_log
72-
- name: MSan option setup
73-
if: inputs.sanitizer == 'MSan'
74-
run: |
75-
echo "MSAN_OPTIONS=${SAN_LOG_OPTION} allocator_may_return_null=1 handle_segv=0" >> "$GITHUB_ENV"
76-
# MSan reports false positives for memory initialized by libraries
77-
# that are not built with MSan, so disable modules that use them.
78-
# _remote_debugging links to libzstd directly, but we unpoision the memory.
79-
{
80-
echo '*disabled*'
81-
echo '_bz2 _ctypes _curses _curses_panel _dbm _decimal _gdbm _hashlib'
82-
echo '_lzma _sqlite3 _ssl _tkinter _uuid _zstd readline zlib'
83-
} > Modules/Setup.local
84-
env:
85-
SAN_LOG_OPTION: log_path=${{ github.workspace }}/san_log
8672
- name: Add ccache to PATH
8773
run: |
8874
echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
@@ -107,8 +93,6 @@ jobs:
10793
# gh-157958: -O2 instead of the pydebug default -Og to avoid a clang 21
10894
# compile-time blowup on some interpreter files.
10995
# (https://github.com/llvm/llvm-project/issues/179695)
110-
# MSan uses --with-assertions instead of --with-pydebug because its
111-
# hooks on the Python memory allocators hide uninitialized reads.
11296
- name: Configure CPython
11397
run: >-
11498
./configure
@@ -117,11 +101,9 @@ jobs:
117101
${{
118102
inputs.sanitizer == 'TSan'
119103
&& '--with-thread-sanitizer'
120-
|| inputs.sanitizer == 'MSan'
121-
&& '--with-memory-sanitizer'
122104
|| '--with-undefined-behavior-sanitizer --with-strict-overflow'
123105
}}
124-
${{ inputs.sanitizer == 'MSan' && '--with-assertions' || '--with-pydebug' }}
106+
--with-pydebug
125107
${{ inputs.sanitizer == 'TSan' && '--with-openssl="$OPENSSL_DIR" --with-openssl-rpath=auto' || '' }}
126108
${{ inputs.free-threading && '--disable-gil' || '' }}
127109
- name: Build CPython

‎Doc/using/configure.rst‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,10 +1015,6 @@ Debug options
10151015

10161016
Enable MemorySanitizer allocation error detector, ``msan`` (default is no).
10171017

1018-
MSan reports false positives for memory initialized by libraries that are
1019-
not built with MSan, so either build all dependencies with MSan or disable
1020-
the extension modules that use them in :file:`Modules/Setup.local`.
1021-
10221018
.. versionadded:: 3.6
10231019

10241020
.. option:: --with-undefined-behavior-sanitizer

‎Include/internal/pycore_interp_structs.h‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,6 @@ struct gc_old_stats_buffer {
219219
struct gc_stats {
220220
struct gc_young_stats_buffer young;
221221
struct gc_old_stats_buffer old[2];
222-
uint32_t update_seq;
223222
};
224223

225224
struct _gc_runtime_state {

‎Include/pyport.h‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,6 @@ extern "C" {
554554
# define _Py_MEMORY_SANITIZER
555555
# define _Py_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
556556
# define _Py_MSAN_UNPOISON(PTR, SIZE) (__msan_unpoison(PTR, SIZE))
557-
# define _Py_MSAN_UNPOISON_STRING(STR) (__msan_unpoison_string(STR))
558557
# endif
559558
# endif
560559
# if __has_feature(address_sanitizer)
@@ -596,9 +595,6 @@ extern "C" {
596595
#ifndef _Py_MSAN_UNPOISON
597596
# define _Py_MSAN_UNPOISON(PTR, SIZE)
598597
#endif
599-
#ifndef _Py_MSAN_UNPOISON_STRING
600-
# define _Py_MSAN_UNPOISON_STRING(STR)
601-
#endif
602598

603599
/* AIX has __bool__ redefined in it's system header file. */
604600
#if defined(_AIX) && defined(__bool__)

‎Lib/asyncio/tools.py‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ def __init__(
2727
# ─── indexing helpers ───────────────────────────────────────────
2828
def _format_stack_entry(elem: str|FrameInfo) -> str:
2929
if not isinstance(elem, str):
30-
if elem.location is None:
31-
if elem.filename in ("", "~"):
32-
return f"{elem.funcname}"
33-
return f"{elem.funcname} {elem.filename}"
3430
if elem.location.lineno == 0 and elem.filename == "":
3531
return f"{elem.funcname}"
3632
else:
@@ -194,7 +190,8 @@ def build_task_table(result):
194190
# Build coroutine stack string
195191
frames = [frame for coro in task_info.coroutine_stack
196192
for frame in coro.call_stack]
197-
coro_stack = " -> ".join(x.funcname for x in frames)
193+
coro_stack = " -> ".join(_format_stack_entry(x).split(" ")[0]
194+
for x in frames)
198195

199196
# Handle tasks with no awaiters
200197
if not task_info.awaited_by:
@@ -205,7 +202,8 @@ def build_task_table(result):
205202
# Handle tasks with awaiters
206203
for coro_info in task_info.awaited_by:
207204
parent_id = coro_info.task_name
208-
awaiter_frames = [x.funcname for x in coro_info.call_stack]
205+
awaiter_frames = [_format_stack_entry(x).split(" ")[0]
206+
for x in coro_info.call_stack]
209207
awaiter_chain = " -> ".join(awaiter_frames)
210208
awaiter_name = id2name.get(parent_id, "Unknown")
211209
parent_id_str = (hex(parent_id) if isinstance(parent_id, int)

‎Lib/profiling/sampling/_sync_coordinator.py‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,6 @@ def _execute_script(script_path: str, script_args: List[str], cwd: str) -> None:
168168
if not os.path.isfile(script_path):
169169
raise TargetError(f"Script not found: {script_path}")
170170

171-
script_dir = os.path.dirname(os.path.realpath(script_path))
172-
if script_dir in sys.path:
173-
sys.path.remove(script_dir)
174-
sys.path.insert(0, script_dir)
175-
176171
# Replace sys.argv to match original script call
177172
sys.argv = [script_path] + script_args
178173

‎Lib/profiling/sampling/binary_collector.py‎

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Thin Python wrapper around C binary writer for profiling data."""
22

3-
import sys
43
import time
54

65
import _remote_debugging
@@ -82,7 +81,6 @@ def __init__(self, filename, sample_interval_usec, *, skip_idle=False,
8281
self.filename = filename
8382
self.sample_interval_usec = sample_interval_usec
8483
self.skip_idle = skip_idle
85-
self.running = True
8684

8785
compression_type = _resolve_compression(compression)
8886
start_time_us = int(time.monotonic() * 1_000_000)
@@ -104,19 +102,9 @@ def collect(self, stack_frames, timestamp_us=None):
104102
timestamp_us: Optional timestamp in microseconds. If not provided,
105103
uses time.monotonic() to generate one.
106104
"""
107-
if not self.running:
108-
return
109105
if timestamp_us is None:
110106
timestamp_us = int(time.monotonic() * 1_000_000)
111-
try:
112-
self._writer.write_sample(stack_frames, timestamp_us)
113-
except OverflowError as e:
114-
if not self._writer.limit_reached:
115-
raise
116-
self.running = False
117-
print(f"Warning: {e}; stopping early and keeping the data "
118-
"collected so far.",
119-
file=sys.stderr)
107+
self._writer.write_sample(stack_frames, timestamp_us)
120108

121109
def collect_failed_sample(self):
122110
"""Record a failed sample attempt (no-op for binary format)."""
@@ -155,5 +143,9 @@ def __enter__(self):
155143
return self
156144

157145
def __exit__(self, exc_type, exc_val, exc_tb):
158-
"""Finalize if the writer can still produce a valid file."""
159-
return self._writer.__exit__(exc_type, exc_val, exc_tb)
146+
"""Context manager exit - finalize unless there was an error."""
147+
if exc_type is None:
148+
self._writer.finalize()
149+
else:
150+
self._writer.close()
151+
return False

‎Lib/profiling/sampling/heatmap_collector.py‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -785,14 +785,14 @@ def _generate_file_html(self, output_path: Path, filename: str,
785785
line_counts: Dict[int, int], self_counts: Dict[int, int],
786786
file_stat: FileStats):
787787
"""Generate HTML for a single source file with heatmap coloring."""
788-
source_lines = [f"# Source file not available: {filename}"]
788+
# Read source file
789789
try:
790-
path = Path(filename)
791-
if path.is_file():
792-
source_lines = path.read_text(
793-
encoding='utf-8', errors='replace').splitlines()
794-
except (IOError, OSError):
795-
pass
790+
source_lines = Path(filename).read_text(encoding='utf-8', errors='replace').splitlines()
791+
except (IOError, OSError) as e:
792+
if not (filename.startswith('<') or filename.startswith('[') or
793+
filename in ('~', '...', '.') or len(filename) < 2):
794+
print(f"Warning: Could not read source file {filename}: {e}")
795+
source_lines = [f"# Source file not available: {filename}"]
796796

797797
# Generate HTML for each line
798798
max_samples = max(line_counts.values()) if line_counts else 1

‎Lib/profiling/sampling/stack_collector.py‎

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,13 @@ def export(self, filename):
6060

6161
lines.sort(key=lambda x: (-x[1], x[0]))
6262

63-
with open(filename, "w",
64-
encoding="utf-8", errors="surrogatepass") as f:
63+
with open(filename, "w") as f:
6564
for stack, count in lines:
6665
f.write(f"{stack} {count}\n")
6766
print(f"Collapsed stack output written to {filename}")
6867
return True
6968

7069

71-
# Bounded by the unwinder's maximum captured stack depth (MAX_FRAMES).
72-
_FLAMEGRAPH_RECURSION_MARGIN = 2000
73-
74-
7570
class FlamegraphCollector(StackTraceCollector):
7671
def __init__(self, *args, **kwargs):
7772
super().__init__(*args, **kwargs)
@@ -170,41 +165,34 @@ def set_mode(self, mode):
170165
self.stats["mode"] = mode
171166

172167
def export(self, filename):
173-
# Converting the call tree recurses to the sampled stack depth.
174-
old_limit = sys.getrecursionlimit()
175-
sys.setrecursionlimit(old_limit + _FLAMEGRAPH_RECURSION_MARGIN)
176-
try:
177-
flamegraph_data = self._convert_to_flamegraph_format()
178-
179-
# Debug output with string table statistics
180-
num_functions = len(flamegraph_data.get("children", []))
181-
total_time = flamegraph_data.get("value", 0)
182-
string_count = len(self._string_table)
183-
s1 = "" if num_functions == 1 else "s"
184-
s2 = "" if total_time == 1 else "s"
185-
s3 = "" if string_count == 1 else "s"
168+
flamegraph_data = self._convert_to_flamegraph_format()
169+
170+
# Debug output with string table statistics
171+
num_functions = len(flamegraph_data.get("children", []))
172+
total_time = flamegraph_data.get("value", 0)
173+
string_count = len(self._string_table)
174+
s1 = "" if num_functions == 1 else "s"
175+
s2 = "" if total_time == 1 else "s"
176+
s3 = "" if string_count == 1 else "s"
177+
print(
178+
f"Flamegraph data: {num_functions} root function{s1}, "
179+
f"{total_time} total sample{s2}, "
180+
f"{string_count} unique string{s3}"
181+
)
182+
183+
if num_functions == 0:
186184
print(
187-
f"Flamegraph data: {num_functions} root function{s1}, "
188-
f"{total_time} total sample{s2}, "
189-
f"{string_count} unique string{s3}"
185+
"Warning: No functions found in profiling data. Check if sampling captured any data."
190186
)
187+
return False
191188

192-
if num_functions == 0:
193-
print(
194-
"Warning: No functions found in profiling data. "
195-
"Check if sampling captured any data."
196-
)
197-
return False
198-
199-
html_content = self._create_flamegraph_html(flamegraph_data)
189+
html_content = self._create_flamegraph_html(flamegraph_data)
200190

201-
with open(filename, "w", encoding="utf-8") as f:
202-
f.write(html_content)
191+
with open(filename, "w", encoding="utf-8") as f:
192+
f.write(html_content)
203193

204-
print(f"Flamegraph saved to: {filename}")
205-
return True
206-
finally:
207-
sys.setrecursionlimit(old_limit)
194+
print(f"Flamegraph saved to: {filename}")
195+
return True
208196

209197
@staticmethod
210198
@functools.lru_cache(maxsize=None)

0 commit comments

Comments
 (0)