-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Implement SetThreadName in PAL for Linux. #27182
Conversation
This means thread names will now show up in gdb, htop, etc... on Linux. I did not implement this for any other platforms: I did not have anything to test them with, and pthread_setname_np's API varies wildly.
src/pal/src/thread/thread.cpp
Outdated
LOGEXIT("SetThreadNameExit"); | ||
PERF_EXIT(SetThreadName); | ||
|
||
return NO_ERROR != palError ? 0x80004005 : 0; // E_FAIL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure how to correctly get a hold of the HRESULT macros, or if I should even return a bog standard one like E_FAIL here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that we don't use HRESULT anywhere in PAL, so the macros to translate between win32 error (which is the same as PAL error) and HRESULT are not available. We have a couple of options:
- Move the
coreclr/src/pal/inc/rt/palrt.h
Lines 321 to 373 in 921b39c
/******************* HRESULT types ****************************************/ #define FACILITY_WINDOWS 8 #define FACILITY_URT 19 #define FACILITY_UMI 22 #define FACILITY_SXS 23 #define FACILITY_STORAGE 3 #define FACILITY_SSPI 9 #define FACILITY_SCARD 16 #define FACILITY_SETUPAPI 15 #define FACILITY_SECURITY 9 #define FACILITY_RPC 1 #define FACILITY_WIN32 7 #define FACILITY_CONTROL 10 #define FACILITY_NULL 0 #define FACILITY_MSMQ 14 #define FACILITY_MEDIASERVER 13 #define FACILITY_INTERNET 12 #define FACILITY_ITF 4 #define FACILITY_DPLAY 21 #define FACILITY_DISPATCH 2 #define FACILITY_COMPLUS 17 #define FACILITY_CERT 11 #define FACILITY_ACS 20 #define FACILITY_AAF 18 #define NO_ERROR 0L #define SEVERITY_SUCCESS 0 #define SEVERITY_ERROR 1 #define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) #define FAILED(Status) ((HRESULT)(Status)<0) #define IS_ERROR(Status) ((ULONG)(Status) >> 31 == SEVERITY_ERROR) // diff from win32 #define HRESULT_CODE(hr) ((hr) & 0xFFFF) #define SCODE_CODE(sc) ((sc) & 0xFFFF) #define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1fff) #define SCODE_FACILITY(sc) (((sc) >> 16) & 0x1fff) #define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1) #define SCODE_SEVERITY(sc) (((sc) >> 31) & 0x1) // both macros diff from Win32 #define MAKE_HRESULT(sev,fac,code) \ ((HRESULT) (((ULONG)(sev)<<31) | ((ULONG)(fac)<<16) | ((ULONG)(code))) ) #define MAKE_SCODE(sev,fac,code) \ ((SCODE) (((ULONG)(sev)<<31) | ((ULONG)(fac)<<16) | ((LONG)(code))) ) #define FACILITY_NT_BIT 0x10000000 #define HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000))) #define __HRESULT_FROM_WIN32(x) HRESULT_FROM_WIN32(x) #define HRESULT_FROM_NT(x) ((HRESULT) ((x) | FACILITY_NT_BIT)) HRESULT_FROM_WIN32(palError)
here. - Change the function to return PAL_ERROR instead of HRESULT and do the translation to HRESULT in the wrapper function, I've mentioned in my previous comment. In that case, we should rename the function to have a
PAL_
prefix to indicate it is not a 1:1 implementation of the Windows API.
I would prefer option 1
src/pal/inc/pal.h
Outdated
PALIMPORT | ||
HRESULT | ||
PALAPI | ||
SetThreadName( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The actual Windows API is named SetThreadDescription and the SetThreadName is just a wrapper to this function defined in src/utilcode/winfix.cpp. The PAL either implements Windows APIs 1:1 or use PAL_
prefix in for names of functions that are not Windows APIs.
Can you please rename this function to SetThreadDescription and add a wrapper function named SetThreadName that just calls this function? You can put it at the end of the winfix.cpp file (add #else // !FEATURE_PAL
before the #endif
and put the wrapper there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the result is ignored by the calling code anyway. Since this is just a diagnostic aid API, it may be best to just make a PAL_
function that returns void.
src/pal/src/thread/thread.cpp
Outdated
pTargetThread->Lock(pThread); | ||
|
||
/* translate the wide char lpPathName string to multibyte string */ | ||
if(0 == (nameSize = WideCharToMultiByte( CP_ACP, 0, lpThreadDescription, -1, NULL, 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer moving the nameSize
assignment before the if
. The same for the nameBuf
below. It makes it easier to read and step through the code during debugging.
src/pal/src/thread/thread.cpp
Outdated
LOGEXIT("SetThreadNameExit"); | ||
PERF_EXIT(SetThreadName); | ||
|
||
return NO_ERROR != palError ? 0x80004005 : 0; // E_FAIL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that we don't use HRESULT anywhere in PAL, so the macros to translate between win32 error (which is the same as PAL error) and HRESULT are not available. We have a couple of options:
- Move the
coreclr/src/pal/inc/rt/palrt.h
Lines 321 to 373 in 921b39c
/******************* HRESULT types ****************************************/ #define FACILITY_WINDOWS 8 #define FACILITY_URT 19 #define FACILITY_UMI 22 #define FACILITY_SXS 23 #define FACILITY_STORAGE 3 #define FACILITY_SSPI 9 #define FACILITY_SCARD 16 #define FACILITY_SETUPAPI 15 #define FACILITY_SECURITY 9 #define FACILITY_RPC 1 #define FACILITY_WIN32 7 #define FACILITY_CONTROL 10 #define FACILITY_NULL 0 #define FACILITY_MSMQ 14 #define FACILITY_MEDIASERVER 13 #define FACILITY_INTERNET 12 #define FACILITY_ITF 4 #define FACILITY_DPLAY 21 #define FACILITY_DISPATCH 2 #define FACILITY_COMPLUS 17 #define FACILITY_CERT 11 #define FACILITY_ACS 20 #define FACILITY_AAF 18 #define NO_ERROR 0L #define SEVERITY_SUCCESS 0 #define SEVERITY_ERROR 1 #define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) #define FAILED(Status) ((HRESULT)(Status)<0) #define IS_ERROR(Status) ((ULONG)(Status) >> 31 == SEVERITY_ERROR) // diff from win32 #define HRESULT_CODE(hr) ((hr) & 0xFFFF) #define SCODE_CODE(sc) ((sc) & 0xFFFF) #define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1fff) #define SCODE_FACILITY(sc) (((sc) >> 16) & 0x1fff) #define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1) #define SCODE_SEVERITY(sc) (((sc) >> 31) & 0x1) // both macros diff from Win32 #define MAKE_HRESULT(sev,fac,code) \ ((HRESULT) (((ULONG)(sev)<<31) | ((ULONG)(fac)<<16) | ((ULONG)(code))) ) #define MAKE_SCODE(sev,fac,code) \ ((SCODE) (((ULONG)(sev)<<31) | ((ULONG)(fac)<<16) | ((LONG)(code))) ) #define FACILITY_NT_BIT 0x10000000 #define HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000))) #define __HRESULT_FROM_WIN32(x) HRESULT_FROM_WIN32(x) #define HRESULT_FROM_NT(x) ((HRESULT) ((x) | FACILITY_NT_BIT)) HRESULT_FROM_WIN32(palError)
here. - Change the function to return PAL_ERROR instead of HRESULT and do the translation to HRESULT in the wrapper function, I've mentioned in my previous comment. In that case, we should rename the function to have a
PAL_
prefix to indicate it is not a 1:1 implementation of the Windows API.
I would prefer option 1
} | ||
|
||
// Null terminate early. | ||
// pthread_setname_np only accepts up to 16 chars. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do names of all threads that the runtime look reasonable when they are cutoff to 15 characters?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This can be tweaked in follow up change.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Threadpool and finalizer threads at least are recognizable from my testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thank you!
Build breaks on Mac: Undefined symbols for architecture x86_64: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please take a look at the Mac build breaks?
// pthread_setname_np only accepts up to 16 chars. | ||
nameBuf[15] = '\0'; | ||
|
||
error = pthread_setname_np(pTargetThread->GetPThreadSelf(), nameBuf); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On FreeBSD, this is pthread_set_name_np()
(and requires <pthread_np.h>
). On macOS, this is pthread_setname_np()
, only the thread name is passed as an argument (setting the name for the calling thread); it also takes a char*
rather than a const char*
, so I often copy it on the stack with strdupa()
when calling it.
No need to implement all these flavors in this PR, but these are all the differences as far as I can tell, so the #ifdef __linux__
block could encase just this pthread call for all the supported platforms. (Maybe with a #else
returning some "not supported" PAL_ERROR
.) Maybe as a followup patch?
src/pal/src/thread/thread.cpp
Outdated
if (error != 0) | ||
{ | ||
palError = ERROR_INTERNAL_ERROR; | ||
goto InternalSetThreadDescriptionExit; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This goto can be removed (it jumps to the next line).
|
||
InternalSetThreadDescriptionExit: | ||
|
||
if (NULL != pTargetThread) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pTargetThread won't be NULL here (if it is, then the call to lock will blow up).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it possible that InternalGetThreadDataFromHandle
up above will fail and then keep the pTargetThread
pointer NULL? If that were to happen this check would be necessary.
@PJB3005 can you please take a look at the Mac build breaks, as @jkotas has mentioned about 10 days ago? Since we will be migrating to a new repo that will share coreclr, corefx and core-setup stuff, we have about a week large window for merging this change here, otherwise it would have to be ported to the new repo. |
Oh jeez it's already been 10 days. I don't really have a mac that I can test this on, and I can't figure out anything just looking at the build logs. |
Thank you for your contribution. As announced in dotnet/coreclr#27549 this repository will be moving to dotnet/runtime on November 13. If you would like to continue working on this PR after this date, the easiest way to move the change to dotnet/runtime is:
|
Ok, let me try it on my Mac |
@PJB3005 I've found the OSX build break issue. The SetThreadDescription needs to be added after the following line, prefixed with
The libmscordbi.dylib depends on libmscordac.dylib exporting some of the PAL symbols so that it can use the PAL linked into the libmscordac.dylib. |
That seems to have worked, thanks! |
… Linux Fixes dotnet/runtime#33673 This issue is a side-effect of adding support for setting thread names on Linux in dotnet/coreclr#27182. cc @janvorli @lpereira @SteveL-MSFT
… Linux (#34064) Fixes #33673 This issue is a side-effect of adding support for setting thread names on Linux in dotnet/coreclr#27182. Co-authored-by: Dan Moseley <[email protected]> Co-authored-by: Aleksey Kliger (λgeek) <[email protected]>
This means thread names will now show up in gdb, htop, etc... on Linux.
I did not implement this for any other platforms:
I did not have anything to test them with, and
pthread_setname_np
's API varies wildly across platform. (Hell, on macOS you can't call it from any thread except the thread in question..)I'm not very familiar with C/C++ and this is my first time contributing, so extra scrutiny would be very much appreciated.