ThreadName.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*
  2. * Copyright 2017-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <folly/system/ThreadName.h>
  17. #include <type_traits>
  18. #include <folly/Portability.h>
  19. #include <folly/Traits.h>
  20. #include <folly/portability/PThread.h>
  21. #include <folly/portability/Windows.h>
  22. namespace folly {
  23. // This looks a bit weird, but it's necessary to avoid
  24. // having an undefined compiler function called.
  25. #if defined(__GLIBC__) && !defined(__APPLE__) && !defined(__ANDROID__)
  26. #if __GLIBC_PREREQ(2, 12)
  27. // has pthread_setname_np(pthread_t, const char*) (2 params)
  28. #define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 1
  29. #endif
  30. #endif
  31. #if defined(__APPLE__)
  32. #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \
  33. __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
  34. // macOS 10.6+ has pthread_setname_np(const char*) (1 param)
  35. #define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 1
  36. #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \
  37. __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200
  38. // iOS 3.2+ has pthread_setname_np(const char*) (1 param)
  39. #define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 1
  40. #endif
  41. #endif // defined(__APPLE__)
  42. namespace {
  43. #if FOLLY_HAVE_PTHREAD && !_WIN32
  44. pthread_t stdTidToPthreadId(std::thread::id tid) {
  45. static_assert(
  46. std::is_same<pthread_t, std::thread::native_handle_type>::value,
  47. "This assumes that the native handle type is pthread_t");
  48. static_assert(
  49. sizeof(std::thread::native_handle_type) == sizeof(std::thread::id),
  50. "This assumes std::thread::id is a thin wrapper around "
  51. "std::thread::native_handle_type, but that doesn't appear to be true.");
  52. // In most implementations, std::thread::id is a thin wrapper around
  53. // std::thread::native_handle_type, which means we can do unsafe things to
  54. // extract it.
  55. pthread_t id;
  56. std::memcpy(&id, &tid, sizeof(id));
  57. return id;
  58. }
  59. #endif
  60. } // namespace
  61. bool canSetCurrentThreadName() {
  62. #if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || \
  63. FOLLY_HAS_PTHREAD_SETNAME_NP_NAME || _WIN32
  64. return true;
  65. #else
  66. return false;
  67. #endif
  68. }
  69. bool canSetOtherThreadName() {
  70. #if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || _WIN32
  71. return true;
  72. #else
  73. return false;
  74. #endif
  75. }
  76. static constexpr size_t kMaxThreadNameLength = 16;
  77. Optional<std::string> getThreadName(std::thread::id id) {
  78. #if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || \
  79. FOLLY_HAS_PTHREAD_SETNAME_NP_NAME
  80. std::array<char, kMaxThreadNameLength> buf;
  81. if (pthread_getname_np(stdTidToPthreadId(id), buf.data(), buf.size()) != 0) {
  82. return Optional<std::string>();
  83. }
  84. return folly::make_optional(std::string(buf.data()));
  85. #else
  86. // There's not actually a way to get the thread name on Windows because
  87. // thread names are a concept managed by the debugger, not the runtime.
  88. return Optional<std::string>();
  89. #endif
  90. }
  91. Optional<std::string> getCurrentThreadName() {
  92. return getThreadName(std::this_thread::get_id());
  93. }
  94. bool setThreadName(std::thread::id tid, StringPiece name) {
  95. auto trimmedName = name.subpiece(0, kMaxThreadNameLength - 1).str();
  96. #if _WIN32
  97. static_assert(
  98. sizeof(unsigned int) == sizeof(std::thread::id),
  99. "This assumes std::thread::id is a thin wrapper around "
  100. "the thread id as an unsigned int, but that doesn't appear to be true.");
  101. // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
  102. #pragma pack(push, 8)
  103. struct THREADNAME_INFO {
  104. DWORD dwType; // Must be 0x1000
  105. LPCSTR szName; // Pointer to name (in user address space)
  106. DWORD dwThreadID; // Thread ID (-1 for caller thread)
  107. DWORD dwFlags; // Reserved for future use; must be zero
  108. };
  109. union TNIUnion {
  110. THREADNAME_INFO tni;
  111. ULONG_PTR upArray[4];
  112. };
  113. #pragma pack(pop)
  114. static constexpr DWORD kMSVCException = 0x406D1388;
  115. // std::thread::id is a thin wrapper around an integral thread id,
  116. // so just extract the ID.
  117. unsigned int id;
  118. std::memcpy(&id, &tid, sizeof(id));
  119. TNIUnion tniUnion = {0x1000, trimmedName.data(), id, 0};
  120. // This has to be in a separate stack frame from trimmedName, which requires
  121. // C++ object destruction semantics.
  122. return [&]() {
  123. __try {
  124. RaiseException(kMSVCException, 0, 4, tniUnion.upArray);
  125. } __except (
  126. GetExceptionCode() == kMSVCException ? EXCEPTION_CONTINUE_EXECUTION
  127. : EXCEPTION_EXECUTE_HANDLER) {
  128. // Swallow the exception when a debugger isn't attached.
  129. }
  130. return true;
  131. }();
  132. #else
  133. name = name.subpiece(0, kMaxThreadNameLength - 1);
  134. char buf[kMaxThreadNameLength] = {};
  135. std::memcpy(buf, name.data(), name.size());
  136. auto id = stdTidToPthreadId(tid);
  137. #if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME
  138. return 0 == pthread_setname_np(id, buf);
  139. #elif FOLLY_HAS_PTHREAD_SETNAME_NP_NAME
  140. // Since macOS 10.6 and iOS 3.2 it is possible for a thread to set its own
  141. // name, but not that of some other thread.
  142. if (pthread_equal(pthread_self(), id)) {
  143. return 0 == pthread_setname_np(buf);
  144. }
  145. return false;
  146. #else
  147. (void)id;
  148. return false;
  149. #endif
  150. #endif
  151. }
  152. bool setThreadName(pthread_t pid, StringPiece name) {
  153. #if _WIN32
  154. static_assert(
  155. sizeof(unsigned int) == sizeof(std::thread::id),
  156. "This assumes std::thread::id is a thin wrapper around "
  157. "the thread id as an unsigned int, but that doesn't appear to be true.");
  158. // std::thread::id is a thin wrapper around an integral thread id,
  159. // so just stick the ID in.
  160. unsigned int tid = pthread_getw32threadid_np(pid);
  161. std::thread::id id;
  162. std::memcpy(&id, &tid, sizeof(id));
  163. return setThreadName(id, name);
  164. #else
  165. static_assert(
  166. std::is_same<pthread_t, std::thread::native_handle_type>::value,
  167. "This assumes that the native handle type is pthread_t");
  168. static_assert(
  169. sizeof(std::thread::native_handle_type) == sizeof(std::thread::id),
  170. "This assumes std::thread::id is a thin wrapper around "
  171. "std::thread::native_handle_type, but that doesn't appear to be true.");
  172. // In most implementations, std::thread::id is a thin wrapper around
  173. // std::thread::native_handle_type, which means we can do unsafe things to
  174. // extract it.
  175. std::thread::id id;
  176. std::memcpy(static_cast<void*>(&id), &pid, sizeof(id));
  177. return setThreadName(id, name);
  178. #endif
  179. }
  180. bool setThreadName(StringPiece name) {
  181. return setThreadName(std::this_thread::get_id(), name);
  182. }
  183. } // namespace folly