#include <folly/system/ThreadName.h>
#include <type_traits>
#include <folly/Portability.h>
#include <folly/ScopeGuard.h>
#include <folly/Traits.h>
#include <folly/portability/PThread.h>
#include <folly/portability/Windows.h>
#ifdef _WIN32
#include <strsafe.h>
#endif
#if defined(__linux__)
#define FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME …
#else
#define FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME …
#endif
#if FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME
#include <sys/prctl.h>
#endif
#if defined(__GLIBC__) && !defined(__APPLE__) && !defined(__ANDROID__)
#define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME …
#elif defined(__ANDROID__) && __ANDROID_API__ >= 9
#define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME …
#else
#define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME …
#endif
#if defined(__APPLE__)
#define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME …
#else
#define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME …
#endif
namespace folly {
namespace {
#if FOLLY_HAVE_PTHREAD && !defined(_WIN32)
pthread_t stdTidToPthreadId(std::thread::id tid) { … }
#endif
}
bool canSetCurrentThreadName() { … }
bool canSetOtherThreadName() { … }
static constexpr size_t kMaxThreadNameLength = …;
static Optional<std::string> getPThreadName(pthread_t pid) { … }
Optional<std::string> getThreadName(std::thread::id id) { … }
Optional<std::string> getCurrentThreadName() { … }
namespace {
#ifdef _WIN32
typedef HRESULT(__stdcall* SetThreadDescriptionFn)(HANDLE, PCWSTR);
SetThreadDescriptionFn getSetThreadDescription() {
auto proc = GetProcAddress(
GetModuleHandleW(L"kernelbase.dll"), "SetThreadDescription");
return reinterpret_cast<SetThreadDescriptionFn>(proc);
}
bool setThreadNameWindowsViaDescription(DWORD id, StringPiece name) noexcept {
static const auto setThreadDescription = getSetThreadDescription();
if (!setThreadDescription) {
return false;
}
HANDLE thread = id == GetCurrentThreadId()
? GetCurrentThread()
: OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, id);
if (!thread) {
return false;
}
SCOPE_EXIT {
if (id != GetCurrentThreadId()) {
CloseHandle(thread);
}
};
constexpr size_t kMaximumThreadNameLength = 64;
if (name.size() > kMaximumThreadNameLength) {
name = name.subpiece(0, kMaximumThreadNameLength);
}
WCHAR wname[kMaximumThreadNameLength + 1];
int written = MultiByteToWideChar(
CP_UTF8, 0, name.data(), name.size(), wname, kMaximumThreadNameLength);
if (written <= 0 || static_cast<size_t>(written) >= std::size(wname)) {
return false;
}
wname[written] = 0;
if (FAILED(setThreadDescription(thread, wname))) {
return false;
}
return true;
}
bool setThreadNameWindowsViaDebugger(DWORD id, StringPiece name) noexcept {
#pragma pack(push, 8)
struct THREADNAME_INFO {
DWORD dwType;
LPCSTR szName;
DWORD dwThreadID;
DWORD dwFlags;
};
union TNIUnion {
THREADNAME_INFO tni;
ULONG_PTR upArray[4];
};
#pragma pack(pop)
static constexpr DWORD kMSVCException = 0x406D1388;
char trimmed[kMaxThreadNameLength];
if (STRSAFE_E_INVALID_PARAMETER ==
StringCchCopyNA(trimmed, sizeof(trimmed), name.data(), name.size())) {
return false;
}
TNIUnion tniUnion = {0x1000, trimmed, id, 0};
__try {
RaiseException(kMSVCException, 0, 4, tniUnion.upArray);
} __except (
GetExceptionCode() == kMSVCException ? EXCEPTION_CONTINUE_EXECUTION
: EXCEPTION_EXECUTE_HANDLER) {
}
return true;
}
bool setThreadNameWindows(std::thread::id tid, StringPiece name) {
static_assert(
sizeof(DWORD) == sizeof(std::thread::id),
"This assumes std::thread::id is a thin wrapper around "
"the Win32 thread id, but that doesn't appear to be true.");
DWORD id;
std::memcpy(&id, &tid, sizeof(id));
if (setThreadNameWindowsViaDescription(id, name)) {
return true;
}
return setThreadNameWindowsViaDebugger(id, name);
}
#endif
}
bool setThreadName(std::thread::id tid, StringPiece name) { … }
bool setThreadName(pthread_t pid, StringPiece name) { … }
bool setThreadName(StringPiece name) { … }
}