#include "base/profiler/suspendable_thread_delegate_win.h"
#include <windows.h>
#include <winternl.h>
#include <vector>
#include "base/check.h"
#include "base/debug/alias.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/profiler/native_unwinder_win.h"
#include "build/build_config.h"
namespace base {
namespace {
struct TEB {
NT_TIB Tib;
};
win::ScopedHandle GetCurrentThreadHandle() {
HANDLE thread;
CHECK(::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentThread(),
::GetCurrentProcess(), &thread, 0, FALSE,
DUPLICATE_SAME_ACCESS));
return win::ScopedHandle(thread);
}
win::ScopedHandle GetThreadHandle(PlatformThreadId thread_id) {
if (thread_id.raw() == ::GetCurrentThreadId()) {
return GetCurrentThreadHandle();
}
DWORD flags = 0;
base::debug::Alias(&flags);
flags |= THREAD_GET_CONTEXT;
win::ScopedHandle test_handle1(::OpenThread(flags, FALSE, thread_id.raw()));
CHECK(test_handle1.is_valid());
flags |= THREAD_QUERY_INFORMATION;
win::ScopedHandle test_handle2(::OpenThread(flags, FALSE, thread_id.raw()));
CHECK(test_handle2.is_valid());
flags |= THREAD_SUSPEND_RESUME;
win::ScopedHandle handle(::OpenThread(flags, FALSE, thread_id.raw()));
CHECK(handle.is_valid());
return handle;
}
const TEB* GetThreadEnvironmentBlock(PlatformThreadId thread_id,
HANDLE thread_handle) {
if (thread_id.raw() == ::GetCurrentThreadId()) {
return reinterpret_cast<TEB*>(NtCurrentTeb());
}
constexpr auto ThreadBasicInformation = static_cast<THREADINFOCLASS>(0);
struct THREAD_BASIC_INFORMATION {
NTSTATUS ExitStatus;
RAW_PTR_EXCLUSION TEB* Teb;
CLIENT_ID ClientId;
KAFFINITY AffinityMask;
LONG Priority;
LONG BasePriority;
};
THREAD_BASIC_INFORMATION basic_info = {0};
NTSTATUS status = ::NtQueryInformationThread(
thread_handle, ThreadBasicInformation, &basic_info,
sizeof(THREAD_BASIC_INFORMATION), nullptr);
if (status != 0) {
return nullptr;
}
return basic_info.Teb;
}
bool PointsToGuardPage(uintptr_t stack_pointer) {
MEMORY_BASIC_INFORMATION memory_info;
SIZE_T result = ::VirtualQuery(reinterpret_cast<LPCVOID>(stack_pointer),
&memory_info, sizeof(memory_info));
return result != 0 && (memory_info.Protect & PAGE_GUARD);
}
class ScopedDisablePriorityBoost {
public:
ScopedDisablePriorityBoost(HANDLE thread_handle);
ScopedDisablePriorityBoost(const ScopedDisablePriorityBoost&) = delete;
ScopedDisablePriorityBoost& operator=(const ScopedDisablePriorityBoost&) =
delete;
~ScopedDisablePriorityBoost();
private:
HANDLE thread_handle_;
BOOL got_previous_boost_state_;
BOOL boost_state_was_disabled_;
};
ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle)
: thread_handle_(thread_handle),
got_previous_boost_state_(false),
boost_state_was_disabled_(false) {
got_previous_boost_state_ =
::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_);
if (got_previous_boost_state_) {
::SetThreadPriorityBoost(thread_handle_, TRUE);
}
}
ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
if (got_previous_boost_state_) {
::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_);
}
}
}
SuspendableThreadDelegateWin::ScopedSuspendThread::ScopedSuspendThread(
HANDLE thread_handle)
: thread_handle_(thread_handle),
was_successful_(::SuspendThread(thread_handle) !=
static_cast<DWORD>(-1)) {}
SuspendableThreadDelegateWin::ScopedSuspendThread::~ScopedSuspendThread() {
if (!was_successful_) {
return;
}
ScopedDisablePriorityBoost disable_priority_boost(thread_handle_);
bool resume_thread_succeeded =
::ResumeThread(thread_handle_) != static_cast<DWORD>(-1);
PCHECK(resume_thread_succeeded) << "ResumeThread failed";
}
bool SuspendableThreadDelegateWin::ScopedSuspendThread::WasSuccessful() const {
return was_successful_;
}
SuspendableThreadDelegateWin::SuspendableThreadDelegateWin(
SamplingProfilerThreadToken thread_token)
: thread_id_(thread_token.id),
thread_handle_(GetThreadHandle(thread_token.id)),
thread_stack_base_address_(reinterpret_cast<uintptr_t>(
GetThreadEnvironmentBlock(thread_token.id, thread_handle_.get())
->Tib.StackBase)) {}
SuspendableThreadDelegateWin::~SuspendableThreadDelegateWin() = default;
std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread>
SuspendableThreadDelegateWin::CreateScopedSuspendThread() {
return std::make_unique<ScopedSuspendThread>(thread_handle_.get());
}
PlatformThreadId SuspendableThreadDelegateWin::GetThreadId() const {
return thread_id_;
}
bool SuspendableThreadDelegateWin::GetThreadContext(CONTEXT* thread_context) {
*thread_context = {0};
thread_context->ContextFlags = CONTEXT_FULL;
return ::GetThreadContext(thread_handle_.get(), thread_context) != 0;
}
uintptr_t SuspendableThreadDelegateWin::GetStackBaseAddress() const {
return thread_stack_base_address_;
}
bool SuspendableThreadDelegateWin::CanCopyStack(uintptr_t stack_pointer) {
return !PointsToGuardPage(stack_pointer);
}
std::vector<uintptr_t*> SuspendableThreadDelegateWin::GetRegistersToRewrite(
CONTEXT* thread_context) {
return {
#if defined(ARCH_CPU_X86_64)
&thread_context->R12, &thread_context->R13,
&thread_context->R14, &thread_context->R15,
&thread_context->Rdi, &thread_context->Rsi,
&thread_context->Rbx, &thread_context->Rbp,
&thread_context->Rsp
#elif defined(ARCH_CPU_ARM64)
&thread_context->X19, &thread_context->X20, &thread_context->X21,
&thread_context->X22, &thread_context->X23, &thread_context->X24,
&thread_context->X25, &thread_context->X26, &thread_context->X27,
&thread_context->X28, &thread_context->Fp, &thread_context->Lr,
&thread_context->Sp
#endif
};
}
}