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