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#  else
53#    error Architecture not supported!
54#  endif
55
56PtraceRegistersStatus SuspendedThreadsListWindows::GetRegistersAndSP(
57    uptr index, InternalMmapVector<uptr> *buffer, uptr *sp) const {
58  CHECK_LT(index, threadHandles.size());
59
60  buffer->resize(RoundUpTo(sizeof(CONTEXT), sizeof(uptr)) / sizeof(uptr));
61  CONTEXT *thread_context = reinterpret_cast<CONTEXT *>(buffer->data());
62  thread_context->ContextFlags = CONTEXT_ALL;
63  CHECK(GetThreadContext(threadHandles[index], thread_context));
64  *sp = thread_context->SP_REG;
65
66  return REGISTERS_AVAILABLE;
67}
68
69tid_t SuspendedThreadsListWindows::GetThreadID(uptr index) const {
70  CHECK_LT(index, threadIds.size());
71  return threadIds[index];
72}
73
74uptr SuspendedThreadsListWindows::ThreadCount() const {
75  return threadIds.size();
76}
77
78struct RunThreadArgs {
79  StopTheWorldCallback callback;
80  void *argument;
81};
82
83DWORD WINAPI RunThread(void *argument) {
84  RunThreadArgs *run_args = (RunThreadArgs *)argument;
85
86  const DWORD this_thread = GetCurrentThreadId();
87  const DWORD this_process = GetCurrentProcessId();
88
89  SuspendedThreadsListWindows suspended_threads_list;
90  bool new_thread_found;
91
92  do {
93    // Take a snapshot of all Threads
94    const HANDLE threads = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
95    CHECK(threads != INVALID_HANDLE_VALUE);
96
97    THREADENTRY32 thread_entry;
98    thread_entry.dwSize = sizeof(thread_entry);
99    new_thread_found = false;
100
101    if (!Thread32First(threads, &thread_entry))
102      break;
103
104    do {
105      if (thread_entry.th32ThreadID == this_thread ||
106          thread_entry.th32OwnerProcessID != this_process)
107        continue;
108
109      bool suspended_thread = false;
110      for (const auto thread_id : suspended_threads_list.threadIds) {
111        if (thread_id == thread_entry.th32ThreadID) {
112          suspended_thread = true;
113          break;
114        }
115      }
116
117      // Skip the Thread if it was already suspended
118      if (suspended_thread)
119        continue;
120
121      const HANDLE thread =
122          OpenThread(THREAD_ALL_ACCESS, FALSE, thread_entry.th32ThreadID);
123      CHECK(thread);
124
125      if (SuspendThread(thread) == (DWORD)-1) {
126        DWORD last_error = GetLastError();
127
128        VPrintf(1, "Could not suspend thread %lu (error %lu)",
129                thread_entry.th32ThreadID, last_error);
130        continue;
131      }
132
133      suspended_threads_list.threadIds.push_back(thread_entry.th32ThreadID);
134      suspended_threads_list.threadHandles.push_back(thread);
135      new_thread_found = true;
136    } while (Thread32Next(threads, &thread_entry));
137
138    CloseHandle(threads);
139
140    // Between the call to `CreateToolhelp32Snapshot` and suspending the
141    // relevant Threads, new Threads could have potentially been created. So
142    // continue to find and suspend new Threads until we don't find any.
143  } while (new_thread_found);
144
145  // Now all Threads of this Process except of this Thread should be suspended.
146  // Execute the callback function.
147  run_args->callback(suspended_threads_list, run_args->argument);
148
149  // Resume all Threads
150  for (const auto suspended_thread_handle :
151       suspended_threads_list.threadHandles) {
152    CHECK_NE(ResumeThread(suspended_thread_handle), -1);
153    CloseHandle(suspended_thread_handle);
154  }
155
156  return 0;
157}
158
159}  // namespace
160
161void StopTheWorld(StopTheWorldCallback callback, void *argument) {
162  struct RunThreadArgs arg = {callback, argument};
163  DWORD trace_thread_id;
164
165  auto trace_thread =
166      CreateThread(nullptr, 0, RunThread, &arg, 0, &trace_thread_id);
167  CHECK(trace_thread);
168
169  WaitForSingleObject(trace_thread, INFINITE);
170  CloseHandle(trace_thread);
171}
172
173}  // namespace __sanitizer
174
175#endif  // SANITIZER_WINDOWS
176