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 | |
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 | |
56 | 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 | |
69 | tid_t SuspendedThreadsListWindows::GetThreadID(uptr index) const { |
70 | CHECK_LT(index, threadIds.size()); |
71 | return threadIds[index]; |
72 | } |
73 | |
74 | uptr SuspendedThreadsListWindows::ThreadCount() const { |
75 | return threadIds.size(); |
76 | } |
77 | |
78 | struct RunThreadArgs { |
79 | StopTheWorldCallback callback; |
80 | void *argument; |
81 | }; |
82 | |
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 | |
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 | |