]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-125434: Display thread name in faulthandler on Windows (#142011)
authorVictor Stinner <vstinner@python.org>
Thu, 27 Nov 2025 12:05:37 +0000 (13:05 +0100)
committerGitHub <noreply@github.com>
Thu, 27 Nov 2025 12:05:37 +0000 (12:05 +0000)
* 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)

Include/internal/pycore_traceback.h
Misc/NEWS.d/next/Library/2025-10-27-16-01-41.gh-issue-125434.qy0uRA.rst [new file with mode: 0644]
Python/pylifecycle.c
Python/traceback.c

index a4f125e073d3d1dbfdb548ce9c6e268648afb812..8357cce9d899fbb5d2a6944d9d86c6dd1a4eea5b 100644 (file)
@@ -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 (file)
index 0000000..299e9f0
--- /dev/null
@@ -0,0 +1,2 @@
+Display thread name in :mod:`faulthandler` on Windows. Patch by Victor
+Stinner.
index f5fbdeedac34ad1ea0f7c4b4e52b8d91917893b5..fad69f82b607966bba1ca255b60e4f6351b3051c 100644 (file)
@@ -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)) {
index 5efa70fb676882fdeb3c177a7e48cd43036f4e5f..a46276f66b285b7e95679487f7a9590a9da58072 100644 (file)
@@ -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 @@ done:
     }
 }
 
+
+#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
+}