master
  1//===-- sanitizer_stoptheworld_win.cpp ------------------------------------===//
  2//
  3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4// See https://llvm.org/LICENSE.txt for license information.
  5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6//
  7//===----------------------------------------------------------------------===//
  8//
  9// See sanitizer_stoptheworld.h for details.
 10//
 11//===----------------------------------------------------------------------===//
 12
 13#include "sanitizer_platform.h"
 14
 15#if SANITIZER_WINDOWS
 16
 17#  define WIN32_LEAN_AND_MEAN
 18#  include <windows.h>
 19// windows.h needs to be included before tlhelp32.h
 20#  include <tlhelp32.h>
 21
 22#  include "sanitizer_stoptheworld.h"
 23
 24namespace __sanitizer {
 25
 26namespace {
 27
 28struct SuspendedThreadsListWindows final : public SuspendedThreadsList {
 29  InternalMmapVector<HANDLE> threadHandles;
 30  InternalMmapVector<DWORD> threadIds;
 31
 32  SuspendedThreadsListWindows() {
 33    threadIds.reserve(1024);
 34    threadHandles.reserve(1024);
 35  }
 36
 37  PtraceRegistersStatus GetRegistersAndSP(uptr index,
 38                                          InternalMmapVector<uptr> *buffer,
 39                                          uptr *sp) const override;
 40
 41  tid_t GetThreadID(uptr index) const override;
 42  uptr ThreadCount() const override;
 43};
 44
 45// Stack Pointer register names on different architectures
 46#  if SANITIZER_X64
 47#    define SP_REG Rsp
 48#  elif SANITIZER_I386
 49#    define SP_REG Esp
 50#  elif SANITIZER_ARM | SANITIZER_ARM64
 51#    define SP_REG Sp
 52#  elif SANITIZER_MIPS32
 53#    define SP_REG IntSp
 54#  else
 55#    error Architecture not supported!
 56#  endif
 57
 58PtraceRegistersStatus SuspendedThreadsListWindows::GetRegistersAndSP(
 59    uptr index, InternalMmapVector<uptr> *buffer, uptr *sp) const {
 60  CHECK_LT(index, threadHandles.size());
 61
 62  buffer->resize(RoundUpTo(sizeof(CONTEXT), sizeof(uptr)) / sizeof(uptr));
 63  CONTEXT *thread_context = reinterpret_cast<CONTEXT *>(buffer->data());
 64  thread_context->ContextFlags = CONTEXT_ALL;
 65  CHECK(GetThreadContext(threadHandles[index], thread_context));
 66  *sp = thread_context->SP_REG;
 67
 68  return REGISTERS_AVAILABLE;
 69}
 70
 71tid_t SuspendedThreadsListWindows::GetThreadID(uptr index) const {
 72  CHECK_LT(index, threadIds.size());
 73  return threadIds[index];
 74}
 75
 76uptr SuspendedThreadsListWindows::ThreadCount() const {
 77  return threadIds.size();
 78}
 79
 80struct RunThreadArgs {
 81  StopTheWorldCallback callback;
 82  void *argument;
 83};
 84
 85DWORD WINAPI RunThread(void *argument) {
 86  RunThreadArgs *run_args = (RunThreadArgs *)argument;
 87
 88  const DWORD this_thread = GetCurrentThreadId();
 89  const DWORD this_process = GetCurrentProcessId();
 90
 91  SuspendedThreadsListWindows suspended_threads_list;
 92  bool new_thread_found;
 93
 94  do {
 95    // Take a snapshot of all Threads
 96    const HANDLE threads = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
 97    CHECK(threads != INVALID_HANDLE_VALUE);
 98
 99    THREADENTRY32 thread_entry;
100    thread_entry.dwSize = sizeof(thread_entry);
101    new_thread_found = false;
102
103    if (!Thread32First(threads, &thread_entry))
104      break;
105
106    do {
107      if (thread_entry.th32ThreadID == this_thread ||
108          thread_entry.th32OwnerProcessID != this_process)
109        continue;
110
111      bool suspended_thread = false;
112      for (const auto thread_id : suspended_threads_list.threadIds) {
113        if (thread_id == thread_entry.th32ThreadID) {
114          suspended_thread = true;
115          break;
116        }
117      }
118
119      // Skip the Thread if it was already suspended
120      if (suspended_thread)
121        continue;
122
123      const HANDLE thread =
124          OpenThread(THREAD_ALL_ACCESS, FALSE, thread_entry.th32ThreadID);
125      CHECK(thread);
126
127      if (SuspendThread(thread) == (DWORD)-1) {
128        DWORD last_error = GetLastError();
129
130        VPrintf(1, "Could not suspend thread %lu (error %lu)",
131                thread_entry.th32ThreadID, last_error);
132        continue;
133      }
134
135      suspended_threads_list.threadIds.push_back(thread_entry.th32ThreadID);
136      suspended_threads_list.threadHandles.push_back(thread);
137      new_thread_found = true;
138    } while (Thread32Next(threads, &thread_entry));
139
140    CloseHandle(threads);
141
142    // Between the call to `CreateToolhelp32Snapshot` and suspending the
143    // relevant Threads, new Threads could have potentially been created. So
144    // continue to find and suspend new Threads until we don't find any.
145  } while (new_thread_found);
146
147  // Now all Threads of this Process except of this Thread should be suspended.
148  // Execute the callback function.
149  run_args->callback(suspended_threads_list, run_args->argument);
150
151  // Resume all Threads
152  for (const auto suspended_thread_handle :
153       suspended_threads_list.threadHandles) {
154    CHECK_NE(ResumeThread(suspended_thread_handle), -1);
155    CloseHandle(suspended_thread_handle);
156  }
157
158  return 0;
159}
160
161}  // namespace
162
163void StopTheWorld(StopTheWorldCallback callback, void *argument) {
164  struct RunThreadArgs arg = {callback, argument};
165  DWORD trace_thread_id;
166
167  auto trace_thread =
168      CreateThread(nullptr, 0, RunThread, &arg, 0, &trace_thread_id);
169  CHECK(trace_thread);
170
171  WaitForSingleObject(trace_thread, INFINITE);
172  CloseHandle(trace_thread);
173}
174
175}  // namespace __sanitizer
176
177#endif  // SANITIZER_WINDOWS