blob: f114acea79c9c6bac8e924ea7618623eea02c9a0 [file] [log] [blame]
//===-- sanitizer_stoptheworld_win.cpp ------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// See sanitizer_stoptheworld.h for details.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
#if SANITIZER_WINDOWS
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
// windows.h needs to be included before tlhelp32.h
# include <tlhelp32.h>
# include "sanitizer_stoptheworld.h"
namespace __sanitizer {
namespace {
struct SuspendedThreadsListWindows final : public SuspendedThreadsList {
InternalMmapVector<HANDLE> threadHandles;
InternalMmapVector<DWORD> threadIds;
SuspendedThreadsListWindows() {
threadIds.reserve(1024);
threadHandles.reserve(1024);
}
PtraceRegistersStatus GetRegistersAndSP(uptr index,
InternalMmapVector<uptr> *buffer,
uptr *sp) const override;
tid_t GetThreadID(uptr index) const override;
uptr ThreadCount() const override;
};
// Stack Pointer register names on different architectures
# if SANITIZER_X64
# define SP_REG Rsp
# elif SANITIZER_I386
# define SP_REG Esp
# elif SANITIZER_ARM | SANITIZER_ARM64
# define SP_REG Sp
# else
# error Architecture not supported!
# endif
PtraceRegistersStatus SuspendedThreadsListWindows::GetRegistersAndSP(
uptr index, InternalMmapVector<uptr> *buffer, uptr *sp) const {
CHECK_LT(index, threadHandles.size());
buffer->resize(RoundUpTo(sizeof(CONTEXT), sizeof(uptr)) / sizeof(uptr));
CONTEXT *thread_context = reinterpret_cast<CONTEXT *>(buffer->data());
thread_context->ContextFlags = CONTEXT_ALL;
CHECK(GetThreadContext(threadHandles[index], thread_context));
*sp = thread_context->SP_REG;
return REGISTERS_AVAILABLE;
}
tid_t SuspendedThreadsListWindows::GetThreadID(uptr index) const {
CHECK_LT(index, threadIds.size());
return threadIds[index];
}
uptr SuspendedThreadsListWindows::ThreadCount() const {
return threadIds.size();
}
struct RunThreadArgs {
StopTheWorldCallback callback;
void *argument;
};
DWORD WINAPI RunThread(void *argument) {
RunThreadArgs *run_args = (RunThreadArgs *)argument;
const DWORD this_thread = GetCurrentThreadId();
const DWORD this_process = GetCurrentProcessId();
SuspendedThreadsListWindows suspended_threads_list;
bool new_thread_found;
do {
// Take a snapshot of all Threads
const HANDLE threads = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
CHECK(threads != INVALID_HANDLE_VALUE);
THREADENTRY32 thread_entry;
thread_entry.dwSize = sizeof(thread_entry);
new_thread_found = false;
if (!Thread32First(threads, &thread_entry))
break;
do {
if (thread_entry.th32ThreadID == this_thread ||
thread_entry.th32OwnerProcessID != this_process)
continue;
bool suspended_thread = false;
for (const auto thread_id : suspended_threads_list.threadIds) {
if (thread_id == thread_entry.th32ThreadID) {
suspended_thread = true;
break;
}
}
// Skip the Thread if it was already suspended
if (suspended_thread)
continue;
const HANDLE thread =
OpenThread(THREAD_ALL_ACCESS, FALSE, thread_entry.th32ThreadID);
CHECK(thread);
if (SuspendThread(thread) == (DWORD)-1) {
DWORD last_error = GetLastError();
VPrintf(1, "Could not suspend thread %lu (error %lu)",
thread_entry.th32ThreadID, last_error);
continue;
}
suspended_threads_list.threadIds.push_back(thread_entry.th32ThreadID);
suspended_threads_list.threadHandles.push_back(thread);
new_thread_found = true;
} while (Thread32Next(threads, &thread_entry));
CloseHandle(threads);
// Between the call to `CreateToolhelp32Snapshot` and suspending the
// relevant Threads, new Threads could have potentially been created. So
// continue to find and suspend new Threads until we don't find any.
} while (new_thread_found);
// Now all Threads of this Process except of this Thread should be suspended.
// Execute the callback function.
run_args->callback(suspended_threads_list, run_args->argument);
// Resume all Threads
for (const auto suspended_thread_handle :
suspended_threads_list.threadHandles) {
CHECK_NE(ResumeThread(suspended_thread_handle), -1);
CloseHandle(suspended_thread_handle);
}
return 0;
}
} // namespace
void StopTheWorld(StopTheWorldCallback callback, void *argument) {
struct RunThreadArgs arg = {callback, argument};
DWORD trace_thread_id;
auto trace_thread =
CreateThread(nullptr, 0, RunThread, &arg, 0, &trace_thread_id);
CHECK(trace_thread);
WaitForSingleObject(trace_thread, INFINITE);
CloseHandle(trace_thread);
}
} // namespace __sanitizer
#endif // SANITIZER_WINDOWS