@samitouri / QOSamiQemu / commits / 215235d365

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>

Daniel P. Berrangé committed Aug 29, 2025 at 16:30 UTC 215235d365e49c72a85ea2940751e45419676031
4 files changed +119 -5
include/qemu/thread.h
+1
@@ -216,6 +216,7 @@ void qemu_thread_get_self(QemuThread *thread);
216 bool qemu_thread_is_self(QemuThread *thread);
217 G_NORETURN void qemu_thread_exit(void *retval);
218 void qemu_thread_set_name(const char *name);
219 +const char *qemu_thread_get_name(void);
220
221 struct Notifier;
222 /**
meson.build
+21
@@ -2856,6 +2856,27 @@ config_host_data.set('CONFIG_PTHREAD_SET_NAME_NP', cc.links(osdep_prefix + '''
2856 pthread_set_name_np(thread, "QEMU");
2857 return 0;
2858 }''', dependencies: threads))
2859 +
2860 +config_host_data.set('CONFIG_PTHREAD_GETNAME_NP', cc.links(osdep_prefix + '''
2861 + #include <pthread.h>
2862 +
2863 + int main(void)
2864 + {
2865 + char buf[16];
2866 + pthread_getname_np(pthread_self(), buf, sizeof(buf));
2867 + return 0;
2868 + }''', dependencies: threads))
2869 +config_host_data.set('CONFIG_PTHREAD_GET_NAME_NP', cc.links(osdep_prefix + '''
2870 + #include <pthread.h>
2871 + #include <pthread_np.h>
2872 +
2873 + int main(void)
2874 + {
2875 + char buf[16];
2876 + pthread_get_name_np(pthread_self(), buf, sizeof(buf));
2877 + return 0;
2878 + }''', dependencies: threads))
2879 +
2880 config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(osdep_prefix + '''
2881 #include <pthread.h>
2882
util/qemu-thread-posix.c
+52 -1
@@ -18,10 +18,41 @@
18 #include "qemu/tsan.h"
19 #include "qemu/bitmap.h"
20
21 -#ifdef CONFIG_PTHREAD_SET_NAME_NP
21 +#if defined(CONFIG_PTHREAD_SET_NAME_NP) || defined(CONFIG_PTHREAD_GET_NAME_NP)
22 #include <pthread_np.h>
23 #endif
24
25 +/*
26 + * This is not defined on Linux, but the man page indicates
27 + * the buffer must be at least 16 bytes, including the NUL
28 + * terminator
29 + */
30 +#ifndef PTHREAD_MAX_NAMELEN_NP
31 +#define PTHREAD_MAX_NAMELEN_NP 16
32 +#endif
33 +
34 +static __thread char namebuf[PTHREAD_MAX_NAMELEN_NP];
35 +
36 +static void __attribute__((__constructor__(QEMU_CONSTRUCTOR_EARLY)))
37 +qemu_thread_init(void)
38 +{
39 + /*
40 + * Initialize the main thread name. We must not use
41 + * qemu_thread_setname(), since on some platforms (at least Linux)
42 + * this can change the process name that is reported by tools like
43 + * 'ps'.
44 + *
45 + * This workaround suffices to ensure QEMU log/error messages
46 + * get the main thread name, but at the cost of external tools
47 + * like GDB not seeing it.
48 + *
49 + * NB using a constructor instead of static initializing namebuf,
50 + * to ensure it only initializes the thread-local in the main
51 + * thread
52 + */
53 + g_strlcpy(namebuf, "main", sizeof(namebuf));
54 +}
55 +
56 static void error_exit(int err, const char *msg)
57 {
58 fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err));
@@ -526,3 +557,23 @@ void *qemu_thread_join(QemuThread *thread)
557 }
558 return ret;
559 }
560 +
561 +const char *qemu_thread_get_name(void)
562 +{
563 + int rv;
564 + if (namebuf[0] != '\0') {
565 + return namebuf;
566 + }
567 +
568 +# if defined(CONFIG_PTHREAD_GETNAME_NP)
569 + rv = pthread_getname_np(pthread_self(), namebuf, sizeof(namebuf));
570 +# elif defined(CONFIG_PTHREAD_GET_NAME_NP)
571 + rv = pthread_get_name_np(pthread_self(), namebuf, sizeof(namebuf));
572 +# else
573 + rv = -1;
574 +# endif
575 + if (rv != 0) {
576 + g_strlcpy(namebuf, "unnamed", G_N_ELEMENTS(namebuf));
577 + }
578 + return namebuf;
579 +}
util/qemu-thread-win32.c
+45 -4
@@ -19,7 +19,10 @@
19
20 typedef HRESULT (WINAPI *pSetThreadDescription) (HANDLE hThread,
21 PCWSTR lpThreadDescription);
22 +typedef HRESULT (WINAPI *pGetThreadDescription) (HANDLE hThread,
23 + PWSTR *lpThreadDescription);
24 static pSetThreadDescription SetThreadDescriptionFunc;
25 +static pGetThreadDescription GetThreadDescriptionFunc;
26 static HMODULE kernel32_module;
27
28 static void __attribute__((__constructor__(QEMU_CONSTRUCTOR_EARLY)))
@@ -28,7 +31,7 @@ qemu_thread_init(void)
31 qemu_thread_set_name("main");
32 }
33
31 -static bool load_set_thread_description(void)
34 +static bool load_thread_description(void)
35 {
36 static gsize _init_once = 0;
37
@@ -38,14 +41,17 @@ static bool load_set_thread_description(void)
41 SetThreadDescriptionFunc =
42 (pSetThreadDescription)GetProcAddress(kernel32_module,
43 "SetThreadDescription");
41 - if (!SetThreadDescriptionFunc) {
44 + GetThreadDescriptionFunc =
45 + (pGetThreadDescription)GetProcAddress(kernel32_module,
46 + "GetThreadDescription");
47 + if (!SetThreadDescriptionFunc || !GetThreadDescriptionFunc) {
48 FreeLibrary(kernel32_module);
49 }
50 }
51 g_once_init_leave(&_init_once, 1);
52 }
53
48 - return !!SetThreadDescriptionFunc;
54 + return (SetThreadDescriptionFunc && GetThreadDescriptionFunc);
55 }
56
57 static void error_exit(int err, const char *msg)
@@ -331,7 +337,7 @@ void qemu_thread_set_name(const char *name)
337 {
338 g_autofree wchar_t *namew = NULL;
339
334 - if (!load_set_thread_description()) {
340 + if (!load_thread_description()) {
341 return;
342 }
343
@@ -415,3 +421,38 @@ bool qemu_thread_is_self(QemuThread *thread)
421 {
422 return GetCurrentThreadId() == thread->tid;
423 }
424 +
425 +static __thread char namebuf[64];
426 +
427 +const char *qemu_thread_get_name(void)
428 +{
429 + HRESULT hr;
430 + wchar_t *namew = NULL;
431 + g_autofree char *name = NULL;
432 +
433 + if (namebuf[0] != '\0') {
434 + return namebuf;
435 + }
436 +
437 + if (!load_thread_description()) {
438 + goto error;
439 + }
440 +
441 + hr = GetThreadDescriptionFunc(GetCurrentThread(), &namew);
442 + if (!SUCCEEDED(hr)) {
443 + goto error;
444 + }
445 +
446 + name = g_utf16_to_utf8(namew, -1, NULL, NULL, NULL);
447 + LocalFree(namew);
448 + if (!name) {
449 + goto error;
450 + }
451 +
452 + g_strlcpy(namebuf, name, G_N_ELEMENTS(namebuf));
453 + return namebuf;
454 +
455 + error:
456 + g_strlcpy(namebuf, "unnamed", G_N_ELEMENTS(namebuf));
457 + return namebuf;
458 +}