/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include // Android only, prctl is only used when pthread_setname_np // and pthread_getname_np are not avilable. #if defined(__linux__) #define FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME 1 #else #define FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME 0 #endif #if FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME #include #endif // This looks a bit weird, but it's necessary to avoid // having an undefined compiler function called. #if defined(__XROS__) #if FOLLY_HAVE_PTHREAD #define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 1 #else #define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 0 #endif #elif defined(__GLIBC__) && !defined(__APPLE__) && !defined(__ANDROID__) #if __GLIBC_PREREQ(2, 12) // has pthread_setname_np(pthread_t, const char*) (2 params) #define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 1 #else #define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 0 #endif // pthread_setname_np was introduced in Android NDK version 9 #elif defined(__ANDROID__) && __ANDROID_API__ >= 9 #define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 1 #else #define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 0 #endif #if defined(__APPLE__) #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 // macOS 10.6+ has pthread_setname_np(const char*) (1 param) #define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 1 #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200 // iOS 3.2+ has pthread_setname_np(const char*) (1 param) #define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 1 #else #define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 0 #endif #else #define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 0 #endif // defined(__APPLE__) namespace folly { namespace { #if FOLLY_HAVE_PTHREAD && !defined(_WIN32) && !defined(__XROS__) pthread_t stdTidToPthreadId(std::thread::id tid) { static_assert( std::is_same::value, "This assumes that the native handle type is pthread_t"); static_assert( sizeof(std::thread::native_handle_type) == sizeof(std::thread::id), "This assumes std::thread::id is a thin wrapper around " "std::thread::native_handle_type, but that doesn't appear to be true."); // In most implementations, std::thread::id is a thin wrapper around // std::thread::native_handle_type, which means we can do unsafe things to // extract it. pthread_t id; std::memcpy(&id, &tid, sizeof(id)); return id; } #endif } // namespace bool canSetCurrentThreadName() { #if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || \ FOLLY_HAS_PTHREAD_SETNAME_NP_NAME || FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME || \ defined(_WIN32) return true; #else return false; #endif } bool canSetOtherThreadName() { #if (FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME && !defined(__XROS__)) || \ defined(_WIN32) return true; #else return false; #endif } static constexpr size_t kMaxThreadNameLength = 16; static Optional getPThreadName(pthread_t pid) { #if ( \ FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || \ FOLLY_HAS_PTHREAD_SETNAME_NP_NAME) && \ !defined(__ANDROID__) // Android NDK does not yet support pthread_getname_np. std::array buf; if (pthread_getname_np(pid, buf.data(), buf.size()) == 0) { return std::string(buf.data()); } #endif (void)pid; return none; } Optional getThreadName(std::thread::id id) { #if ( \ FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || \ FOLLY_HAS_PTHREAD_SETNAME_NP_NAME) && \ !defined(__ANDROID__) && !defined(__XROS__) // Android NDK does not yet support pthread_getname_np. if (id != std::thread::id()) { return getPThreadName(stdTidToPthreadId(id)); } #elif FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME std::array buf; if (id == std::this_thread::get_id() && prctl(PR_GET_NAME, buf.data(), 0L, 0L, 0L) == 0) { return std::string(buf.data()); } #endif (void)id; return none; } // namespace folly Optional getCurrentThreadName() { #if FOLLY_HAVE_PTHREAD return getPThreadName(pthread_self()); #else return getThreadName(std::this_thread::get_id()); #endif } bool setThreadName(std::thread::id tid, StringPiece name) { #ifdef _WIN32 static_assert( sizeof(unsigned int) == sizeof(std::thread::id), "This assumes std::thread::id is a thin wrapper around " "the thread id as an unsigned int, but that doesn't appear to be true."); // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx #pragma pack(push, 8) struct THREADNAME_INFO { DWORD dwType; // Must be 0x1000 LPCSTR szName; // Pointer to name (in user address space) DWORD dwThreadID; // Thread ID (-1 for caller thread) DWORD dwFlags; // Reserved for future use; must be zero }; union TNIUnion { THREADNAME_INFO tni; ULONG_PTR upArray[4]; }; #pragma pack(pop) static constexpr DWORD kMSVCException = 0x406D1388; // std::thread::id is a thin wrapper around an integral thread id, // so just extract the ID. unsigned int id; std::memcpy(&id, &tid, sizeof(id)); auto trimmedName = name.subpiece(0, kMaxThreadNameLength - 1).str(); TNIUnion tniUnion = {0x1000, trimmedName.data(), id, 0}; // This has to be in a separate stack frame from trimmedName, which requires // C++ object destruction semantics. return [&]() { __try { RaiseException(kMSVCException, 0, 4, tniUnion.upArray); } __except ( GetExceptionCode() == kMSVCException ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER) { // Swallow the exception when a debugger isn't attached. } return true; }(); #elif defined(__XROS__) return std::this_thread::get_id() == tid && setThreadName(pthread_self(), name); #else return setThreadName(stdTidToPthreadId(tid), name); #endif } bool setThreadName(pthread_t pid, StringPiece name) { #ifdef _WIN32 static_assert( sizeof(unsigned int) == sizeof(std::thread::id), "This assumes std::thread::id is a thin wrapper around " "the thread id as an unsigned int, but that doesn't appear to be true."); // std::thread::id is a thin wrapper around an integral thread id, // so just stick the ID in. unsigned int tid = pthread_getw32threadid_np(pid); std::thread::id id; std::memcpy(&id, &tid, sizeof(id)); return setThreadName(id, name); #else name = name.subpiece(0, kMaxThreadNameLength - 1); char buf[kMaxThreadNameLength] = {}; std::memcpy(buf, name.data(), name.size()); #if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME return 0 == pthread_setname_np(pid, buf); #elif FOLLY_HAS_PTHREAD_SETNAME_NP_NAME // Since macOS 10.6 and iOS 3.2 it is possible for a thread // to set its own name using pthread, but // not that of some other thread. if (pthread_equal(pthread_self(), pid)) { return 0 == pthread_setname_np(buf); } #elif FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME // for Android prctl is used instead of pthread_setname_np // if Android NDK version is older than API level 9. if (pthread_equal(pthread_self(), pid)) { return 0 == prctl(PR_SET_NAME, buf, 0L, 0L, 0L); } #endif (void)pid; return false; #endif } bool setThreadName(StringPiece name) { #if FOLLY_HAVE_PTHREAD return setThreadName(pthread_self(), name); #else return setThreadName(std::this_thread::get_id(), name); #endif } } // namespace folly