https://github.com/python/cpython/commit/ad60d8963effb7677dbab05c6d8b3c1c3d78904c
commit: ad60d8963effb7677dbab05c6d8b3c1c3d78904c
branch: 3.14
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2025-11-27T12:05:37Z
summary:
[3.14] gh-125434: Display thread name in faulthandler on Windows (#142011)
* gh-125434: Display thread name in faulthandler on Windows (#140675)
(cherry picked from commit 313145eab5f6ebca21d2e3c80c130980d3bcdc88)
* gh-125434: Fix non-ASCII thread names in faulthandler on Windows (#140700)
Add _Py_DumpWideString() function to dump a wide string as ASCII. It
supports surrogate pairs.
Replace _Py_EncodeLocaleRaw() with _Py_DumpWideString()
in write_thread_name().
(cherry picked from commit 80f20f58b2b8368ed8451a0161036dda94d8d33a)
files:
A Misc/NEWS.d/next/Library/2025-10-27-16-01-41.gh-issue-125434.qy0uRA.rst
M Include/internal/pycore_traceback.h
M Python/pylifecycle.c
M Python/traceback.c
diff --git a/Include/internal/pycore_traceback.h
b/Include/internal/pycore_traceback.h
index a4f125e073d3d1..8357cce9d899fb 100644
--- a/Include/internal/pycore_traceback.h
+++ b/Include/internal/pycore_traceback.h
@@ -103,6 +103,8 @@ extern int _Py_WriteIndent(int, PyObject *);
PyAPI_FUNC(void) _Py_InitDumpStack(void);
PyAPI_FUNC(void) _Py_DumpStack(int fd);
+extern void _Py_DumpTraceback_Init(void);
+
#ifdef __cplusplus
}
#endif
diff --git
a/Misc/NEWS.d/next/Library/2025-10-27-16-01-41.gh-issue-125434.qy0uRA.rst
b/Misc/NEWS.d/next/Library/2025-10-27-16-01-41.gh-issue-125434.qy0uRA.rst
new file mode 100644
index 00000000000000..299e9f04df7c39
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-10-27-16-01-41.gh-issue-125434.qy0uRA.rst
@@ -0,0 +1,2 @@
+Display thread name in :mod:`faulthandler` on Windows. Patch by Victor
+Stinner.
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index f5fbdeedac34ad..fad69f82b60796 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -503,6 +503,7 @@ pycore_init_runtime(_PyRuntimeState *runtime,
_PyRuntimeState_SetFinalizing(runtime, NULL);
_Py_InitVersion();
+ _Py_DumpTraceback_Init();
status = _Py_HashRandomization_Init(config);
if (_PyStatus_EXCEPTION(status)) {
diff --git a/Python/traceback.c b/Python/traceback.c
index 5efa70fb676882..a46276f66b285b 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -70,6 +70,13 @@ class traceback "PyTracebackObject *" "&PyTraceback_Type"
#include "clinic/traceback.c.h"
+
+#ifdef MS_WINDOWS
+typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*);
+static PF_GET_THREAD_DESCRIPTION pGetThreadDescription = NULL;
+#endif
+
+
static PyObject *
tb_create_raw(PyTracebackObject *next, PyFrameObject *frame, int lasti,
int lineno)
@@ -974,6 +981,52 @@ _Py_DumpASCII(int fd, PyObject *text)
}
}
+
+#ifdef MS_WINDOWS
+static void
+_Py_DumpWideString(int fd, wchar_t *str)
+{
+ Py_ssize_t size = wcslen(str);
+ int truncated;
+ if (MAX_STRING_LENGTH < size) {
+ size = MAX_STRING_LENGTH;
+ truncated = 1;
+ }
+ else {
+ truncated = 0;
+ }
+
+ for (Py_ssize_t i=0; i < size; i++) {
+ Py_UCS4 ch = str[i];
+ if (' ' <= ch && ch <= 126) {
+ /* printable ASCII character */
+ dump_char(fd, (char)ch);
+ }
+ else if (ch <= 0xff) {
+ PUTS(fd, "\\x");
+ _Py_DumpHexadecimal(fd, ch, 2);
+ }
+ else if (Py_UNICODE_IS_HIGH_SURROGATE(ch)
+ && Py_UNICODE_IS_LOW_SURROGATE(str[i+1])) {
+ ch = Py_UNICODE_JOIN_SURROGATES(ch, str[i+1]);
+ i++; // Skip the low surrogate character
+ PUTS(fd, "\\U");
+ _Py_DumpHexadecimal(fd, ch, 8);
+ }
+ else {
+ Py_BUILD_ASSERT(sizeof(wchar_t) == 2);
+ PUTS(fd, "\\u");
+ _Py_DumpHexadecimal(fd, ch, 4);
+ }
+ }
+
+ if (truncated) {
+ PUTS(fd, "...");
+ }
+}
+#endif
+
+
/* Write a frame into the file fd: "File "xxx", line xxx in xxx".
This function is signal safe.
@@ -1122,23 +1175,12 @@ _Py_DumpTraceback(int fd, PyThreadState *tstate)
# endif
#endif
-/* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if
- is_current is true, "Thread 0xHHHH:\n" otherwise.
-
- This function is signal safe. */
+// Write the thread name
static void
-write_thread_id(int fd, PyThreadState *tstate, int is_current)
+write_thread_name(int fd, PyThreadState *tstate)
{
- if (is_current)
- PUTS(fd, "Current thread 0x");
- else
- PUTS(fd, "Thread 0x");
- _Py_DumpHexadecimal(fd,
- tstate->thread_id,
- sizeof(unsigned long) * 2);
-
- // Write the thread name
+#ifndef MS_WINDOWS
#if defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP)
char name[100];
pthread_t thread = (pthread_t)tstate->thread_id;
@@ -1157,6 +1199,49 @@ write_thread_id(int fd, PyThreadState *tstate, int
is_current)
}
}
#endif
+#else
+ // Windows implementation
+ if (pGetThreadDescription == NULL) {
+ return;
+ }
+
+ HANDLE thread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE,
tstate->thread_id);
+ if (thread == NULL) {
+ return;
+ }
+
+ wchar_t *name;
+ HRESULT hr = pGetThreadDescription(thread, &name);
+ if (!FAILED(hr)) {
+ if (name[0] != 0) {
+ PUTS(fd, " [");
+ _Py_DumpWideString(fd, name);
+ PUTS(fd, "]");
+ }
+ LocalFree(name);
+ }
+ CloseHandle(thread);
+#endif
+}
+
+
+/* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if
+ is_current is true, "Thread 0xHHHH:\n" otherwise.
+
+ This function is signal safe (except on Windows). */
+
+static void
+write_thread_id(int fd, PyThreadState *tstate, int is_current)
+{
+ if (is_current)
+ PUTS(fd, "Current thread 0x");
+ else
+ PUTS(fd, "Thread 0x");
+ _Py_DumpHexadecimal(fd,
+ tstate->thread_id,
+ sizeof(unsigned long) * 2);
+
+ write_thread_name(fd, tstate);
PUTS(fd, " (most recent call first):\n");
}
@@ -1351,3 +1436,20 @@ _Py_InitDumpStack(void)
(void)backtrace(callstack, 1);
#endif
}
+
+
+void
+_Py_DumpTraceback_Init(void)
+{
+#ifdef MS_WINDOWS
+ if (pGetThreadDescription != NULL) {
+ return;
+ }
+
+ HMODULE kernelbase = GetModuleHandleW(L"kernelbase.dll");
+ if (kernelbase != NULL) {
+ pGetThreadDescription = (PF_GET_THREAD_DESCRIPTION)GetProcAddress(
+ kernelbase, "GetThreadDescription");
+ }
+#endif
+}
_______________________________________________
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]