178 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// See sanitizer_stoptheworld.h for details.10//11//===----------------------------------------------------------------------===//12 13#include "sanitizer_platform.h"14 15#if SANITIZER_WINDOWS16 17# define WIN32_LEAN_AND_MEAN18# include <windows.h>19// windows.h needs to be included before tlhelp32.h20# 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 ThreadID GetThreadID(uptr index) const override;42 uptr ThreadCount() const override;43};44 45// Stack Pointer register names on different architectures46# if SANITIZER_X6447# define SP_REG Rsp48# elif SANITIZER_I38649# define SP_REG Esp50# elif SANITIZER_ARM | SANITIZER_ARM6451# define SP_REG Sp52# elif SANITIZER_MIPS3253# define SP_REG IntSp54# else55# error Architecture not supported!56# endif57 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 71ThreadID 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 Threads96 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 suspended120 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 the143 // relevant Threads, new Threads could have potentially been created. So144 // 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 Threads152 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} // namespace162 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 __sanitizer176 177#endif // SANITIZER_WINDOWS178