proxygen
ThreadName.cpp
Go to the documentation of this file.
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 
18 
19 #include <type_traits>
20 
21 #include <folly/Portability.h>
22 #include <folly/Traits.h>
25 
26 namespace folly {
27 
28 // This looks a bit weird, but it's necessary to avoid
29 // having an undefined compiler function called.
30 #if defined(__GLIBC__) && !defined(__APPLE__) && !defined(__ANDROID__)
31 #if __GLIBC_PREREQ(2, 12)
32 // has pthread_setname_np(pthread_t, const char*) (2 params)
33 #define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 1
34 #endif
35 #endif
36 
37 #if defined(__APPLE__)
38 #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \
39  __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
40 // macOS 10.6+ has pthread_setname_np(const char*) (1 param)
41 #define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 1
42 #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \
43  __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200
44 // iOS 3.2+ has pthread_setname_np(const char*) (1 param)
45 #define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 1
46 #endif
47 #endif // defined(__APPLE__)
48 
49 namespace {
50 
51 #if FOLLY_HAVE_PTHREAD && !_WIN32
52 pthread_t stdTidToPthreadId(std::thread::id tid) {
53  static_assert(
55  "This assumes that the native handle type is pthread_t");
56  static_assert(
57  sizeof(std::thread::native_handle_type) == sizeof(std::thread::id),
58  "This assumes std::thread::id is a thin wrapper around "
59  "std::thread::native_handle_type, but that doesn't appear to be true.");
60  // In most implementations, std::thread::id is a thin wrapper around
61  // std::thread::native_handle_type, which means we can do unsafe things to
62  // extract it.
63  pthread_t id;
64  std::memcpy(&id, &tid, sizeof(id));
65  return id;
66 }
67 #endif
68 
69 } // namespace
70 
72 #if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || \
73  FOLLY_HAS_PTHREAD_SETNAME_NP_NAME || _WIN32
74  return true;
75 #else
76  return false;
77 #endif
78 }
79 
81 #if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || _WIN32
82  return true;
83 #else
84  return false;
85 #endif
86 }
87 
88 static constexpr size_t kMaxThreadNameLength = 16;
89 
90 Optional<std::string> getThreadName(std::thread::id id) {
91 #if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || \
92  FOLLY_HAS_PTHREAD_SETNAME_NP_NAME
93  std::array<char, kMaxThreadNameLength> buf;
94  if (pthread_getname_np(stdTidToPthreadId(id), buf.data(), buf.size()) != 0) {
95  return Optional<std::string>();
96  }
97  return folly::make_optional(std::string(buf.data()));
98 #else
99  // There's not actually a way to get the thread name on Windows because
100  // thread names are a concept managed by the debugger, not the runtime.
101  return Optional<std::string>();
102 #endif
103 }
104 
106  return getThreadName(std::this_thread::get_id());
107 }
108 
109 bool setThreadName(std::thread::id tid, StringPiece name) {
110  auto trimmedName = name.subpiece(0, kMaxThreadNameLength - 1).str();
111 #if _WIN32
112  static_assert(
113  sizeof(unsigned int) == sizeof(std::thread::id),
114  "This assumes std::thread::id is a thin wrapper around "
115  "the thread id as an unsigned int, but that doesn't appear to be true.");
116 
117 // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
118 #pragma pack(push, 8)
119  struct THREADNAME_INFO {
120  DWORD dwType; // Must be 0x1000
121  LPCSTR szName; // Pointer to name (in user address space)
122  DWORD dwThreadID; // Thread ID (-1 for caller thread)
123  DWORD dwFlags; // Reserved for future use; must be zero
124  };
125  union TNIUnion {
126  THREADNAME_INFO tni;
127  ULONG_PTR upArray[4];
128  };
129 #pragma pack(pop)
130 
131  static constexpr DWORD kMSVCException = 0x406D1388;
132 
133  // std::thread::id is a thin wrapper around an integral thread id,
134  // so just extract the ID.
135  unsigned int id;
136  std::memcpy(&id, &tid, sizeof(id));
137 
138  TNIUnion tniUnion = {0x1000, trimmedName.data(), id, 0};
139  // This has to be in a separate stack frame from trimmedName, which requires
140  // C++ object destruction semantics.
141  return [&]() {
142  __try {
143  RaiseException(kMSVCException, 0, 4, tniUnion.upArray);
144  } __except (
145  GetExceptionCode() == kMSVCException ? EXCEPTION_CONTINUE_EXECUTION
146  : EXCEPTION_EXECUTE_HANDLER) {
147  // Swallow the exception when a debugger isn't attached.
148  }
149  return true;
150  }();
151 #else
152  name = name.subpiece(0, kMaxThreadNameLength - 1);
153  char buf[kMaxThreadNameLength] = {};
154  std::memcpy(buf, name.data(), name.size());
155  auto id = stdTidToPthreadId(tid);
156 #if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME
157  return 0 == pthread_setname_np(id, buf);
158 #elif FOLLY_HAS_PTHREAD_SETNAME_NP_NAME
159  // Since macOS 10.6 and iOS 3.2 it is possible for a thread to set its own
160  // name, but not that of some other thread.
161  if (pthread_equal(pthread_self(), id)) {
162  return 0 == pthread_setname_np(buf);
163  }
164  return false;
165 #else
166  (void)id;
167  return false;
168 #endif
169 #endif
170 }
171 
172 bool setThreadName(pthread_t pid, StringPiece name) {
173 #if _WIN32
174  static_assert(
175  sizeof(unsigned int) == sizeof(std::thread::id),
176  "This assumes std::thread::id is a thin wrapper around "
177  "the thread id as an unsigned int, but that doesn't appear to be true.");
178 
179  // std::thread::id is a thin wrapper around an integral thread id,
180  // so just stick the ID in.
181  unsigned int tid = pthread_getw32threadid_np(pid);
182  std::thread::id id;
183  std::memcpy(&id, &tid, sizeof(id));
184  return setThreadName(id, name);
185 #else
186  static_assert(
188  "This assumes that the native handle type is pthread_t");
189  static_assert(
190  sizeof(std::thread::native_handle_type) == sizeof(std::thread::id),
191  "This assumes std::thread::id is a thin wrapper around "
192  "std::thread::native_handle_type, but that doesn't appear to be true.");
193  // In most implementations, std::thread::id is a thin wrapper around
194  // std::thread::native_handle_type, which means we can do unsafe things to
195  // extract it.
196  std::thread::id id;
197  std::memcpy(static_cast<void*>(&id), &pid, sizeof(id));
198  return setThreadName(id, name);
199 #endif
200 }
201 
203  return setThreadName(std::this_thread::get_id(), name);
204 }
205 } // namespace folly
std::string str() const
Definition: Range.h:591
Optional< std::string > getThreadName(std::thread::id id)
Definition: ThreadName.cpp:90
bool canSetOtherThreadName()
Definition: ThreadName.cpp:80
constexpr size_type size() const
Definition: Range.h:431
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
constexpr Optional< _t< std::decay< T > > > make_optional(T &&v)
Definition: Optional.h:450
const char * name
Definition: http_parser.c:437
constexpr Iter data() const
Definition: Range.h:446
Range subpiece(size_type first, size_type length=npos) const
Definition: Range.h:686
bool canSetCurrentThreadName()
Definition: ThreadName.cpp:71
static const char *const value
Definition: Conv.cpp:50
bool setThreadName(std::thread::id tid, StringPiece name)
Definition: ThreadName.cpp:109
const char * string
Definition: Conv.cpp:212
static constexpr size_t kMaxThreadNameLength
Definition: ThreadName.cpp:88
Optional< std::string > getCurrentThreadName()
Definition: ThreadName.cpp:105