Added SetThreadDescription to set the unmanaged thread name (#12593)
authorAlois-xx <akraus1@gmx.de>
Mon, 14 Aug 2017 17:26:28 +0000 (19:26 +0200)
committerJan Kotas <jkotas@microsoft.com>
Mon, 14 Aug 2017 17:26:28 +0000 (10:26 -0700)
* Added SetThreadDescription to set the unmanaged thread name as well when a managed thread name was set.
This will show up in future debuggers which know how to read that information or in ETW traces in the Thread Name column.

* use printf  instead of wprintf which exists on all platforms.

* Removed printf
Ensure that GetProceAddress is only called once to when the method is not present.
Potential perf hit should be negligible since setting a thread name can only happen once per managed thread.

* - Moved SetThreadName code to winfix.cpp as proposed
- Finalizer and threadpool threads get their name
- GCToEEInterface::CreateBackgroundThread is also named
- but regular GC threads have no name because when I included utilcode.h things did break apart.

* Fix for data race in g_pfnSetThreadDescription

* Fix string literals on unix builds.

* Fixed nits
Settled thread name on ".NET Core ThreadPool"

src/inc/utilcode.h
src/utilcode/winfix.cpp
src/vm/comsynchronizable.cpp
src/vm/finalizerthread.cpp
src/vm/gcenv.ee.cpp
src/vm/threads.cpp
src/vm/threads.h
src/vm/win32threadpool.cpp

index db8465a..f48af70 100644 (file)
@@ -4687,6 +4687,8 @@ inline void ClrFlsClearThreadType (TlsThreadTypeFlag flag)
 #define CLEAR_THREAD_TYPE_STACKWALKER() ClrFlsSetValue(TlsIdx_StackWalkerWalkingThread, NULL)
 #endif  // DACCESS_COMPILE
 
+HRESULT SetThreadName(HANDLE hThread, PCWSTR lpThreadDescription);
+
 inline BOOL IsStackWalkerThread()
 {
     STATIC_CONTRACT_NOTHROW;
index 3a04486..c914fb6 100644 (file)
@@ -426,6 +426,45 @@ lExit:
     
 }
 
+typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
+extern pfnSetThreadDescription g_pfnSetThreadDescription;
+
+// Dummy method if windows version does not support it
+HRESULT SetThreadDescriptionDummy(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+    return NOERROR;
+}
+
+HRESULT WINAPI InitializeSetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+    HMODULE hKernel32 = WszLoadLibrary(W("kernel32.dll"));
+
+    pfnSetThreadDescription pLocal = NULL; 
+    if (hKernel32 != NULL)
+    {
+        // store to thread local variable to prevent data race
+        pLocal = (pfnSetThreadDescription)GetProcAddress(hKernel32, "SetThreadDescription");
+    }
+
+    if (pLocal == NULL) // method is only available with Windows 10 Creators Update or later
+    {
+        g_pfnSetThreadDescription = SetThreadDescriptionDummy;
+    }
+    else
+    {
+        g_pfnSetThreadDescription = pLocal;
+    }
+
+    return g_pfnSetThreadDescription(hThread, lpThreadDescription);
+}
+
+pfnSetThreadDescription g_pfnSetThreadDescription = &InitializeSetThreadDescription;
+
+// Set unmanaged thread name which will show up in ETW and Debuggers which know how to read this data.
+HRESULT SetThreadName(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+    return g_pfnSetThreadDescription(hThread, lpThreadDescription);
+}
 
 DWORD
 WszGetWorkingSet()
index 3425e2d..0554fe3 100644 (file)
 #include "appdomain.hpp"
 #include "appdomain.inl"
 
+#ifndef FEATURE_PAL
+#include "utilcode.h"
+#endif
+
 #include "newapis.h"
 
 // To include definition of CAPTURE_BUCKETS_AT_TRANSITION
@@ -1542,9 +1546,18 @@ void QCALLTYPE ThreadNative::InformThreadNameChange(QCall::ThreadHandle thread,
     QCALL_CONTRACT;
 
     BEGIN_QCALL;
-
+    
     Thread* pThread = &(*thread);
 
+#ifndef FEATURE_PAL
+    // Set on Windows 10 Creators Update and later machines the unmanaged thread name as well. That will show up in ETW traces and debuggers which is very helpful
+    // if more and more threads get a meaningful name
+    if (len > 0 && name != NULL)
+    {
+        SetThreadName(pThread->GetThreadHandle(), name);
+    }
+#endif
+
 #ifdef PROFILING_SUPPORTED
     {
         BEGIN_PIN_PROFILER(CORProfilerTrackThreads());
index 0a4da16..3ba3468 100644 (file)
@@ -909,7 +909,7 @@ void FinalizerThread::FinalizerThreadCreate()
     // actual thread terminates.
     GetFinalizerThread()->IncExternalCount();
 
-    if (GetFinalizerThread()->CreateNewThread(0, &FinalizerThreadStart, NULL))
+    if (GetFinalizerThread()->CreateNewThread(0, &FinalizerThreadStart, NULL, W("Finalizer")) )
     {
         DWORD dwRet = GetFinalizerThread()->StartThread();
 
index 7ab22db..b61069f 100644 (file)
@@ -445,7 +445,7 @@ Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threa
         return NULL;
     }
 
-    if (threadStubArgs.thread->CreateNewThread(0, (LPTHREAD_START_ROUTINE)BackgroundThreadStub, &threadStubArgs))
+    if (threadStubArgs.thread->CreateNewThread(0, (LPTHREAD_START_ROUTINE)BackgroundThreadStub, &threadStubArgs, W("Background GC")))
     {
         threadStubArgs.thread->SetBackground (TRUE, FALSE);
         threadStubArgs.thread->StartThread();
index a28164f..b827140 100644 (file)
@@ -2524,7 +2524,7 @@ void UndoRevert(BOOL bReverted, HANDLE hToken)
 // We don't want ::CreateThread() calls scattered throughout the source.  So gather
 // them all here.
 
-BOOL Thread::CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, void *args)
+BOOL Thread::CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, void *args, LPCWSTR pName)
 {
     CONTRACTL {
         NOTHROW;
@@ -2551,6 +2551,7 @@ BOOL Thread::CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, voi
     bRet = CreateNewOSThread(stackSize, start, args);
 #ifndef FEATURE_PAL
     UndoRevert(bReverted, token);
+    SetThreadName(m_ThreadHandle, pName);
 #endif // !FEATURE_PAL
 
     return bRet;
index 9824ef1..ad433e7 100644 (file)
@@ -1946,7 +1946,7 @@ public:
     // Create all new threads here.  The thread is created as suspended, so
     // you must ::ResumeThread to kick it off.  It is guaranteed to create the
     // thread, or throw.
-    BOOL CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, void *args);
+    BOOL CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, void *args, LPCWSTR pName=NULL);
 
 
     enum StackSizeBucket
index e2887c5..eabbcb9 100644 (file)
@@ -1815,8 +1815,8 @@ Thread* ThreadpoolMgr::CreateUnimpersonatedThread(LPTHREAD_START_ROUTINE lpStart
         // CreateNewThread takes care of reverting any impersonation - so dont do anything here.
         bOK = pThread->CreateNewThread(0,               // default stack size
                                        lpStartAddress,
-                                       lpArgs           //arguments
-                                       );
+                                       lpArgs,           //arguments
+                                       W(".NET Core ThreadPool"));
     }
     else {
 #ifndef FEATURE_PAL