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 
24 namespace __sanitizer {
25 
26 namespace {
27 
28 struct SuspendedThreadsListWindows final : public SuspendedThreadsList {
29   InternalMmapVector<HANDLE> threadHandles;
30   InternalMmapVector<DWORD> threadIds;
31 
SuspendedThreadsListWindows__sanitizer::__anon7595438f0111::SuspendedThreadsListWindows32   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 
GetRegistersAndSP(uptr index,InternalMmapVector<uptr> * buffer,uptr * sp) const56 PtraceRegistersStatus 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 
GetThreadID(uptr index) const69 tid_t SuspendedThreadsListWindows::GetThreadID(uptr index) const {
70   CHECK_LT(index, threadIds.size());
71   return threadIds[index];
72 }
73 
ThreadCount() const74 uptr SuspendedThreadsListWindows::ThreadCount() const {
75   return threadIds.size();
76 }
77 
78 struct RunThreadArgs {
79   StopTheWorldCallback callback;
80   void *argument;
81 };
82 
RunThread(void * argument)83 DWORD 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 
StopTheWorld(StopTheWorldCallback callback,void * argument)161 void 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