Fix recordStack feature in FiberManager
[folly.git] / folly / fibers / FiberManager.cpp
1 /*
2  * Copyright 2016 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 "FiberManagerInternal.h"
17
18 #include <signal.h>
19
20 #include <cassert>
21 #include <stdexcept>
22
23 #include <glog/logging.h>
24
25 #include <folly/fibers/Fiber.h>
26 #include <folly/fibers/LoopController.h>
27
28 #include <folly/SingletonThreadLocal.h>
29 #include <folly/portability/SysSyscall.h>
30 #include <folly/portability/Unistd.h>
31
32 #ifdef FOLLY_SANITIZE_ADDRESS
33
34 #include <dlfcn.h>
35
36 static void __asan_enter_fiber_weak(
37     void const* fiber_stack_base,
38     size_t fiber_stack_extent)
39     __attribute__((__weakref__("__asan_enter_fiber")));
40 static void __asan_exit_fiber_weak()
41     __attribute__((__weakref__("__asan_exit_fiber")));
42 static void __asan_unpoison_memory_region_weak(
43     void const /* nolint */ volatile* addr,
44     size_t size) __attribute__((__weakref__("__asan_unpoison_memory_region")));
45
46 typedef void (*AsanEnterFiberFuncPtr)(void const*, size_t);
47 typedef void (*AsanExitFiberFuncPtr)();
48 typedef void (*AsanUnpoisonMemoryRegionFuncPtr)(
49     void const /* nolint */ volatile*,
50     size_t);
51
52 namespace folly {
53 namespace fibers {
54
55 static AsanEnterFiberFuncPtr getEnterFiberFunc();
56 static AsanExitFiberFuncPtr getExitFiberFunc();
57 static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc();
58 }
59 }
60
61 #endif
62
63 namespace folly {
64 namespace fibers {
65
66 FOLLY_TLS FiberManager* FiberManager::currentFiberManager_ = nullptr;
67
68 FiberManager::FiberManager(
69     std::unique_ptr<LoopController> loopController,
70     Options options)
71     : FiberManager(
72           LocalType<void>(),
73           std::move(loopController),
74           std::move(options)) {}
75
76 FiberManager::~FiberManager() {
77   if (isLoopScheduled_) {
78     loopController_->cancel();
79   }
80
81   while (!fibersPool_.empty()) {
82     fibersPool_.pop_front_and_dispose([](Fiber* fiber) { delete fiber; });
83   }
84   assert(readyFibers_.empty());
85   assert(fibersActive_ == 0);
86 }
87
88 LoopController& FiberManager::loopController() {
89   return *loopController_;
90 }
91
92 const LoopController& FiberManager::loopController() const {
93   return *loopController_;
94 }
95
96 bool FiberManager::hasTasks() const {
97   return fibersActive_ > 0 || !remoteReadyQueue_.empty() ||
98       !remoteTaskQueue_.empty();
99 }
100
101 Fiber* FiberManager::getFiber() {
102   Fiber* fiber = nullptr;
103
104   if (options_.fibersPoolResizePeriodMs > 0 && !fibersPoolResizerScheduled_) {
105     fibersPoolResizer_();
106     fibersPoolResizerScheduled_ = true;
107   }
108
109   if (fibersPool_.empty()) {
110     fiber = new Fiber(*this);
111     ++fibersAllocated_;
112   } else {
113     fiber = &fibersPool_.front();
114     fibersPool_.pop_front();
115     assert(fibersPoolSize_ > 0);
116     --fibersPoolSize_;
117   }
118   assert(fiber);
119   if (++fibersActive_ > maxFibersActiveLastPeriod_) {
120     maxFibersActiveLastPeriod_ = fibersActive_;
121   }
122   ++fiberId_;
123   bool recordStack = (options_.recordStackEvery != 0) &&
124       (fiberId_ % options_.recordStackEvery == 0);
125   fiber->init(recordStack);
126   return fiber;
127 }
128
129 void FiberManager::setExceptionCallback(FiberManager::ExceptionCallback ec) {
130   assert(ec);
131   exceptionCallback_ = std::move(ec);
132 }
133
134 size_t FiberManager::fibersAllocated() const {
135   return fibersAllocated_;
136 }
137
138 size_t FiberManager::fibersPoolSize() const {
139   return fibersPoolSize_;
140 }
141
142 size_t FiberManager::stackHighWatermark() const {
143   return stackHighWatermark_;
144 }
145
146 void FiberManager::remoteReadyInsert(Fiber* fiber) {
147   if (observer_) {
148     observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
149   }
150   auto insertHead = [&]() { return remoteReadyQueue_.insertHead(fiber); };
151   loopController_->scheduleThreadSafe(std::ref(insertHead));
152 }
153
154 void FiberManager::setObserver(ExecutionObserver* observer) {
155   observer_ = observer;
156 }
157
158 ExecutionObserver* FiberManager::getObserver() {
159   return observer_;
160 }
161
162 void FiberManager::setPreemptRunner(InlineFunctionRunner* preemptRunner) {
163   preemptRunner_ = preemptRunner;
164 }
165
166 void FiberManager::doFibersPoolResizing() {
167   while (fibersAllocated_ > maxFibersActiveLastPeriod_ &&
168          fibersPoolSize_ > options_.maxFibersPoolSize) {
169     auto fiber = &fibersPool_.front();
170     assert(fiber != nullptr);
171     fibersPool_.pop_front();
172     delete fiber;
173     --fibersPoolSize_;
174     --fibersAllocated_;
175   }
176
177   maxFibersActiveLastPeriod_ = fibersActive_;
178 }
179
180 void FiberManager::FibersPoolResizer::operator()() {
181   fiberManager_.doFibersPoolResizing();
182   fiberManager_.timeoutManager_->registerTimeout(
183       *this,
184       std::chrono::milliseconds(
185           fiberManager_.options_.fibersPoolResizePeriodMs));
186 }
187
188 #ifdef FOLLY_SANITIZE_ADDRESS
189
190 void FiberManager::registerFiberActivationWithAsan(Fiber* fiber) {
191   auto context = &fiber->fcontext_;
192   void* top = context->stackBase();
193   void* bottom = context->stackLimit();
194   size_t extent = static_cast<char*>(top) - static_cast<char*>(bottom);
195
196   // Check if we can find a fiber enter function and call it if we find one
197   static AsanEnterFiberFuncPtr fn = getEnterFiberFunc();
198   if (fn == nullptr) {
199     LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
200   } else {
201     fn(bottom, extent);
202   }
203 }
204
205 void FiberManager::registerFiberDeactivationWithAsan(Fiber* fiber) {
206   (void)fiber; // currently unused
207
208   // Check if we can find a fiber exit function and call it if we find one
209   static AsanExitFiberFuncPtr fn = getExitFiberFunc();
210   if (fn == nullptr) {
211     LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
212   } else {
213     fn();
214   }
215 }
216
217 void FiberManager::unpoisonFiberStack(const Fiber* fiber) {
218   auto stack = fiber->getStack();
219
220   // Check if we can find a fiber enter function and call it if we find one
221   static AsanUnpoisonMemoryRegionFuncPtr fn = getUnpoisonMemoryRegionFunc();
222   if (fn == nullptr) {
223     LOG(FATAL) << "This version of ASAN doesn't support memory unpoisoning";
224   } else {
225     fn(stack.first, stack.second);
226   }
227 }
228
229 static AsanEnterFiberFuncPtr getEnterFiberFunc() {
230   AsanEnterFiberFuncPtr fn{nullptr};
231
232   // Check whether weak reference points to statically linked enter function
233   if (nullptr != (fn = &::__asan_enter_fiber_weak)) {
234     return fn;
235   }
236
237   // Check whether we can find a dynamically linked enter function
238   if (nullptr !=
239       (fn = (AsanEnterFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_enter_fiber"))) {
240     return fn;
241   }
242
243   // Couldn't find the function at all
244   return nullptr;
245 }
246
247 static AsanExitFiberFuncPtr getExitFiberFunc() {
248   AsanExitFiberFuncPtr fn{nullptr};
249
250   // Check whether weak reference points to statically linked exit function
251   if (nullptr != (fn = &::__asan_exit_fiber_weak)) {
252     return fn;
253   }
254
255   // Check whether we can find a dynamically linked exit function
256   if (nullptr !=
257       (fn = (AsanExitFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_exit_fiber"))) {
258     return fn;
259   }
260
261   // Couldn't find the function at all
262   return nullptr;
263 }
264
265 static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() {
266   AsanUnpoisonMemoryRegionFuncPtr fn{nullptr};
267
268   // Check whether weak reference points to statically linked unpoison function
269   if (nullptr != (fn = &::__asan_unpoison_memory_region_weak)) {
270     return fn;
271   }
272
273   // Check whether we can find a dynamically linked unpoison function
274   if (nullptr != (fn = (AsanUnpoisonMemoryRegionFuncPtr)dlsym(
275                       RTLD_DEFAULT, "__asan_unpoison_memory_region"))) {
276     return fn;
277   }
278
279   // Couldn't find the function at all
280   return nullptr;
281 }
282
283 #endif // FOLLY_SANITIZE_ADDRESS
284
285 #ifndef _WIN32
286 namespace {
287
288 // SIGSTKSZ (8 kB on our architectures) isn't always enough for
289 // folly::symbolizer, so allocate 32 kB.
290 constexpr size_t kAltStackSize = folly::constexpr_max(SIGSTKSZ, 32 * 1024);
291
292 bool hasAlternateStack() {
293   stack_t ss;
294   sigaltstack(nullptr, &ss);
295   return !(ss.ss_flags & SS_DISABLE);
296 }
297
298 int setAlternateStack(char* sp, size_t size) {
299   CHECK(sp);
300   stack_t ss{};
301   ss.ss_sp = sp;
302   ss.ss_size = size;
303   return sigaltstack(&ss, nullptr);
304 }
305
306 int unsetAlternateStack() {
307   stack_t ss{};
308   ss.ss_flags = SS_DISABLE;
309   return sigaltstack(&ss, nullptr);
310 }
311
312 class ScopedAlternateSignalStack {
313  public:
314   ScopedAlternateSignalStack() {
315     if (hasAlternateStack()) {
316       return;
317     }
318
319     stack_ = folly::make_unique<AltStackBuffer>();
320
321     setAlternateStack(stack_->data(), stack_->size());
322   }
323
324   ~ScopedAlternateSignalStack() {
325     if (stack_) {
326       unsetAlternateStack();
327     }
328   }
329
330  private:
331   using AltStackBuffer = std::array<char, kAltStackSize>;
332   std::unique_ptr<AltStackBuffer> stack_;
333 };
334 }
335
336 void FiberManager::registerAlternateSignalStack() {
337   static folly::SingletonThreadLocal<ScopedAlternateSignalStack> singleton;
338   singleton.get();
339
340   alternateSignalStackRegistered_ = true;
341 }
342 #endif
343 }
344 }