| 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 | |