suspendable_thread_delegate_win.cc 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/profiler/suspendable_thread_delegate_win.h"
  5. #include <windows.h>
  6. #include <winternl.h>
  7. #include <vector>
  8. #include "base/check.h"
  9. #include "base/debug/alias.h"
  10. #include "base/memory/raw_ptr.h"
  11. #include "base/profiler/native_unwinder_win.h"
  12. #include "build/build_config.h"
  13. // IMPORTANT NOTE: Some functions within this implementation are invoked while
  14. // the target thread is suspended so it must not do any allocation from the
  15. // heap, including indirectly via use of DCHECK/CHECK or other logging
  16. // statements. Otherwise this code can deadlock on heap locks acquired by the
  17. // target thread before it was suspended. These functions are commented with "NO
  18. // HEAP ALLOCATIONS".
  19. namespace base {
  20. namespace {
  21. // The thread environment block internal type.
  22. struct TEB {
  23. NT_TIB Tib;
  24. // Rest of struct is ignored.
  25. };
  26. win::ScopedHandle GetCurrentThreadHandle() {
  27. HANDLE thread;
  28. CHECK(::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentThread(),
  29. ::GetCurrentProcess(), &thread, 0, FALSE,
  30. DUPLICATE_SAME_ACCESS));
  31. return win::ScopedHandle(thread);
  32. }
  33. win::ScopedHandle GetThreadHandle(PlatformThreadId thread_id) {
  34. // TODO(https://crbug.com/947459): Move this logic to
  35. // GetSamplingProfilerCurrentThreadToken() and pass the handle in
  36. // SamplingProfilerThreadToken.
  37. if (thread_id == ::GetCurrentThreadId())
  38. return GetCurrentThreadHandle();
  39. // TODO(http://crbug.com/947459): Remove the test_handle* CHECKs once we
  40. // understand which flag is triggering the failure.
  41. DWORD flags = 0;
  42. base::debug::Alias(&flags);
  43. flags |= THREAD_GET_CONTEXT;
  44. win::ScopedHandle test_handle1(::OpenThread(flags, FALSE, thread_id));
  45. CHECK(test_handle1.is_valid());
  46. flags |= THREAD_QUERY_INFORMATION;
  47. win::ScopedHandle test_handle2(::OpenThread(flags, FALSE, thread_id));
  48. CHECK(test_handle2.is_valid());
  49. flags |= THREAD_SUSPEND_RESUME;
  50. win::ScopedHandle handle(::OpenThread(flags, FALSE, thread_id));
  51. CHECK(handle.is_valid());
  52. return handle;
  53. }
  54. // Returns the thread environment block pointer for |thread_handle|.
  55. const TEB* GetThreadEnvironmentBlock(PlatformThreadId thread_id,
  56. HANDLE thread_handle) {
  57. // TODO(https://crbug.com/947459): Move this logic to
  58. // GetSamplingProfilerCurrentThreadToken() and pass the TEB* in
  59. // SamplingProfilerThreadToken.
  60. if (thread_id == ::GetCurrentThreadId())
  61. return reinterpret_cast<TEB*>(NtCurrentTeb());
  62. // Define the internal types we need to invoke NtQueryInformationThread.
  63. enum THREAD_INFORMATION_CLASS { ThreadBasicInformation };
  64. struct CLIENT_ID {
  65. HANDLE UniqueProcess;
  66. HANDLE UniqueThread;
  67. };
  68. struct THREAD_BASIC_INFORMATION {
  69. NTSTATUS ExitStatus;
  70. raw_ptr<TEB> Teb;
  71. CLIENT_ID ClientId;
  72. KAFFINITY AffinityMask;
  73. LONG Priority;
  74. LONG BasePriority;
  75. };
  76. using NtQueryInformationThreadFunction =
  77. NTSTATUS(WINAPI*)(HANDLE, THREAD_INFORMATION_CLASS, PVOID, ULONG, PULONG);
  78. static const auto nt_query_information_thread =
  79. reinterpret_cast<NtQueryInformationThreadFunction>(::GetProcAddress(
  80. ::GetModuleHandle(L"ntdll.dll"), "NtQueryInformationThread"));
  81. if (!nt_query_information_thread)
  82. return nullptr;
  83. THREAD_BASIC_INFORMATION basic_info = {0};
  84. NTSTATUS status = nt_query_information_thread(
  85. thread_handle, ThreadBasicInformation, &basic_info,
  86. sizeof(THREAD_BASIC_INFORMATION), nullptr);
  87. if (status != 0)
  88. return nullptr;
  89. return basic_info.Teb;
  90. }
  91. // Tests whether |stack_pointer| points to a location in the guard page. NO HEAP
  92. // ALLOCATIONS.
  93. bool PointsToGuardPage(uintptr_t stack_pointer) {
  94. MEMORY_BASIC_INFORMATION memory_info;
  95. SIZE_T result = ::VirtualQuery(reinterpret_cast<LPCVOID>(stack_pointer),
  96. &memory_info, sizeof(memory_info));
  97. return result != 0 && (memory_info.Protect & PAGE_GUARD);
  98. }
  99. // ScopedDisablePriorityBoost -------------------------------------------------
  100. // Disables priority boost on a thread for the lifetime of the object.
  101. class ScopedDisablePriorityBoost {
  102. public:
  103. ScopedDisablePriorityBoost(HANDLE thread_handle);
  104. ScopedDisablePriorityBoost(const ScopedDisablePriorityBoost&) = delete;
  105. ScopedDisablePriorityBoost& operator=(const ScopedDisablePriorityBoost&) =
  106. delete;
  107. ~ScopedDisablePriorityBoost();
  108. private:
  109. HANDLE thread_handle_;
  110. BOOL got_previous_boost_state_;
  111. BOOL boost_state_was_disabled_;
  112. };
  113. // NO HEAP ALLOCATIONS.
  114. ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle)
  115. : thread_handle_(thread_handle),
  116. got_previous_boost_state_(false),
  117. boost_state_was_disabled_(false) {
  118. got_previous_boost_state_ =
  119. ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_);
  120. if (got_previous_boost_state_) {
  121. // Confusingly, TRUE disables priority boost.
  122. ::SetThreadPriorityBoost(thread_handle_, TRUE);
  123. }
  124. }
  125. ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
  126. if (got_previous_boost_state_)
  127. ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_);
  128. }
  129. } // namespace
  130. // ScopedSuspendThread --------------------------------------------------------
  131. // NO HEAP ALLOCATIONS after ::SuspendThread.
  132. SuspendableThreadDelegateWin::ScopedSuspendThread::ScopedSuspendThread(
  133. HANDLE thread_handle)
  134. : thread_handle_(thread_handle),
  135. was_successful_(::SuspendThread(thread_handle) !=
  136. static_cast<DWORD>(-1)) {}
  137. // NO HEAP ALLOCATIONS. The CHECK is OK because it provides a more noisy failure
  138. // mode than deadlocking.
  139. SuspendableThreadDelegateWin::ScopedSuspendThread::~ScopedSuspendThread() {
  140. if (!was_successful_)
  141. return;
  142. // Disable the priority boost that the thread would otherwise receive on
  143. // resume. We do this to avoid artificially altering the dynamics of the
  144. // executing application any more than we already are by suspending and
  145. // resuming the thread.
  146. //
  147. // Note that this can racily disable a priority boost that otherwise would
  148. // have been given to the thread, if the thread is waiting on other wait
  149. // conditions at the time of SuspendThread and those conditions are satisfied
  150. // before priority boost is reenabled. The measured length of this window is
  151. // ~100us, so this should occur fairly rarely.
  152. ScopedDisablePriorityBoost disable_priority_boost(thread_handle_);
  153. bool resume_thread_succeeded =
  154. ::ResumeThread(thread_handle_) != static_cast<DWORD>(-1);
  155. CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError();
  156. }
  157. bool SuspendableThreadDelegateWin::ScopedSuspendThread::WasSuccessful() const {
  158. return was_successful_;
  159. }
  160. // SuspendableThreadDelegateWin
  161. // ----------------------------------------------------------
  162. SuspendableThreadDelegateWin::SuspendableThreadDelegateWin(
  163. SamplingProfilerThreadToken thread_token)
  164. : thread_id_(thread_token.id),
  165. thread_handle_(GetThreadHandle(thread_token.id)),
  166. thread_stack_base_address_(reinterpret_cast<uintptr_t>(
  167. GetThreadEnvironmentBlock(thread_token.id, thread_handle_.get())
  168. ->Tib.StackBase)) {}
  169. SuspendableThreadDelegateWin::~SuspendableThreadDelegateWin() = default;
  170. std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread>
  171. SuspendableThreadDelegateWin::CreateScopedSuspendThread() {
  172. return std::make_unique<ScopedSuspendThread>(thread_handle_.get());
  173. }
  174. PlatformThreadId SuspendableThreadDelegateWin::GetThreadId() const {
  175. return thread_id_;
  176. }
  177. // NO HEAP ALLOCATIONS.
  178. bool SuspendableThreadDelegateWin::GetThreadContext(CONTEXT* thread_context) {
  179. *thread_context = {0};
  180. thread_context->ContextFlags = CONTEXT_FULL;
  181. return ::GetThreadContext(thread_handle_.get(), thread_context) != 0;
  182. }
  183. // NO HEAP ALLOCATIONS.
  184. uintptr_t SuspendableThreadDelegateWin::GetStackBaseAddress() const {
  185. return thread_stack_base_address_;
  186. }
  187. // Tests whether |stack_pointer| points to a location in the guard page. NO HEAP
  188. // ALLOCATIONS.
  189. bool SuspendableThreadDelegateWin::CanCopyStack(uintptr_t stack_pointer) {
  190. // Dereferencing a pointer in the guard page in a thread that doesn't own the
  191. // stack results in a STATUS_GUARD_PAGE_VIOLATION exception and a crash. This
  192. // occurs very rarely, but reliably over the population.
  193. return !PointsToGuardPage(stack_pointer);
  194. }
  195. std::vector<uintptr_t*> SuspendableThreadDelegateWin::GetRegistersToRewrite(
  196. CONTEXT* thread_context) {
  197. // Return the set of non-volatile registers.
  198. return {
  199. #if defined(ARCH_CPU_X86_64)
  200. &thread_context->R12, &thread_context->R13, &thread_context->R14,
  201. &thread_context->R15, &thread_context->Rdi, &thread_context->Rsi,
  202. &thread_context->Rbx, &thread_context->Rbp, &thread_context->Rsp
  203. #elif defined(ARCH_CPU_ARM64)
  204. &thread_context->X19, &thread_context->X20, &thread_context->X21,
  205. &thread_context->X22, &thread_context->X23, &thread_context->X24,
  206. &thread_context->X25, &thread_context->X26, &thread_context->X27,
  207. &thread_context->X28, &thread_context->Fp, &thread_context->Lr,
  208. &thread_context->Sp
  209. #endif
  210. };
  211. }
  212. } // namespace base