aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16-rt/lib/sanitizer_common/sanitizer_stoptheworld_win.cpp
blob: f114acea79c9c6bac8e924ea7618623eea02c9a0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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 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