Support proper Windows kernel Unicode thread names. (mono/mono#15919)
authorJay Krell <jaykrell@microsoft.com>
Thu, 1 Aug 2019 13:53:41 +0000 (06:53 -0700)
committerAlexander Köplinger <alex.koeplinger@outlook.com>
Thu, 1 Aug 2019 13:53:41 +0000 (15:53 +0200)
These do not require a debugger, can be retrieved, appear in ETW, etc.

Extracted from https://github.com/mono/mono/pull/15859.

Commit migrated from https://github.com/mono/mono/commit/a7b74fc69c8c42911c5406bceca35220c87fccc4

src/mono/mono/metadata/sgen-mono.c
src/mono/mono/metadata/threads-types.h
src/mono/mono/metadata/threads.c
src/mono/mono/metadata/w32subset.h
src/mono/mono/utils/Makefile.am
src/mono/mono/utils/mono-threads-windows.c
src/mono/mono/utils/mono-windows-thread-name.c [new file with mode: 0644]
src/mono/msvc/libmonoutils-win32.targets
src/mono/msvc/libmonoutils-win32.targets.filters

index d5a95be..61d019d 100644 (file)
@@ -2123,6 +2123,7 @@ sgen_client_thread_register_worker (void)
 {
        mono_thread_info_register_small_id ();
        mono_native_thread_set_name (mono_native_thread_id_get (), "SGen worker");
+       mono_thread_set_name_windows (GetCurrentThread (), L"SGen worker");
 }
 
 /* Variables holding start/end nursery so it won't have to be passed at every call */
index 172c48d..4e96f99 100644 (file)
@@ -325,6 +325,20 @@ MONO_API MonoException* mono_thread_get_undeniable_exception (void);
 ICALL_EXPORT
 void ves_icall_thread_finish_async_abort (void);
 
+#if HOST_WIN32
+
+void
+mono_thread_set_name_windows (HANDLE thread_handle, PCWSTR thread_name);
+
+#define MONO_THREAD_NAME_WINDOWS_CONSTANT(x) L ## x
+
+#else
+
+#define mono_thread_set_name_windows(thread_handle, thread_name) /* nothing */
+
+#define MONO_THREAD_NAME_WINDOWS_CONSTANT(x) NULL
+
+#endif
 
 typedef enum {
     MonoSetThreadNameFlag_None      = 0x0000,
index 9584d05..00a9f27 100644 (file)
@@ -1899,6 +1899,8 @@ mono_thread_set_name_internal (MonoInternalThread *this_obj,
                mono_native_thread_set_name (tid, tname);
                mono_free (tname);
        }
+
+       mono_thread_set_name_windows (this_obj->native_handle, name ? mono_string_chars_internal (name) : NULL);
 }
 
 void 
index c3be134..d2035bd 100644 (file)
 #define HAVE_API_SUPPORT_WIN32_IS_WOW64_PROCESS 0
 #endif
 #endif
+
+#ifndef HAVE_LOADLIBRARY
+#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
+#define HAVE_LOADLIBRARY 1
+#else
+#define HAVE_LOADLIBRARY 0
+#endif
+#endif
+
+#ifndef HAVE_SET_THREAD_DESCRIPTION
+#define HAVE_SET_THREAD_DESCRIPTION 0
+#endif
+
+#ifndef HAVE_SET_THREAD_NAME
+// https://github.com/microsoft/xbox-live-api/blob/90b38b434d9c13ce4916c116cd28a98b239e38e2/InProgressSamples/Kits/ATGTK/ThreadHelpers.h#L21
+#if defined(_XBOX_ONE) && defined(_TITLE)
+#define HAVE_SET_THREAD_NAME 1
+#else
+#define HAVE_SET_THREAD_NAME 0
+#endif
+#endif
index 19a65eb..c870dac 100644 (file)
@@ -41,9 +41,10 @@ endif
 
 if HOST_WIN32
 win32_sources = \
-       os-event-win32.c \
+       mono-os-semaphore-win32.c \
        mono-os-wait-win32.c \
-       mono-os-semaphore-win32.c
+       mono-windows-thread-name.c \
+       os-event-win32.c
 
 platform_sources = $(win32_sources)
 else
index 4c0f162..5614f23 100644 (file)
@@ -563,38 +563,6 @@ mono_thread_info_get_system_max_stack_size (void)
        return INT_MAX;
 }
 
-#if defined(_MSC_VER)
-const DWORD MS_VC_EXCEPTION=0x406D1388;
-#pragma pack(push,8)
-typedef struct tagTHREADNAME_INFO
-{
-   DWORD dwType; // Must be 0x1000.
-   LPCSTR szName; // Pointer to name (in user addr space).
-   DWORD dwThreadID; // Thread ID (-1=caller thread).
-  DWORD dwFlags; // Reserved for future use, must be zero.
-} THREADNAME_INFO;
-#pragma pack(pop)
-#endif
-
-void
-mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
-{
-#if defined(_MSC_VER)
-       /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
-       THREADNAME_INFO info;
-       info.dwType = 0x1000;
-       info.szName = name;
-       info.dwThreadID = tid;
-       info.dwFlags = 0;
-
-       __try {
-               RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
-       }
-       __except(EXCEPTION_EXECUTE_HANDLER) {
-       }
-#endif
-}
-
 void
 mono_memory_barrier_process_wide (void)
 {
diff --git a/src/mono/mono/utils/mono-windows-thread-name.c b/src/mono/mono/utils/mono-windows-thread-name.c
new file mode 100644 (file)
index 0000000..efd42eb
--- /dev/null
@@ -0,0 +1,149 @@
+// mono-windows-thread-name.c
+//
+// There several ways to set thread name on Windows.
+//
+// Historically: Raise an exception with an ASCII string.
+//  This is visible only in a debugger. Only if a debugger
+//  is running when the exception is raised. There is
+//  no way to get the thread name and they do not appear in ETW traces.
+//
+// XboxOne: SetThreadName(thread handle, unicode)
+//   This works with or without a debugger and can be retrieved with GetThreadName.
+//   Sometimes an inline function SetThreadDescription for source compat with next.
+//   https://github.com/microsoft/xbox-live-api/blob/90b38b434d9c13ce4916c116cd28a98b239e38e2/InProgressSamples/Kits/ATGTK/ThreadHelpers.h#L21
+//
+// Windows 10 1607 or newer (according to documentation, or Creators Update says https://randomascii.wordpress.com/2015/10/26/thread-naming-in-windows-time-for-something-better).
+//  SetThreadDescription(thread handle, unicode)
+//  This is like XboxOne -- works with or without debugger, can be retrieved
+//  with GetThreadDescription, and appears in ETW traces.
+//  See https://randomascii.wordpress.com/2015/10/26/thread-naming-in-windows-time-for-something-better.
+//
+//  This is not called SetThreadName to avoid breaking compilation of C (but not C++)
+//  copied from http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx.
+//
+// UWP: Also SetThreadDescription, but running prior to 1607 (or Creators Update per above).
+//   would require LoadLibrary / GetProcAddress, but LoadLibrary is not allowed.
+//
+// Author:
+//  Jay Krell (jaykrell@microsoft.com)
+//
+// Copyright 2019 Microsoft
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+#include "config.h"
+#include "mono-threads.h"
+
+#if HOST_WIN32
+
+#include "mono/metadata/w32subset.h"
+
+// This is compiler specific because of the use of __try / __except.
+#if _MSC_VER
+const DWORD MS_VC_EXCEPTION = 0x406D1388;
+#pragma pack(push,8)
+typedef struct tagTHREADNAME_INFO
+{
+       DWORD dwType;     // Must be 0x1000.
+       PCSTR szName;     // Pointer to name (in user addr space).
+       DWORD dwThreadID; // Thread ID (-1=caller thread).
+       DWORD dwFlags;    // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+#pragma pack(pop)
+#endif
+
+void
+mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
+{
+// This is compiler specific because of the use of __try / __except.
+#if _MSC_VER
+       // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
+       THREADNAME_INFO info = {0x1000, name, tid, 0};
+
+       // Checking for IsDebuggerPresent here would be reasonable, for
+       // efficiency, and would let this work with other compilers (without
+       // structured exception handling support), however
+       // there is a race condition then, in that a debugger
+       // can be detached (and attached) at any time.
+       //
+       // A vectored exception handler could also be used to handle this exception.
+
+       __try {
+               RaiseException (MS_VC_EXCEPTION, 0, sizeof (info) / sizeof (ULONG_PTR), (ULONG_PTR*)&info);
+       } __except (EXCEPTION_EXECUTE_HANDLER) {
+       }
+#endif
+}
+
+#if HAVE_SET_THREAD_NAME
+
+void
+mono_thread_set_name_windows (HANDLE thread_handle, PCWSTR thread_name)
+{
+       SetThreadName (thread_handle, thread_name);
+}
+
+#elif HAVE_SET_THREAD_DESCRIPTION
+
+void
+mono_thread_set_name_windows (HANDLE thread_handle, PCWSTR thread_name)
+{
+       SetThreadDescription (thread_handle, thread_name);
+}
+
+#elif HAVE_LOADLIBRARY
+
+typedef
+HRESULT
+(__stdcall * MonoSetThreadDescription_t) (HANDLE thread_handle, PCWSTR thread_name);
+
+static
+HRESULT
+__stdcall
+mono_thread_set_name_windows_fallback_nop (HANDLE thread_handle, PCWSTR thread_name)
+{
+       // This function is called on older systems, when LoadLibrary / GetProcAddress fail.
+       return 0;
+}
+
+static
+HRESULT
+__stdcall
+mono_thread_set_name_windows_init (HANDLE thread_handle, PCWSTR thread_name);
+
+static MonoSetThreadDescription_t set_thread_description = mono_thread_set_name_windows_init;
+
+static
+HRESULT
+__stdcall
+mono_thread_set_name_windows_init (HANDLE thread_handle, PCWSTR thread_name)
+{
+       // This function is called the first time mono_thread_set_name_windows is called
+       // to LoadLibrary / GetProcAddress.
+       //
+       // Do not write NULL to global, that is racy.
+       MonoSetThreadDescription_t local = NULL;
+       const HMODULE kernel32 = LoadLibraryExW (L"kernel32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+       if (kernel32)
+               local = (MonoSetThreadDescription_t)GetProcAddress (kernel32, "SetThreadDescription");
+       if (!local)
+               local = mono_thread_set_name_windows_fallback_nop;
+       set_thread_description = local;
+       return local (thread_handle, thread_name);
+}
+
+void
+mono_thread_set_name_windows (HANDLE thread_handle, PCWSTR thread_name)
+{
+       (void)set_thread_description (thread_handle, thread_name);
+}
+
+#else // nothing
+
+void
+mono_thread_set_name_windows (HANDLE thread_handle, PCWSTR thread_name)
+{
+}
+
+#endif
+
+#endif // Win32
index 2c237d9..7691abc 100644 (file)
@@ -4,5 +4,6 @@
     <ClCompile Include="$(MonoSourceLocation)\mono\utils\os-event-win32.c" />
     <ClCompile Include="$(MonoSourceLocation)\mono\utils\mono-os-wait-win32.c" />
     <ClCompile Include="$(MonoSourceLocation)\mono\utils\mono-os-semaphore-win32.c" />
+    <ClCompile Include="$(MonoSourceLocation)\mono\utils\mono-windows-thread-name.c" />
   </ItemGroup>
 </Project>
index eab5c88..197e374 100644 (file)
@@ -10,6 +10,9 @@
     <ClCompile Include="$(MonoSourceLocation)\mono\utils\mono-os-semaphore-win32.c">
       <Filter>Source Files$(MonoUtilsFilterSubFolder)\win32</Filter>
     </ClCompile>
+    <ClCompile Include="$(MonoSourceLocation)\mono\utils\mono-windows-thread-name.c">
+      <Filter>Source Files$(MonoUtilsFilterSubFolder)\win32</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="Source Files$(MonoUtilsFilterSubFolder)\win32">