xref: /llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_win.cpp (revision 5d5d806e7b95d8a99d5628d113d8ecd33b2b289f)
19b4f179bSClemens Wasser //===-- sanitizer_stoptheworld_win.cpp ------------------------------------===//
29b4f179bSClemens Wasser //
39b4f179bSClemens Wasser // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49b4f179bSClemens Wasser // See https://llvm.org/LICENSE.txt for license information.
59b4f179bSClemens Wasser // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69b4f179bSClemens Wasser //
79b4f179bSClemens Wasser //===----------------------------------------------------------------------===//
89b4f179bSClemens Wasser //
99b4f179bSClemens Wasser // See sanitizer_stoptheworld.h for details.
109b4f179bSClemens Wasser //
119b4f179bSClemens Wasser //===----------------------------------------------------------------------===//
129b4f179bSClemens Wasser 
139b4f179bSClemens Wasser #include "sanitizer_platform.h"
149b4f179bSClemens Wasser 
159b4f179bSClemens Wasser #if SANITIZER_WINDOWS
169b4f179bSClemens Wasser 
179b4f179bSClemens Wasser #  define WIN32_LEAN_AND_MEAN
189b4f179bSClemens Wasser #  include <windows.h>
199b4f179bSClemens Wasser // windows.h needs to be included before tlhelp32.h
209b4f179bSClemens Wasser #  include <tlhelp32.h>
219b4f179bSClemens Wasser 
229b4f179bSClemens Wasser #  include "sanitizer_stoptheworld.h"
239b4f179bSClemens Wasser 
249b4f179bSClemens Wasser namespace __sanitizer {
259b4f179bSClemens Wasser 
269b4f179bSClemens Wasser namespace {
279b4f179bSClemens Wasser 
289b4f179bSClemens Wasser struct SuspendedThreadsListWindows final : public SuspendedThreadsList {
299b4f179bSClemens Wasser   InternalMmapVector<HANDLE> threadHandles;
309b4f179bSClemens Wasser   InternalMmapVector<DWORD> threadIds;
319b4f179bSClemens Wasser 
SuspendedThreadsListWindows__sanitizer::__anon9291efba0111::SuspendedThreadsListWindows329b4f179bSClemens Wasser   SuspendedThreadsListWindows() {
339b4f179bSClemens Wasser     threadIds.reserve(1024);
349b4f179bSClemens Wasser     threadHandles.reserve(1024);
359b4f179bSClemens Wasser   }
369b4f179bSClemens Wasser 
379b4f179bSClemens Wasser   PtraceRegistersStatus GetRegistersAndSP(uptr index,
389b4f179bSClemens Wasser                                           InternalMmapVector<uptr> *buffer,
399b4f179bSClemens Wasser                                           uptr *sp) const override;
409b4f179bSClemens Wasser 
419b4f179bSClemens Wasser   tid_t GetThreadID(uptr index) const override;
429b4f179bSClemens Wasser   uptr ThreadCount() const override;
439b4f179bSClemens Wasser };
449b4f179bSClemens Wasser 
459b4f179bSClemens Wasser // Stack Pointer register names on different architectures
469b4f179bSClemens Wasser #  if SANITIZER_X64
479b4f179bSClemens Wasser #    define SP_REG Rsp
489b4f179bSClemens Wasser #  elif SANITIZER_I386
499b4f179bSClemens Wasser #    define SP_REG Esp
509b4f179bSClemens Wasser #  elif SANITIZER_ARM | SANITIZER_ARM64
519b4f179bSClemens Wasser #    define SP_REG Sp
529b4f179bSClemens Wasser #  else
539b4f179bSClemens Wasser #    error Architecture not supported!
549b4f179bSClemens Wasser #  endif
559b4f179bSClemens Wasser 
GetRegistersAndSP(uptr index,InternalMmapVector<uptr> * buffer,uptr * sp) const569b4f179bSClemens Wasser PtraceRegistersStatus SuspendedThreadsListWindows::GetRegistersAndSP(
579b4f179bSClemens Wasser     uptr index, InternalMmapVector<uptr> *buffer, uptr *sp) const {
589b4f179bSClemens Wasser   CHECK_LT(index, threadHandles.size());
599b4f179bSClemens Wasser 
609b4f179bSClemens Wasser   buffer->resize(RoundUpTo(sizeof(CONTEXT), sizeof(uptr)) / sizeof(uptr));
619b4f179bSClemens Wasser   CONTEXT *thread_context = reinterpret_cast<CONTEXT *>(buffer->data());
629b4f179bSClemens Wasser   thread_context->ContextFlags = CONTEXT_ALL;
639b4f179bSClemens Wasser   CHECK(GetThreadContext(threadHandles[index], thread_context));
649b4f179bSClemens Wasser   *sp = thread_context->SP_REG;
659b4f179bSClemens Wasser 
669b4f179bSClemens Wasser   return REGISTERS_AVAILABLE;
679b4f179bSClemens Wasser }
689b4f179bSClemens Wasser 
GetThreadID(uptr index) const699b4f179bSClemens Wasser tid_t SuspendedThreadsListWindows::GetThreadID(uptr index) const {
709b4f179bSClemens Wasser   CHECK_LT(index, threadIds.size());
719b4f179bSClemens Wasser   return threadIds[index];
729b4f179bSClemens Wasser }
739b4f179bSClemens Wasser 
ThreadCount() const749b4f179bSClemens Wasser uptr SuspendedThreadsListWindows::ThreadCount() const {
759b4f179bSClemens Wasser   return threadIds.size();
769b4f179bSClemens Wasser }
779b4f179bSClemens Wasser 
789b4f179bSClemens Wasser struct RunThreadArgs {
799b4f179bSClemens Wasser   StopTheWorldCallback callback;
809b4f179bSClemens Wasser   void *argument;
819b4f179bSClemens Wasser };
829b4f179bSClemens Wasser 
RunThread(void * argument)839b4f179bSClemens Wasser DWORD WINAPI RunThread(void *argument) {
849b4f179bSClemens Wasser   RunThreadArgs *run_args = (RunThreadArgs *)argument;
859b4f179bSClemens Wasser 
869b4f179bSClemens Wasser   const DWORD this_thread = GetCurrentThreadId();
879b4f179bSClemens Wasser   const DWORD this_process = GetCurrentProcessId();
889b4f179bSClemens Wasser 
899b4f179bSClemens Wasser   SuspendedThreadsListWindows suspended_threads_list;
909b4f179bSClemens Wasser   bool new_thread_found;
919b4f179bSClemens Wasser 
929b4f179bSClemens Wasser   do {
939b4f179bSClemens Wasser     // Take a snapshot of all Threads
949b4f179bSClemens Wasser     const HANDLE threads = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
959b4f179bSClemens Wasser     CHECK(threads != INVALID_HANDLE_VALUE);
969b4f179bSClemens Wasser 
979b4f179bSClemens Wasser     THREADENTRY32 thread_entry;
989b4f179bSClemens Wasser     thread_entry.dwSize = sizeof(thread_entry);
999b4f179bSClemens Wasser     new_thread_found = false;
1009b4f179bSClemens Wasser 
1019b4f179bSClemens Wasser     if (!Thread32First(threads, &thread_entry))
1029b4f179bSClemens Wasser       break;
1039b4f179bSClemens Wasser 
1049b4f179bSClemens Wasser     do {
1059b4f179bSClemens Wasser       if (thread_entry.th32ThreadID == this_thread ||
1069b4f179bSClemens Wasser           thread_entry.th32OwnerProcessID != this_process)
1079b4f179bSClemens Wasser         continue;
1089b4f179bSClemens Wasser 
1099b4f179bSClemens Wasser       bool suspended_thread = false;
1109b4f179bSClemens Wasser       for (const auto thread_id : suspended_threads_list.threadIds) {
1119b4f179bSClemens Wasser         if (thread_id == thread_entry.th32ThreadID) {
1129b4f179bSClemens Wasser           suspended_thread = true;
1139b4f179bSClemens Wasser           break;
1149b4f179bSClemens Wasser         }
1159b4f179bSClemens Wasser       }
1169b4f179bSClemens Wasser 
1179b4f179bSClemens Wasser       // Skip the Thread if it was already suspended
1189b4f179bSClemens Wasser       if (suspended_thread)
1199b4f179bSClemens Wasser         continue;
1209b4f179bSClemens Wasser 
1219b4f179bSClemens Wasser       const HANDLE thread =
1229b4f179bSClemens Wasser           OpenThread(THREAD_ALL_ACCESS, FALSE, thread_entry.th32ThreadID);
1239b4f179bSClemens Wasser       CHECK(thread);
1249b4f179bSClemens Wasser 
125*5d5d806eSAlexandre Ganea       if (SuspendThread(thread) == (DWORD)-1) {
1269b4f179bSClemens Wasser         DWORD last_error = GetLastError();
1279b4f179bSClemens Wasser 
1289b4f179bSClemens Wasser         VPrintf(1, "Could not suspend thread %lu (error %lu)",
1299b4f179bSClemens Wasser                 thread_entry.th32ThreadID, last_error);
1309b4f179bSClemens Wasser         continue;
1319b4f179bSClemens Wasser       }
1329b4f179bSClemens Wasser 
1339b4f179bSClemens Wasser       suspended_threads_list.threadIds.push_back(thread_entry.th32ThreadID);
1349b4f179bSClemens Wasser       suspended_threads_list.threadHandles.push_back(thread);
1359b4f179bSClemens Wasser       new_thread_found = true;
1369b4f179bSClemens Wasser     } while (Thread32Next(threads, &thread_entry));
1379b4f179bSClemens Wasser 
1389b4f179bSClemens Wasser     CloseHandle(threads);
1399b4f179bSClemens Wasser 
1409b4f179bSClemens Wasser     // Between the call to `CreateToolhelp32Snapshot` and suspending the
1419b4f179bSClemens Wasser     // relevant Threads, new Threads could have potentially been created. So
1429b4f179bSClemens Wasser     // continue to find and suspend new Threads until we don't find any.
1439b4f179bSClemens Wasser   } while (new_thread_found);
1449b4f179bSClemens Wasser 
1459b4f179bSClemens Wasser   // Now all Threads of this Process except of this Thread should be suspended.
1469b4f179bSClemens Wasser   // Execute the callback function.
1479b4f179bSClemens Wasser   run_args->callback(suspended_threads_list, run_args->argument);
1489b4f179bSClemens Wasser 
1499b4f179bSClemens Wasser   // Resume all Threads
1509b4f179bSClemens Wasser   for (const auto suspended_thread_handle :
1519b4f179bSClemens Wasser        suspended_threads_list.threadHandles) {
1529b4f179bSClemens Wasser     CHECK_NE(ResumeThread(suspended_thread_handle), -1);
1539b4f179bSClemens Wasser     CloseHandle(suspended_thread_handle);
1549b4f179bSClemens Wasser   }
1559b4f179bSClemens Wasser 
1569b4f179bSClemens Wasser   return 0;
1579b4f179bSClemens Wasser }
1589b4f179bSClemens Wasser 
1599b4f179bSClemens Wasser }  // namespace
1609b4f179bSClemens Wasser 
StopTheWorld(StopTheWorldCallback callback,void * argument)1619b4f179bSClemens Wasser void StopTheWorld(StopTheWorldCallback callback, void *argument) {
1629b4f179bSClemens Wasser   struct RunThreadArgs arg = {callback, argument};
1639b4f179bSClemens Wasser   DWORD trace_thread_id;
1649b4f179bSClemens Wasser 
1659b4f179bSClemens Wasser   auto trace_thread =
1669b4f179bSClemens Wasser       CreateThread(nullptr, 0, RunThread, &arg, 0, &trace_thread_id);
1679b4f179bSClemens Wasser   CHECK(trace_thread);
1689b4f179bSClemens Wasser 
1699b4f179bSClemens Wasser   WaitForSingleObject(trace_thread, INFINITE);
1709b4f179bSClemens Wasser   CloseHandle(trace_thread);
1719b4f179bSClemens Wasser }
1729b4f179bSClemens Wasser 
1739b4f179bSClemens Wasser }  // namespace __sanitizer
1749b4f179bSClemens Wasser 
1759b4f179bSClemens Wasser #endif  // SANITIZER_WINDOWS
176