Skip to content

Commit ad60d89

Browse files
authored
[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 313145e) * 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 80f20f5)
1 parent f47e928 commit ad60d89

File tree

4 files changed

+121
-14
lines changed

4 files changed

+121
-14
lines changed

Include/internal/pycore_traceback.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ extern int _Py_WriteIndent(int, PyObject *);
103103
PyAPI_FUNC(void) _Py_InitDumpStack(void);
104104
PyAPI_FUNC(void) _Py_DumpStack(int fd);
105105

106+
extern void _Py_DumpTraceback_Init(void);
107+
106108
#ifdef __cplusplus
107109
}
108110
#endif
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Display thread name in :mod:`faulthandler` on Windows. Patch by Victor
2+
Stinner.

Python/pylifecycle.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ pycore_init_runtime(_PyRuntimeState *runtime,
503503
_PyRuntimeState_SetFinalizing(runtime, NULL);
504504

505505
_Py_InitVersion();
506+
_Py_DumpTraceback_Init();
506507

507508
status = _Py_HashRandomization_Init(config);
508509
if (_PyStatus_EXCEPTION(status)) {

Python/traceback.c

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ class traceback "PyTracebackObject *" "&PyTraceback_Type"
7070

7171
#include "clinic/traceback.c.h"
7272

73+
74+
#ifdef MS_WINDOWS
75+
typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*);
76+
static PF_GET_THREAD_DESCRIPTION pGetThreadDescription = NULL;
77+
#endif
78+
79+
7380
static PyObject *
7481
tb_create_raw(PyTracebackObject *next, PyFrameObject *frame, int lasti,
7582
int lineno)
@@ -974,6 +981,52 @@ _Py_DumpASCII(int fd, PyObject *text)
974981
}
975982
}
976983

984+
985+
#ifdef MS_WINDOWS
986+
static void
987+
_Py_DumpWideString(int fd, wchar_t *str)
988+
{
989+
Py_ssize_t size = wcslen(str);
990+
int truncated;
991+
if (MAX_STRING_LENGTH < size) {
992+
size = MAX_STRING_LENGTH;
993+
truncated = 1;
994+
}
995+
else {
996+
truncated = 0;
997+
}
998+
999+
for (Py_ssize_t i=0; i < size; i++) {
1000+
Py_UCS4 ch = str[i];
1001+
if (' ' <= ch && ch <= 126) {
1002+
/* printable ASCII character */
1003+
dump_char(fd, (char)ch);
1004+
}
1005+
else if (ch <= 0xff) {
1006+
PUTS(fd, "\\x");
1007+
_Py_DumpHexadecimal(fd, ch, 2);
1008+
}
1009+
else if (Py_UNICODE_IS_HIGH_SURROGATE(ch)
1010+
&& Py_UNICODE_IS_LOW_SURROGATE(str[i+1])) {
1011+
ch = Py_UNICODE_JOIN_SURROGATES(ch, str[i+1]);
1012+
i++; // Skip the low surrogate character
1013+
PUTS(fd, "\\U");
1014+
_Py_DumpHexadecimal(fd, ch, 8);
1015+
}
1016+
else {
1017+
Py_BUILD_ASSERT(sizeof(wchar_t) == 2);
1018+
PUTS(fd, "\\u");
1019+
_Py_DumpHexadecimal(fd, ch, 4);
1020+
}
1021+
}
1022+
1023+
if (truncated) {
1024+
PUTS(fd, "...");
1025+
}
1026+
}
1027+
#endif
1028+
1029+
9771030
/* Write a frame into the file fd: "File "xxx", line xxx in xxx".
9781031
9791032
This function is signal safe.
@@ -1122,23 +1175,12 @@ _Py_DumpTraceback(int fd, PyThreadState *tstate)
11221175
# endif
11231176
#endif
11241177

1125-
/* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if
1126-
is_current is true, "Thread 0xHHHH:\n" otherwise.
1127-
1128-
This function is signal safe. */
11291178

1179+
// Write the thread name
11301180
static void
1131-
write_thread_id(int fd, PyThreadState *tstate, int is_current)
1181+
write_thread_name(int fd, PyThreadState *tstate)
11321182
{
1133-
if (is_current)
1134-
PUTS(fd, "Current thread 0x");
1135-
else
1136-
PUTS(fd, "Thread 0x");
1137-
_Py_DumpHexadecimal(fd,
1138-
tstate->thread_id,
1139-
sizeof(unsigned long) * 2);
1140-
1141-
// Write the thread name
1183+
#ifndef MS_WINDOWS
11421184
#if defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP)
11431185
char name[100];
11441186
pthread_t thread = (pthread_t)tstate->thread_id;
@@ -1157,6 +1199,49 @@ write_thread_id(int fd, PyThreadState *tstate, int is_current)
11571199
}
11581200
}
11591201
#endif
1202+
#else
1203+
// Windows implementation
1204+
if (pGetThreadDescription == NULL) {
1205+
return;
1206+
}
1207+
1208+
HANDLE thread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, tstate->thread_id);
1209+
if (thread == NULL) {
1210+
return;
1211+
}
1212+
1213+
wchar_t *name;
1214+
HRESULT hr = pGetThreadDescription(thread, &name);
1215+
if (!FAILED(hr)) {
1216+
if (name[0] != 0) {
1217+
PUTS(fd, " [");
1218+
_Py_DumpWideString(fd, name);
1219+
PUTS(fd, "]");
1220+
}
1221+
LocalFree(name);
1222+
}
1223+
CloseHandle(thread);
1224+
#endif
1225+
}
1226+
1227+
1228+
/* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if
1229+
is_current is true, "Thread 0xHHHH:\n" otherwise.
1230+
1231+
This function is signal safe (except on Windows). */
1232+
1233+
static void
1234+
write_thread_id(int fd, PyThreadState *tstate, int is_current)
1235+
{
1236+
if (is_current)
1237+
PUTS(fd, "Current thread 0x");
1238+
else
1239+
PUTS(fd, "Thread 0x");
1240+
_Py_DumpHexadecimal(fd,
1241+
tstate->thread_id,
1242+
sizeof(unsigned long) * 2);
1243+
1244+
write_thread_name(fd, tstate);
11601245

11611246
PUTS(fd, " (most recent call first):\n");
11621247
}
@@ -1351,3 +1436,20 @@ _Py_InitDumpStack(void)
13511436
(void)backtrace(callstack, 1);
13521437
#endif
13531438
}
1439+
1440+
1441+
void
1442+
_Py_DumpTraceback_Init(void)
1443+
{
1444+
#ifdef MS_WINDOWS
1445+
if (pGetThreadDescription != NULL) {
1446+
return;
1447+
}
1448+
1449+
HMODULE kernelbase = GetModuleHandleW(L"kernelbase.dll");
1450+
if (kernelbase != NULL) {
1451+
pGetThreadDescription = (PF_GET_THREAD_DESCRIPTION)GetProcAddress(
1452+
kernelbase, "GetThreadDescription");
1453+
}
1454+
#endif
1455+
}

0 commit comments

Comments
 (0)