| // Copyright 2019 The Chromium Authors | |
| // 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_suspend.h" | |
| #include "base/profiler/stack_buffer.h" | |
| #include "base/profiler/suspendable_thread_delegate.h" | |
| namespace base { | |
| StackCopierSuspend::StackCopierSuspend( | |
| std::unique_ptr<SuspendableThreadDelegate> thread_delegate) | |
| : thread_delegate_(std::move(thread_delegate)) {} | |
| StackCopierSuspend::~StackCopierSuspend() = default; | |
| // Suspends the thread, copies the stack state, and resumes the thread. The | |
| // copied stack state includes the stack itself, the top address of the stack | |
| // copy, and the register context. Returns true on success, and returns the | |
| // copied state via the params. | |
| // | |
| // NO HEAP ALLOCATIONS within the ScopedSuspendThread scope. | |
| bool StackCopierSuspend::CopyStack(StackBuffer* stack_buffer, | |
| uintptr_t* stack_top, | |
| TimeTicks* timestamp, | |
| RegisterContext* thread_context, | |
| Delegate* delegate) { | |
| const uintptr_t top = thread_delegate_->GetStackBaseAddress(); | |
| uintptr_t bottom = 0; | |
| const uint8_t* stack_copy_bottom = nullptr; | |
| { | |
| // Allocation of the ScopedSuspendThread object itself is OK since it | |
| // necessarily occurs before the thread is suspended by the object. | |
| std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread> | |
| suspend_thread = thread_delegate_->CreateScopedSuspendThread(); | |
| // TimeTicks::Now() is implemented in terms of reads to the timer tick | |
| // counter or TSC register on x86/x86_64 so is reentrant. | |
| *timestamp = TimeTicks::Now(); | |
| if (!suspend_thread->WasSuccessful()) | |
| return false; | |
| if (!thread_delegate_->GetThreadContext(thread_context)) | |
| return false; | |
| bottom = RegisterContextStackPointer(thread_context); | |
| // The StackBuffer allocation is expected to be at least as large as the | |
| // largest stack region allocation on the platform, but check just in case | |
| // it isn't *and* the actual stack itself exceeds the buffer allocation | |
| // size. | |
| if ((top - bottom) > stack_buffer->size()) | |
| return false; | |
| if (!thread_delegate_->CanCopyStack(bottom)) | |
| return false; | |
| delegate->OnStackCopy(); | |
| stack_copy_bottom = CopyStackContentsAndRewritePointers( | |
| reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top), | |
| StackBuffer::kPlatformStackAlignment, stack_buffer->buffer()); | |
| } | |
| *stack_top = reinterpret_cast<uintptr_t>(stack_copy_bottom) + (top - bottom); | |
| for (uintptr_t* reg : | |
| thread_delegate_->GetRegistersToRewrite(thread_context)) { | |
| *reg = RewritePointerIfInOriginalStack(reinterpret_cast<uint8_t*>(bottom), | |
| reinterpret_cast<uintptr_t*>(top), | |
| stack_copy_bottom, *reg); | |
| } | |
| return true; | |
| } | |
| } // namespace base |