util: add API to fetch the current thread name

This will be used to include the thread name in error reports
in a later patch. It returns a const string stored in a thread
local to avoid memory allocation when it is called repeatedly
in a single thread. The thread name should be set at the very
start of the thread execution, which is the case when using
qemu_thread_create.

This uses the official thread APIs for fetching thread names,
so that it captures  names of threads spawned by code in 3rd
party libraries, not merely QEMU spawned thrads.

This also addresses the gap from the previous patch for setting
the name of the main thread. A constructor is used to initialize
the 'namebuf' thread-local in the main thread only.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé
2025-08-29 16:30:26 +01:00
parent d74938d14c
commit 215235d365
4 changed files with 119 additions and 5 deletions

View File

@@ -18,10 +18,41 @@
#include "qemu/tsan.h"
#include "qemu/bitmap.h"
#ifdef CONFIG_PTHREAD_SET_NAME_NP
#if defined(CONFIG_PTHREAD_SET_NAME_NP) || defined(CONFIG_PTHREAD_GET_NAME_NP)
#include <pthread_np.h>
#endif
/*
* This is not defined on Linux, but the man page indicates
* the buffer must be at least 16 bytes, including the NUL
* terminator
*/
#ifndef PTHREAD_MAX_NAMELEN_NP
#define PTHREAD_MAX_NAMELEN_NP 16
#endif
static __thread char namebuf[PTHREAD_MAX_NAMELEN_NP];
static void __attribute__((__constructor__(QEMU_CONSTRUCTOR_EARLY)))
qemu_thread_init(void)
{
/*
* Initialize the main thread name. We must not use
* qemu_thread_setname(), since on some platforms (at least Linux)
* this can change the process name that is reported by tools like
* 'ps'.
*
* This workaround suffices to ensure QEMU log/error messages
* get the main thread name, but at the cost of external tools
* like GDB not seeing it.
*
* NB using a constructor instead of static initializing namebuf,
* to ensure it only initializes the thread-local in the main
* thread
*/
g_strlcpy(namebuf, "main", sizeof(namebuf));
}
static void error_exit(int err, const char *msg)
{
fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err));
@@ -526,3 +557,23 @@ void *qemu_thread_join(QemuThread *thread)
}
return ret;
}
const char *qemu_thread_get_name(void)
{
int rv;
if (namebuf[0] != '\0') {
return namebuf;
}
# if defined(CONFIG_PTHREAD_GETNAME_NP)
rv = pthread_getname_np(pthread_self(), namebuf, sizeof(namebuf));
# elif defined(CONFIG_PTHREAD_GET_NAME_NP)
rv = pthread_get_name_np(pthread_self(), namebuf, sizeof(namebuf));
# else
rv = -1;
# endif
if (rv != 0) {
g_strlcpy(namebuf, "unnamed", G_N_ELEMENTS(namebuf));
}
return namebuf;
}

View File

@@ -19,7 +19,10 @@
typedef HRESULT (WINAPI *pSetThreadDescription) (HANDLE hThread,
PCWSTR lpThreadDescription);
typedef HRESULT (WINAPI *pGetThreadDescription) (HANDLE hThread,
PWSTR *lpThreadDescription);
static pSetThreadDescription SetThreadDescriptionFunc;
static pGetThreadDescription GetThreadDescriptionFunc;
static HMODULE kernel32_module;
static void __attribute__((__constructor__(QEMU_CONSTRUCTOR_EARLY)))
@@ -28,7 +31,7 @@ qemu_thread_init(void)
qemu_thread_set_name("main");
}
static bool load_set_thread_description(void)
static bool load_thread_description(void)
{
static gsize _init_once = 0;
@@ -38,14 +41,17 @@ static bool load_set_thread_description(void)
SetThreadDescriptionFunc =
(pSetThreadDescription)GetProcAddress(kernel32_module,
"SetThreadDescription");
if (!SetThreadDescriptionFunc) {
GetThreadDescriptionFunc =
(pGetThreadDescription)GetProcAddress(kernel32_module,
"GetThreadDescription");
if (!SetThreadDescriptionFunc || !GetThreadDescriptionFunc) {
FreeLibrary(kernel32_module);
}
}
g_once_init_leave(&_init_once, 1);
}
return !!SetThreadDescriptionFunc;
return (SetThreadDescriptionFunc && GetThreadDescriptionFunc);
}
static void error_exit(int err, const char *msg)
@@ -331,7 +337,7 @@ void qemu_thread_set_name(const char *name)
{
g_autofree wchar_t *namew = NULL;
if (!load_set_thread_description()) {
if (!load_thread_description()) {
return;
}
@@ -415,3 +421,38 @@ bool qemu_thread_is_self(QemuThread *thread)
{
return GetCurrentThreadId() == thread->tid;
}
static __thread char namebuf[64];
const char *qemu_thread_get_name(void)
{
HRESULT hr;
wchar_t *namew = NULL;
g_autofree char *name = NULL;
if (namebuf[0] != '\0') {
return namebuf;
}
if (!load_thread_description()) {
goto error;
}
hr = GetThreadDescriptionFunc(GetCurrentThread(), &namew);
if (!SUCCEEDED(hr)) {
goto error;
}
name = g_utf16_to_utf8(namew, -1, NULL, NULL, NULL);
LocalFree(namew);
if (!name) {
goto error;
}
g_strlcpy(namebuf, name, G_N_ELEMENTS(namebuf));
return namebuf;
error:
g_strlcpy(namebuf, "unnamed", G_N_ELEMENTS(namebuf));
return namebuf;
}