From d44c1190a3317d4ac26d2865cf84f530da9f383f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurycy=20Paw=C5=82owski-Wiero=C5=84ski?= Date: Mon, 5 Oct 2026 02:37:21 +0200 Subject: [PATCH 1/2] gh-154194: Degrade frames in Tachyon instead of failing the sample (#154195) * degrade gracefully * news * better NEWS wording * do not raise on MAX_REMOTE_STR_READ * bye MAX_REMOTE_STR_READ * fix -m asyncio ps|pstree * test truncation and linetable sentinel * simpler * simpler * redundant now * respect #157790 in the news --------- Co-authored-by: Pablo Galindo Salgado (cherry picked from commit 7d25916b41de4fc28e04b4944864ad1094a9e8e0) --- .../pycore_global_objects_fini_generated.h | 3 + Include/internal/pycore_global_strings.h | 3 + .../internal/pycore_runtime_init_generated.h | 3 + .../internal/pycore_unicodeobject_generated.h | 12 +++ Lib/asyncio/tools.py | 10 ++- Lib/test/test_asyncio/test_tools.py | 76 ++++++++++++++++ Lib/test/test_external_inspection.py | 87 +++++++++++++++++++ .../test_binary_format.py | 20 +++++ ...-07-19-22-08-05.gh-issue-154194.1bqRdx.rst | 3 + Modules/_remote_debugging/_remote_debugging.h | 2 +- Modules/_remote_debugging/code_objects.c | 53 +++++++++-- Python/remote_debug.h | 11 ++- 12 files changed, 269 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-19-22-08-05.gh-issue-154194.1bqRdx.rst diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index c536079498e9cbd..9af9ab2dc1f2dca 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -1334,6 +1334,9 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(native)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(str_replace_inf)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(type_params)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(unknown_file)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(unknown_function)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(unreadable_frame)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(utf_8)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(AGEN_CLOSED)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(AGEN_CREATED)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index daa1f2ad2f109d7..9bed9b9acd118a8 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -54,6 +54,9 @@ struct _Py_global_strings { STRUCT_FOR_STR(native, "") STRUCT_FOR_STR(str_replace_inf, "1e309") STRUCT_FOR_STR(type_params, ".type_params") + STRUCT_FOR_STR(unknown_file, "") + STRUCT_FOR_STR(unknown_function, "") + STRUCT_FOR_STR(unreadable_frame, "") STRUCT_FOR_STR(utf_8, "utf-8") } literals; diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 5c6635e6c3c232c..a8ef7449144d86b 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1329,6 +1329,9 @@ extern "C" { INIT_STR(native, ""), \ INIT_STR(str_replace_inf, "1e309"), \ INIT_STR(type_params, ".type_params"), \ + INIT_STR(unknown_file, ""), \ + INIT_STR(unknown_function, ""), \ + INIT_STR(unreadable_frame, ""), \ INIT_STR(utf_8, "utf-8"), \ } diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index ddda6b467ce1c1a..415c8ebb69b9cd0 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -3464,10 +3464,22 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_STR(unknown_file); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_STR(unknown_function); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_STR(anon_unknown); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_STR(unreadable_frame); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_STR(json_decoder); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 2ac1738d15c6c72..fd494912549bf8c 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -27,6 +27,10 @@ def __init__( # ─── indexing helpers ─────────────────────────────────────────── def _format_stack_entry(elem: str|FrameInfo) -> str: if not isinstance(elem, str): + if elem.location is None: + if elem.filename in ("", "~"): + return f"{elem.funcname}" + return f"{elem.funcname} {elem.filename}" if elem.location.lineno == 0 and elem.filename == "": return f"{elem.funcname}" else: @@ -190,8 +194,7 @@ def build_task_table(result): # Build coroutine stack string frames = [frame for coro in task_info.coroutine_stack for frame in coro.call_stack] - coro_stack = " -> ".join(_format_stack_entry(x).split(" ")[0] - for x in frames) + coro_stack = " -> ".join(x.funcname for x in frames) # Handle tasks with no awaiters if not task_info.awaited_by: @@ -202,8 +205,7 @@ def build_task_table(result): # Handle tasks with awaiters for coro_info in task_info.awaited_by: parent_id = coro_info.task_name - awaiter_frames = [_format_stack_entry(x).split(" ")[0] - for x in coro_info.call_stack] + awaiter_frames = [x.funcname for x in coro_info.call_stack] awaiter_chain = " -> ".join(awaiter_frames) awaiter_name = id2name.get(parent_id, "Unknown") parent_id_str = (hex(parent_id) if isinstance(parent_id, int) diff --git a/Lib/test/test_asyncio/test_tools.py b/Lib/test/test_asyncio/test_tools.py index df934164eb9fd60..2b8e4940333196b 100644 --- a/Lib/test/test_asyncio/test_tools.py +++ b/Lib/test/test_asyncio/test_tools.py @@ -1558,6 +1558,82 @@ def test_table_output_format(self): class TestAsyncioToolsEdgeCases(unittest.TestCase): + def test_frames_without_location_tree(self): + """Frames the unwinder could not fully read - should not crash.""" + input_ = [ + AwaitedInfo( + thread_id=1, + awaited_by=[ + TaskInfo( + task_id=1, + task_name="Task-A", + coroutine_stack=[ + CoroInfo( + call_stack=[ + FrameInfo("", "~", None), + FrameInfo("", "app.py", None), + FrameInfo("big", "big.py", None), + ], + task_name=1 + ) + ], + awaited_by=[] + ) + ] + ) + ] + self.assertEqual( + tools.build_async_tree(input_), + [[ + "└── (T) Task-A", + " └── big big.py", + " └── app.py", + " └── ", + ]], + ) + + def test_frames_without_location_table(self): + """Frame names are not truncated at the first space.""" + input_ = [ + AwaitedInfo( + thread_id=1, + awaited_by=[ + TaskInfo( + task_id=1, + task_name="Task-A", + coroutine_stack=[ + CoroInfo( + call_stack=[ + FrameInfo("", "~", None) + ], + task_name=1 + ) + ], + awaited_by=[ + CoroInfo( + call_stack=[ + FrameInfo("", "app.py", None) + ], + task_name=2 + ) + ] + ) + ] + ) + ] + self.assertEqual( + tools.build_task_table(input_), + [[ + 1, + "0x1", + "Task-A", + "", + "", + "Unknown", + "0x2", + ]], + ) + def test_task_awaits_self(self): """A task directly awaits itself - should raise a cycle.""" input_ = [ diff --git a/Lib/test/test_external_inspection.py b/Lib/test/test_external_inspection.py index d48a2b9eb6e02ab..a0bedf4910fa2ab 100644 --- a/Lib/test/test_external_inspection.py +++ b/Lib/test/test_external_inspection.py @@ -4070,6 +4070,93 @@ def test_get_stats_disabled_raises(self): client_socket.sendall(b"done") +@requires_remote_subprocess_debugging() +@skip_if_not_supported +@unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", +) +class TestMetadataDegradation(RemoteInspectionTestBase): + """Tests for graceful degradation of oversized code-object metadata.""" + + def test_long_qualname_truncated_not_dropped(self): + """A qualname longer than 1024 chars is truncated instead of + failing the whole sample.""" + name = "f" * 1100 + src = f"def {name}(sample):\n return sample()\n" + ns = {} + exec(src, ns) + + trace = ns[name](RemoteUnwinder(os.getpid()).get_stack_trace) + frame = self._find_frame_in_trace( + trace, lambda f: f.funcname.startswith("fff") + ) + self.assertIsNotNone(frame) + self.assertEqual(frame.funcname, "f" * 1024) + + def test_long_filename_truncated(self): + """A filename longer than 1024 chars is truncated instead of + failing the whole sample.""" + src = "def g(sample):\n return sample()\n" + ns = {} + exec(compile(src, "x" * 1500 + ".py", "exec"), ns) + + trace = ns["g"](RemoteUnwinder(os.getpid()).get_stack_trace) + frame = self._find_frame_in_trace(trace, lambda f: f.funcname == "g") + self.assertIsNotNone(frame) + self.assertEqual(frame.filename, "x" * 1024) + + def test_oversized_linetable_degrades_to_no_location(self): + """A linetable over MAX_LINETABLE_SIZE degrades to a frame without + location instead of failing the whole sample.""" + src = ( + "def big(sample):\n" + + " x = 1\n" * 20_000 + + " return sample()\n" + ) + ns = {} + exec(compile(src, "big_linetable.py", "exec"), ns) + big = ns["big"] + self.assertGreater(len(big.__code__.co_linetable), 64 * 1024) + + trace = big(RemoteUnwinder(os.getpid()).get_stack_trace) + frame = self._find_frame_in_trace( + trace, lambda f: f.funcname == "big" + ) + self.assertIsNone(frame.location) + self.assertEqual(frame.filename, "big_linetable.py") + + @unittest.skipIf( + sys.platform == "win32", + "Process death maps to ProcessLookupError only on POSIX platforms", + ) + def test_dead_process_raises_not_degrades(self): + """Death of the target raises ProcessLookupError instead of + degrading to synthetic frames.""" + script_body = """\ + import time + sock.sendall(b"ready") + time.sleep(10_000) + """ + with self._target_process(script_body) as (p, client_socket, make_unwinder): + _wait_for_signal(client_socket, b"ready") + unwinder = make_unwinder() + _get_stack_trace_with_retry(unwinder) + + p.kill() + p.wait() + + for _ in busy_retry(SHORT_TIMEOUT, error=False): + try: + unwinder.get_stack_trace() + except ProcessLookupError: + break + except RuntimeError: + continue + else: + self.fail("ProcessLookupError never raised for dead process") + + @requires_remote_subprocess_debugging() class TestFrameChainLimits(RemoteInspectionTestBase): """Frame chain walks abort instead of looping/overflowing on deep chains.""" diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py b/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py index ff944b3163ec3e8..d33cc8b619adb1c 100644 --- a/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py +++ b/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py @@ -665,6 +665,26 @@ def test_same_line_different_columns(self): collector, count = self.roundtrip(samples) self.assertEqual(count, 3) + def test_synthetic_frames_roundtrip(self): + """Degraded/sentinel frames (location=None) survive the binary format.""" + frames = [ + FrameInfo(("~", None, name, None)) + for name in ( + "", + "", + "", + "", + "", + ) + ] + frames.append(FrameInfo(("app.py", None, "", None))) + frames.append(FrameInfo(("", None, "real_func", None))) + samples = [[make_interpreter(0, [make_thread(1, frames)])]] + + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + class TestBinaryEdgeCases(BinaryFormatTestBase): """Tests for edge cases in binary format.""" diff --git a/Misc/NEWS.d/next/Library/2026-07-19-22-08-05.gh-issue-154194.1bqRdx.rst b/Misc/NEWS.d/next/Library/2026-07-19-22-08-05.gh-issue-154194.1bqRdx.rst new file mode 100644 index 000000000000000..9971cd3f8dc25bd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-19-22-08-05.gh-issue-154194.1bqRdx.rst @@ -0,0 +1,3 @@ +Fix the sampling profiler dropping entire samples when a non-fatal read fails; +frames now keep any readable metadata, and long funcnames and filenames are +truncated instead. Patch by Maurycy Pawłowski-Wieroński. diff --git a/Modules/_remote_debugging/_remote_debugging.h b/Modules/_remote_debugging/_remote_debugging.h index 3aecf3982b95d75..cf923014445f558 100644 --- a/Modules/_remote_debugging/_remote_debugging.h +++ b/Modules/_remote_debugging/_remote_debugging.h @@ -182,7 +182,7 @@ typedef enum _WIN32_THREADSTATE { #define set_exception_cause(unwinder, exc_type, message) \ do { \ assert(PyErr_Occurred() && "function returned -1 without setting exception"); \ - if (unwinder->debug && !_Py_RemoteDebug_HasPermissionError()) { \ + if (unwinder->debug && !_Py_RemoteDebug_IsFatalReadError()) { \ _set_debug_exception_cause(exc_type, message); \ } \ } while (0) diff --git a/Modules/_remote_debugging/code_objects.c b/Modules/_remote_debugging/code_objects.c index 6ad058850196269..081aaa49fb49ffb 100644 --- a/Modules/_remote_debugging/code_objects.c +++ b/Modules/_remote_debugging/code_objects.c @@ -341,11 +341,16 @@ parse_code_object(RemoteUnwinderObject *unwinder, PyObject **result, const CodeObjectContext *ctx) { + _Py_DECLARE_STR(unknown_function, ""); + _Py_DECLARE_STR(unknown_file, ""); + _Py_DECLARE_STR(unreadable_frame, ""); + void *key = (void *)ctx->code_addr; CachedCodeMetadata *meta = NULL; PyObject *func = NULL; PyObject *file = NULL; PyObject *linetable = NULL; + int code_metadata_incomplete = 0; #ifdef Py_GIL_DISABLED // In free threading builds, code object addresses might have the low bit set @@ -369,30 +374,50 @@ parse_code_object(RemoteUnwinderObject *unwinder, if (_Py_RemoteDebug_PagedReadRemoteMemory( &unwinder->handle, real_address, SIZEOF_CODE_OBJ, code_object) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read code object"); - goto error; + if (_Py_RemoteDebug_IsFatalReadError()) { + goto error; + } + PyErr_Clear(); + func = Py_NewRef(&_Py_STR(unreadable_frame)); + file = Py_NewRef(_Py_LATIN1_CHR('~')); + goto degraded; } func = read_py_str(unwinder, GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.qualname), 1024); if (!func) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read function name from code object"); - goto error; + if (_Py_RemoteDebug_IsFatalReadError()) { + goto error; + } + PyErr_Clear(); + func = Py_NewRef(&_Py_STR(unknown_function)); + code_metadata_incomplete = 1; } file = read_py_str(unwinder, GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.filename), 1024); if (!file) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read filename from code object"); - goto error; + if (_Py_RemoteDebug_IsFatalReadError()) { + goto error; + } + PyErr_Clear(); + file = Py_NewRef(&_Py_STR(unknown_file)); + code_metadata_incomplete = 1; + } + + if (code_metadata_incomplete) { + goto degraded; } linetable = read_py_bytes(unwinder, GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable), MAX_LINETABLE_SIZE); if (!linetable) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read linetable from code object"); - goto error; + if (_Py_RemoteDebug_IsFatalReadError()) { + goto error; + } + PyErr_Clear(); + goto degraded; } meta = PyMem_RawMalloc(sizeof(CachedCodeMetadata)); @@ -561,6 +586,18 @@ parse_code_object(RemoteUnwinderObject *unwinder, *result = tuple; return 0; +degraded: { + PyObject *degraded_tuple = make_frame_info(unwinder, file, Py_None, + func, Py_None); + Py_CLEAR(func); + Py_CLEAR(file); + if (!degraded_tuple) { + return -1; + } + *result = degraded_tuple; + return 0; +} + error: Py_XDECREF(func); Py_XDECREF(file); diff --git a/Python/remote_debug.h b/Python/remote_debug.h index 16229df936ffef6..30cad806a6f210b 100644 --- a/Python/remote_debug.h +++ b/Python/remote_debug.h @@ -107,9 +107,18 @@ _Py_RemoteDebug_HasPermissionError(void) && PyErr_ExceptionMatches(PyExc_PermissionError); } +static inline int +_Py_RemoteDebug_IsFatalReadError(void) +{ + return _Py_RemoteDebug_HasPermissionError() + || PyErr_ExceptionMatches(PyExc_MemoryError) + || PyErr_ExceptionMatches(PyExc_ProcessLookupError) + || (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_Exception)); +} + #define _set_debug_exception_cause(exception, format, ...) \ do { \ - if (!_Py_RemoteDebug_HasPermissionError()) { \ + if (!_Py_RemoteDebug_IsFatalReadError()) { \ PyThreadState *tstate = _PyThreadState_GET(); \ if (!_PyErr_Occurred(tstate)) { \ _PyErr_Format(tstate, exception, format, ##__VA_ARGS__); \ From ce031a67b184bd433fd53f76e7195b843f905454 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Mon, 5 Oct 2026 10:31:58 +0100 Subject: [PATCH 2/2] Preserve the stable ABI when creating fallback frame names --- .../pycore_global_objects_fini_generated.h | 3 --- Include/internal/pycore_global_strings.h | 3 --- .../internal/pycore_runtime_init_generated.h | 3 --- .../internal/pycore_unicodeobject_generated.h | 12 ------------ Modules/_remote_debugging/code_objects.c | 19 ++++++++++++------- 5 files changed, 12 insertions(+), 28 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 9af9ab2dc1f2dca..c536079498e9cbd 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -1334,9 +1334,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(native)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(str_replace_inf)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(type_params)); - _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(unknown_file)); - _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(unknown_function)); - _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(unreadable_frame)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(utf_8)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(AGEN_CLOSED)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(AGEN_CREATED)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 9bed9b9acd118a8..daa1f2ad2f109d7 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -54,9 +54,6 @@ struct _Py_global_strings { STRUCT_FOR_STR(native, "") STRUCT_FOR_STR(str_replace_inf, "1e309") STRUCT_FOR_STR(type_params, ".type_params") - STRUCT_FOR_STR(unknown_file, "") - STRUCT_FOR_STR(unknown_function, "") - STRUCT_FOR_STR(unreadable_frame, "") STRUCT_FOR_STR(utf_8, "utf-8") } literals; diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index a8ef7449144d86b..5c6635e6c3c232c 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1329,9 +1329,6 @@ extern "C" { INIT_STR(native, ""), \ INIT_STR(str_replace_inf, "1e309"), \ INIT_STR(type_params, ".type_params"), \ - INIT_STR(unknown_file, ""), \ - INIT_STR(unknown_function, ""), \ - INIT_STR(unreadable_frame, ""), \ INIT_STR(utf_8, "utf-8"), \ } diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 415c8ebb69b9cd0..ddda6b467ce1c1a 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -3464,22 +3464,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); - string = &_Py_STR(unknown_file); - _PyUnicode_InternStatic(interp, &string); - assert(_PyUnicode_CheckConsistency(string, 1)); - assert(PyUnicode_GET_LENGTH(string) != 1); - string = &_Py_STR(unknown_function); - _PyUnicode_InternStatic(interp, &string); - assert(_PyUnicode_CheckConsistency(string, 1)); - assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_STR(anon_unknown); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); - string = &_Py_STR(unreadable_frame); - _PyUnicode_InternStatic(interp, &string); - assert(_PyUnicode_CheckConsistency(string, 1)); - assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_STR(json_decoder); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Modules/_remote_debugging/code_objects.c b/Modules/_remote_debugging/code_objects.c index 081aaa49fb49ffb..2789c95e6e321b8 100644 --- a/Modules/_remote_debugging/code_objects.c +++ b/Modules/_remote_debugging/code_objects.c @@ -341,10 +341,6 @@ parse_code_object(RemoteUnwinderObject *unwinder, PyObject **result, const CodeObjectContext *ctx) { - _Py_DECLARE_STR(unknown_function, ""); - _Py_DECLARE_STR(unknown_file, ""); - _Py_DECLARE_STR(unreadable_frame, ""); - void *key = (void *)ctx->code_addr; CachedCodeMetadata *meta = NULL; PyObject *func = NULL; @@ -378,7 +374,10 @@ parse_code_object(RemoteUnwinderObject *unwinder, goto error; } PyErr_Clear(); - func = Py_NewRef(&_Py_STR(unreadable_frame)); + func = PyUnicode_FromString(""); + if (!func) { + goto error; + } file = Py_NewRef(_Py_LATIN1_CHR('~')); goto degraded; } @@ -390,7 +389,10 @@ parse_code_object(RemoteUnwinderObject *unwinder, goto error; } PyErr_Clear(); - func = Py_NewRef(&_Py_STR(unknown_function)); + func = PyUnicode_FromString(""); + if (!func) { + goto error; + } code_metadata_incomplete = 1; } @@ -401,7 +403,10 @@ parse_code_object(RemoteUnwinderObject *unwinder, goto error; } PyErr_Clear(); - file = Py_NewRef(&_Py_STR(unknown_file)); + file = PyUnicode_FromString(""); + if (!file) { + goto error; + } code_metadata_incomplete = 1; }