From 5bd051648891c1c0186e9322d46b4b3fa39295a4 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 9 Jun 2024 14:01:24 +0200 Subject: [PATCH] MDEV-32537 Name threads to improve debugging experience and diagnostics. Use SetThreadDescription/pthread_setname_np to give threads a name. --- include/mysql/psi/psi.h | 8 +++ include/mysql/psi/psi_abi_v1.h.pp | 2 + include/pfs_thread_provider.h | 2 + mysys/CMakeLists.txt | 2 +- mysys/my_thr_init.c | 29 --------- mysys/my_thread_name.cc | 97 +++++++++++++++++++++++++++++ mysys/psi_noop.c | 6 ++ sql/mysqld.cc | 2 +- sql/rpl_parallel.cc | 2 +- sql/semisync_master_ack_receiver.cc | 2 +- sql/sql_manager.cc | 2 +- sql/threadpool_generic.cc | 2 +- storage/maria/ma_checkpoint.c | 2 +- storage/perfschema/pfs.cc | 9 +++ 14 files changed, 131 insertions(+), 36 deletions(-) create mode 100644 mysys/my_thread_name.cc diff --git a/include/mysql/psi/psi.h b/include/mysql/psi/psi.h index fefc3391b37..48abf93b627 100644 --- a/include/mysql/psi/psi.h +++ b/include/mysql/psi/psi.h @@ -1624,6 +1624,12 @@ typedef void (*set_thread_os_id_v1_t)(struct PSI_thread *thread); */ typedef struct PSI_thread* (*get_thread_v1_t)(void); +/** + Get name of the thread, according to the thread class. + The name is returns without the thread/subsystem prefix. +*/ +typedef const char* (*get_thread_class_name_v1_t)(void); + /** Assign a user name to the instrumented thread. @param user the user name @@ -2538,6 +2544,8 @@ struct PSI_v1 set_thread_os_id_v1_t set_thread_os_id; /** @sa get_thread_v1_t. */ get_thread_v1_t get_thread; + /** @sa get_thread_name_v1_t. */ + get_thread_class_name_v1_t get_thread_class_name; /** @sa set_thread_user_v1_t. */ set_thread_user_v1_t set_thread_user; /** @sa set_thread_account_v1_t. */ diff --git a/include/mysql/psi/psi_abi_v1.h.pp b/include/mysql/psi/psi_abi_v1.h.pp index ca0d0206cb9..e0db18f8d7c 100644 --- a/include/mysql/psi/psi_abi_v1.h.pp +++ b/include/mysql/psi/psi_abi_v1.h.pp @@ -449,6 +449,7 @@ typedef void (*set_thread_id_v1_t)(struct PSI_thread *thread, ulonglong id); typedef void (*set_thread_os_id_v1_t)(struct PSI_thread *thread); typedef struct PSI_thread* (*get_thread_v1_t)(void); +typedef const char* (*get_thread_class_name_v1_t)(void); typedef void (*set_thread_user_v1_t)(const char *user, int user_len); typedef void (*set_thread_account_v1_t)(const char *user, int user_len, const char *host, int host_len); @@ -725,6 +726,7 @@ struct PSI_v1 set_thread_THD_v1_t set_thread_THD; set_thread_os_id_v1_t set_thread_os_id; get_thread_v1_t get_thread; + get_thread_class_name_v1_t get_thread_class_name; set_thread_user_v1_t set_thread_user; set_thread_account_v1_t set_thread_account; set_thread_db_v1_t set_thread_db; diff --git a/include/pfs_thread_provider.h b/include/pfs_thread_provider.h index 6b5633722f5..0d1f675768a 100644 --- a/include/pfs_thread_provider.h +++ b/include/pfs_thread_provider.h @@ -80,6 +80,8 @@ void pfs_set_thread_os_id_v1(PSI_thread *thread); PSI_thread* pfs_get_thread_v1(void); +const char *pfs_get_thread_class_name_v1(void); + void pfs_set_thread_user_v1(const char *user, int user_len); void pfs_set_thread_account_v1(const char *user, int user_len, diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 646a8bae174..d39893bc415 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -46,7 +46,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c my_default.c my_rdtsc.c psi_noop.c my_atomic_writes.c my_cpu.c my_likely.c my_largepage.c file_logger.c my_dlerror.c crc32/crc32c.cc - my_timezone.cc) + my_timezone.cc my_thread_name.cc) IF (WIN32) SET (MYSYS_SOURCES ${MYSYS_SOURCES} diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index 0021a37faff..f40dab43fa8 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -172,35 +172,6 @@ my_bool my_thread_global_init(void) return 0; } -#ifdef _WIN32 -#define MAX_THREAD_NAME 256 -#elif defined(__linux__) -#define MAX_THREAD_NAME 16 -#elif defined(__FreeBSD__) || defined(__OpenBSD__) -#include -#endif - -void my_thread_set_name(const char *name) -{ -#ifdef _WIN32 - wchar_t wname[MAX_THREAD_NAME]; - wname[0]= 0; - MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, MAX_THREAD_NAME); - SetThreadDescription(GetCurrentThread(), wname); -#elif defined __linux__ - char shortname[MAX_THREAD_NAME]; - snprintf(shortname, MAX_THREAD_NAME, "%s", name); - pthread_setname_np(pthread_self(), shortname); -#elif defined __NetBSD__ - pthread_setname_np(pthread_self(), "%s", (void *) name); -#elif defined __FreeBSD__ || defined __OpenBSD__ - pthread_set_name_np(pthread_self(), name); -#elif defined __APPLE__ - pthread_setname_np(name); -#else - (void) name; -#endif -} /** End the mysys thread system. Called when ending the last thread diff --git a/mysys/my_thread_name.cc b/mysys/my_thread_name.cc new file mode 100644 index 00000000000..d52d7360158 --- /dev/null +++ b/mysys/my_thread_name.cc @@ -0,0 +1,97 @@ +/* Copyright 2024, MariaDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include +#include +#include +#include "mysql/psi/psi.h" +#include + +#ifdef _WIN32 +#define MAX_THREAD_NAME 256 +typedef HRESULT (*func_SetThreadDescription)(HANDLE,PCWSTR); +#elif defined(__linux__) +#define MAX_THREAD_NAME 16 +#elif defined(__FreeBSD__) || defined(__OpenBSD__) +#include +#endif + +#if defined(HAVE_PSI_THREAD_INTERFACE) && !defined DBUG_OFF +/** + Check that the name is consistent with PSI. + Require that the name matches the last part of PSI's class name + (e.g. "thread/sql/main" -> "main"). + + We drop the namespace prefix, because these thread names are + truncated to 15 characters on Linux, and something like "innodb/" would + already take up about half of that. +*/ +static void dbug_verify_thread_name(const char *name) +{ + const char *psi_name= NULL; + const char *thread_class_name= PSI_THREAD_CALL(get_thread_class_name)(); + if (thread_class_name) + { + /* Remove the path prefix */ + const char *last_part= strrchr(thread_class_name, '/'); + if (last_part) + psi_name= last_part + 1; + else + psi_name= thread_class_name; + } + if (psi_name && strcmp(psi_name, name)) + { + fprintf(stderr, "my_thread_set_name() mismatch: PSI name '%s' != '%s'\n", + psi_name, name); + abort(); + } +} +#else +#define dbug_verify_thread_name(name) do {} while (0) +#endif + +/* Set current thread name for debugger/profiler and similar tools. */ +extern "C" void my_thread_set_name(const char *name) +{ + dbug_verify_thread_name(name); + +#ifdef _WIN32 + /* + SetThreadDescription might not be there on older Windows versions. + Load it dynamically. + */ + static func_SetThreadDescription my_SetThreadDescription= + (func_SetThreadDescription) GetProcAddress(GetModuleHandle("kernel32"), + "SetThreadDescription"); + if (!my_SetThreadDescription) + return; + wchar_t wname[MAX_THREAD_NAME]; + wname[0]= 0; + MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, MAX_THREAD_NAME); + my_SetThreadDescription(GetCurrentThread(), wname); +#elif defined __linux__ + char shortname[MAX_THREAD_NAME]; + snprintf(shortname, MAX_THREAD_NAME, "%s", name); + pthread_setname_np(pthread_self(), shortname); +#elif defined __NetBSD__ + pthread_setname_np(pthread_self(), "%s", (void *) name); +#elif defined __FreeBSD__ || defined __OpenBSD__ + pthread_set_name_np(pthread_self(), name); +#elif defined __APPLE__ + pthread_setname_np(name); +#else + (void) name; +#endif +} diff --git a/mysys/psi_noop.c b/mysys/psi_noop.c index 403be3c94e9..cc40e609f63 100644 --- a/mysys/psi_noop.c +++ b/mysys/psi_noop.c @@ -228,6 +228,11 @@ get_thread_noop(void NNN) return NULL; } +static const char* get_thread_class_name_noop(void) +{ + return NULL; +} + static void set_thread_user_noop(const char *user NNN, int user_len NNN) { return; @@ -938,6 +943,7 @@ static PSI PSI_noop= set_thread_THD_noop, set_thread_os_id_noop, get_thread_noop, + get_thread_class_name_noop, set_thread_user_noop, set_thread_user_host_noop, set_thread_db_noop, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5be648dc2e5..269f34bcfce 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3219,7 +3219,7 @@ pthread_handler_t signal_hand(void *) sigset_t set; int sig; my_thread_init(); // Init new thread - my_thread_set_name("signal_hand"); + my_thread_set_name("signal_handler"); signal_thread_in_use= 1; if (test_flags & TEST_SIGINT) diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index bdf089cb5d9..7422b548a29 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -1226,7 +1226,7 @@ handle_rpl_parallel_thread(void *arg) struct rpl_parallel_thread *rpt= (struct rpl_parallel_thread *)arg; my_thread_init(); - my_thread_set_name("rpl_parallel"); + my_thread_set_name("rpl_parallel_thread"); thd = new THD(next_thread_id()); thd->thread_stack = (char*)&thd; server_threads.insert(thd); diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc index 77a5c13b9f4..f5aafb57aba 100644 --- a/sql/semisync_master_ack_receiver.cc +++ b/sql/semisync_master_ack_receiver.cc @@ -33,7 +33,7 @@ pthread_handler_t ack_receive_handler(void *arg) Ack_receiver *recv= reinterpret_cast(arg); my_thread_init(); - my_thread_set_name("ack_receive"); + my_thread_set_name("Ack_receiver"); recv->run(); my_thread_end(); diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index 2cffddf0158..be56cb03661 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -75,7 +75,7 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused))) struct timespec abstime; bool reset_flush_time = TRUE; my_thread_init(); - my_thread_set_name("handle_manager"); + my_thread_set_name("manager"); DBUG_ENTER("handle_manager"); pthread_detach_this_thread(); diff --git a/sql/threadpool_generic.cc b/sql/threadpool_generic.cc index 74405ee4c80..4dd9faddd7e 100644 --- a/sql/threadpool_generic.cc +++ b/sql/threadpool_generic.cc @@ -1534,7 +1534,7 @@ static void *worker_main(void *param) worker_thread_t this_thread; pthread_detach_this_thread(); my_thread_init(); - my_thread_set_name("connection_worker"); + my_thread_set_name("worker_thread"); DBUG_ENTER("worker_main"); diff --git a/storage/maria/ma_checkpoint.c b/storage/maria/ma_checkpoint.c index 18653ed130c..14325a50296 100644 --- a/storage/maria/ma_checkpoint.c +++ b/storage/maria/ma_checkpoint.c @@ -577,7 +577,7 @@ pthread_handler_t ma_checkpoint_background(void *arg) PAGECACHE_FILE *UNINIT_VAR(kfile); /**< index file currently being flushed */ my_thread_init(); - my_thread_set_name("ma_checkpoint_background"); + my_thread_set_name("checkpoint_background"); DBUG_PRINT("info",("Maria background checkpoint thread starts")); DBUG_ASSERT(interval > 0); diff --git a/storage/perfschema/pfs.cc b/storage/perfschema/pfs.cc index 1a861b2a1a3..f6609c53b5a 100644 --- a/storage/perfschema/pfs.cc +++ b/storage/perfschema/pfs.cc @@ -2325,6 +2325,14 @@ pfs_get_thread_v1(void) return reinterpret_cast (pfs); } +const char *pfs_get_thread_class_name_v1(void) +{ + PFS_thread *pfs= my_thread_get_THR_PFS(); + if (!pfs) + return NULL; + return pfs->m_class->m_name; +} + /** Implementation of the thread instrumentation interface. @sa PSI_v1::set_thread_user. @@ -7040,6 +7048,7 @@ PSI_v1 PFS_v1= pfs_set_thread_THD_v1, pfs_set_thread_os_id_v1, pfs_get_thread_v1, + pfs_get_thread_class_name_v1, pfs_set_thread_user_v1, pfs_set_thread_account_v1, pfs_set_thread_db_v1,