#include "sanitizer_platform.h"
#if SANITIZER_WINDOWS
#define WIN32_LEAN_AND_MEAN
# include <windows.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;
};
# if SANITIZER_X64
#define SP_REG …
# elif SANITIZER_I386
#define SP_REG …
# elif SANITIZER_ARM | SANITIZER_ARM64
#define SP_REG …
# 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 {
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;
}
}
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);
} while (new_thread_found);
run_args->callback(suspended_threads_list, run_args->argument);
for (const auto suspended_thread_handle :
suspended_threads_list.threadHandles) {
CHECK_NE(ResumeThread(suspended_thread_handle), -1);
CloseHandle(suspended_thread_handle);
}
return 0;
}
}
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);
}
}
#endif