123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/profiler/stack_copier_signal.h"
- #include <linux/futex.h>
- #include <signal.h>
- #include <sys/ucontext.h>
- #include <syscall.h>
- #include <atomic>
- #include <cstring>
- #include "base/memory/raw_ptr.h"
- #include "base/notreached.h"
- #include "base/profiler/register_context.h"
- #include "base/profiler/stack_buffer.h"
- #include "base/profiler/suspendable_thread_delegate.h"
- #include "base/time/time_override.h"
- #include "base/trace_event/base_tracing.h"
- #include "build/build_config.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- namespace base {
- namespace {
- // Waitable event implementation with futex and without DCHECK(s), since signal
- // handlers cannot allocate memory or use pthread api.
- class AsyncSafeWaitableEvent {
- public:
- AsyncSafeWaitableEvent() { futex_.store(0, std::memory_order_release); }
- ~AsyncSafeWaitableEvent() {}
- bool Wait() {
- // futex() can wake up spuriously if this memory address was previously used
- // for a pthread mutex. So, also check the condition.
- while (true) {
- long res =
- syscall(SYS_futex, futex_int_ptr(), FUTEX_WAIT | FUTEX_PRIVATE_FLAG,
- 0, nullptr, nullptr, 0);
- if (futex_.load(std::memory_order_acquire) != 0)
- return true;
- if (res != 0)
- return false;
- }
- }
- void Signal() {
- futex_.store(1, std::memory_order_release);
- syscall(SYS_futex, futex_int_ptr(), FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1,
- nullptr, nullptr, 0);
- }
- private:
- // Provides a pointer to the atomic's storage. std::atomic_int has standard
- // layout so its address can be used for the pointer as long as it only
- // contains the int.
- int* futex_int_ptr() {
- static_assert(sizeof(futex_) == sizeof(int),
- "Expected std::atomic_int to be the same size as int");
- return reinterpret_cast<int*>(&futex_);
- }
- std::atomic_int futex_{0};
- };
- // Scoped signal event that calls Signal on the AsyncSafeWaitableEvent at
- // destructor.
- class ScopedEventSignaller {
- public:
- ScopedEventSignaller(AsyncSafeWaitableEvent* event) : event_(event) {}
- ~ScopedEventSignaller() { event_->Signal(); }
- private:
- raw_ptr<AsyncSafeWaitableEvent> event_;
- };
- // Struct to store the arguments to the signal handler.
- struct HandlerParams {
- uintptr_t stack_base_address;
- // The event is signalled when signal handler is done executing.
- raw_ptr<AsyncSafeWaitableEvent> event;
- // Return values:
- // Successfully copied the stack segment.
- raw_ptr<bool> success;
- // The thread context of the leaf function.
- raw_ptr<mcontext_t> context;
- // Buffer to copy the stack segment.
- raw_ptr<StackBuffer> stack_buffer;
- raw_ptr<const uint8_t*> stack_copy_bottom;
- // The timestamp when the stack was copied.
- raw_ptr<absl::optional<TimeTicks>> maybe_timestamp;
- // The delegate provided to the StackCopier.
- raw_ptr<StackCopier::Delegate> stack_copier_delegate;
- };
- // Pointer to the parameters to be "passed" to the CopyStackSignalHandler() from
- // the sampling thread to the sampled (stopped) thread. This value is set just
- // before sending the signal to the thread and reset when the handler is done.
- std::atomic<HandlerParams*> g_handler_params;
- // CopyStackSignalHandler is invoked on the stopped thread and records the
- // thread's stack and register context at the time the signal was received. This
- // function may only call reentrant code.
- void CopyStackSignalHandler(int n, siginfo_t* siginfo, void* sigcontext) {
- HandlerParams* params = g_handler_params.load(std::memory_order_acquire);
- // MaybeTimeTicksNowIgnoringOverride() is implemented in terms of
- // clock_gettime on Linux, which is signal safe per the signal-safety(7) man
- // page, but is not garanteed to succeed, in which case absl::nullopt is
- // returned. TimeTicks::Now() can't be used because it expects clock_gettime
- // to always succeed and is thus not signal-safe.
- *params->maybe_timestamp = subtle::MaybeTimeTicksNowIgnoringOverride();
- ScopedEventSignaller e(params->event);
- *params->success = false;
- const ucontext_t* ucontext = static_cast<ucontext_t*>(sigcontext);
- std::memcpy(params->context, &ucontext->uc_mcontext, sizeof(mcontext_t));
- const uintptr_t bottom = RegisterContextStackPointer(params->context);
- const uintptr_t top = params->stack_base_address;
- if ((top - bottom) > params->stack_buffer->size()) {
- // The stack exceeds the size of the allocated buffer. The buffer is sized
- // such that this shouldn't happen under typical execution so we can safely
- // punt in this situation.
- return;
- }
- params->stack_copier_delegate->OnStackCopy();
- *params->stack_copy_bottom =
- StackCopierSignal::CopyStackContentsAndRewritePointers(
- reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top),
- StackBuffer::kPlatformStackAlignment, params->stack_buffer->buffer());
- *params->success = true;
- }
- // Sets the global handler params for the signal handler function.
- class ScopedSetSignalHandlerParams {
- public:
- ScopedSetSignalHandlerParams(HandlerParams* params) {
- g_handler_params.store(params, std::memory_order_release);
- }
- ~ScopedSetSignalHandlerParams() {
- g_handler_params.store(nullptr, std::memory_order_release);
- }
- };
- class ScopedSigaction {
- public:
- ScopedSigaction(int signal,
- struct sigaction* action,
- struct sigaction* original_action)
- : signal_(signal),
- action_(action),
- original_action_(original_action),
- succeeded_(sigaction(signal, action, original_action) == 0) {}
- bool succeeded() const { return succeeded_; }
- ~ScopedSigaction() {
- if (!succeeded_)
- return;
- bool reset_succeeded = sigaction(signal_, original_action_, action_) == 0;
- DCHECK(reset_succeeded);
- }
- private:
- const int signal_;
- const raw_ptr<struct sigaction> action_;
- const raw_ptr<struct sigaction> original_action_;
- const bool succeeded_;
- };
- } // namespace
- StackCopierSignal::StackCopierSignal(
- std::unique_ptr<ThreadDelegate> thread_delegate)
- : thread_delegate_(std::move(thread_delegate)) {}
- StackCopierSignal::~StackCopierSignal() = default;
- bool StackCopierSignal::CopyStack(StackBuffer* stack_buffer,
- uintptr_t* stack_top,
- TimeTicks* timestamp,
- RegisterContext* thread_context,
- Delegate* delegate) {
- AsyncSafeWaitableEvent wait_event;
- bool copied = false;
- const uint8_t* stack_copy_bottom = nullptr;
- const uintptr_t stack_base_address = thread_delegate_->GetStackBaseAddress();
- absl::optional<TimeTicks> maybe_timestamp;
- HandlerParams params = {stack_base_address, &wait_event, &copied,
- thread_context, stack_buffer, &stack_copy_bottom,
- &maybe_timestamp, delegate};
- {
- ScopedSetSignalHandlerParams scoped_handler_params(¶ms);
- // Set the signal handler for the thread to the stack copy function.
- struct sigaction action;
- struct sigaction original_action;
- memset(&action, 0, sizeof(action));
- action.sa_sigaction = CopyStackSignalHandler;
- action.sa_flags = SA_RESTART | SA_SIGINFO;
- sigemptyset(&action.sa_mask);
- TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
- "StackCopierSignal copy stack");
- // SIGURG is chosen here because we observe no crashes with this signal and
- // neither Chrome or the AOSP sets up a special handler for this signal.
- ScopedSigaction scoped_sigaction(SIGURG, &action, &original_action);
- if (!scoped_sigaction.succeeded())
- return false;
- if (syscall(SYS_tgkill, getpid(), thread_delegate_->GetThreadId(),
- SIGURG) != 0) {
- NOTREACHED();
- return false;
- }
- bool finished_waiting = wait_event.Wait();
- TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
- "StackCopierSignal copy stack");
- if (!finished_waiting) {
- NOTREACHED();
- return false;
- }
- // Ideally, an accurate timestamp is captured while the sampled thread is
- // paused. In rare cases, this may fail, in which case we resort to
- // capturing an delayed timestamp here instead.
- if (maybe_timestamp.has_value())
- *timestamp = maybe_timestamp.value();
- else {
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
- "Fallback on TimeTicks::Now()");
- *timestamp = TimeTicks::Now();
- }
- }
- const uintptr_t bottom = RegisterContextStackPointer(params.context);
- for (uintptr_t* reg :
- thread_delegate_->GetRegistersToRewrite(thread_context)) {
- *reg = StackCopierSignal::RewritePointerIfInOriginalStack(
- reinterpret_cast<uint8_t*>(bottom),
- reinterpret_cast<uintptr_t*>(stack_base_address), stack_copy_bottom,
- *reg);
- }
- *stack_top = reinterpret_cast<uintptr_t>(stack_copy_bottom) +
- (stack_base_address - bottom);
- return copied;
- }
- } // namespace base
|