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