[compiler-rt] 9b4f179 - Port __sanitizer::StopTheWorld to Windows

Vitaly Buka via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 20 18:29:53 PST 2021


Author: Clemens Wasser
Date: 2021-12-20T18:29:44-08:00
New Revision: 9b4f179bf8d3fd82009704d88b590512e622589d

URL: https://github.com/llvm/llvm-project/commit/9b4f179bf8d3fd82009704d88b590512e622589d
DIFF: https://github.com/llvm/llvm-project/commit/9b4f179bf8d3fd82009704d88b590512e622589d.diff

LOG: Port __sanitizer::StopTheWorld to Windows

This also makes the sanitizer_stoptheworld_test cross-platform by using the STL, rather than pthread.

Reviewed By: vitalybuka

Differential Revision: https://reviews.llvm.org/D115204

Added: 
    compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_win.cpp

Modified: 
    compiler-rt/lib/sanitizer_common/CMakeLists.txt
    compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
index 1e8339fb645e2..011c0880168b5 100644
--- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt
+++ b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
@@ -34,6 +34,7 @@ set(SANITIZER_SOURCES_NOTERMINATION
   sanitizer_solaris.cpp
   sanitizer_stoptheworld_fuchsia.cpp
   sanitizer_stoptheworld_mac.cpp
+  sanitizer_stoptheworld_win.cpp
   sanitizer_suppressions.cpp
   sanitizer_tls_get_addr.cpp
   sanitizer_thread_registry.cpp

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_win.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_win.cpp
new file mode 100644
index 0000000000000..e12b9e5bee06f
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_win.cpp
@@ -0,0 +1,175 @@
+//===-- 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 
diff erent 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) == -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

diff  --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cpp
index 247d161de0b76..c2a269084d0f2 100644
--- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cpp
+++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cpp
@@ -13,7 +13,7 @@
 #include "sanitizer_common/sanitizer_stoptheworld.h"
 
 #include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_LINUX && defined(__x86_64__)
+#if (SANITIZER_LINUX || SANITIZER_WINDOWS) && defined(__x86_64__)
 
 #  include <atomic>
 #  include <mutex>
@@ -155,7 +155,13 @@ static void SegvCallback(const SuspendedThreadsList &suspended_threads_list,
   *(volatile int *)0x1234 = 0;
 }
 
-TEST(StopTheWorld, SegvInCallback) {
+#  if SANITIZER_WINDOWS
+#    define MAYBE_SegvInCallback DISABLED_SegvInCallback
+#  else
+#    define MAYBE_SegvInCallback SegvInCallback
+#  endif
+
+TEST(StopTheWorld, MAYBE_SegvInCallback) {
   // Test that tracer thread catches SIGSEGV.
   StopTheWorld(&SegvCallback, NULL);
 }


        


More information about the llvm-commits mailing list