1d6d569fcSNico Weber //===-- sanitizer_stoptheworld_test.cpp -----------------------------------===//
2d6d569fcSNico Weber //
3d6d569fcSNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4d6d569fcSNico Weber // See https://llvm.org/LICENSE.txt for license information.
5d6d569fcSNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d6d569fcSNico Weber //
7d6d569fcSNico Weber //===----------------------------------------------------------------------===//
8d6d569fcSNico Weber //
9d6d569fcSNico Weber // Tests for sanitizer_stoptheworld.h
10d6d569fcSNico Weber //
11d6d569fcSNico Weber //===----------------------------------------------------------------------===//
12d6d569fcSNico Weber
139991ab5dSClemens Wasser #include "sanitizer_common/sanitizer_stoptheworld.h"
149991ab5dSClemens Wasser
15d6d569fcSNico Weber #include "sanitizer_common/sanitizer_platform.h"
16*9b4f179bSClemens Wasser #if (SANITIZER_LINUX || SANITIZER_WINDOWS) && defined(__x86_64__)
17d6d569fcSNico Weber
185ed03c1eSVitaly Buka # include <atomic>
199991ab5dSClemens Wasser # include <mutex>
209991ab5dSClemens Wasser # include <thread>
21d6d569fcSNico Weber
229cf0ea35SVitaly Buka # include "gtest/gtest.h"
239cf0ea35SVitaly Buka # include "sanitizer_common/sanitizer_common.h"
249cf0ea35SVitaly Buka # include "sanitizer_common/sanitizer_libc.h"
259cf0ea35SVitaly Buka
26d6d569fcSNico Weber namespace __sanitizer {
27d6d569fcSNico Weber
285ed03c1eSVitaly Buka static std::mutex mutex;
29d6d569fcSNico Weber
30d6d569fcSNico Weber struct CallbackArgument {
315ed03c1eSVitaly Buka std::atomic_int counter = {};
325ed03c1eSVitaly Buka std::atomic_bool threads_stopped = {};
335ed03c1eSVitaly Buka std::atomic_bool callback_executed = {};
34d6d569fcSNico Weber };
35d6d569fcSNico Weber
IncrementerThread(CallbackArgument & callback_argument)369991ab5dSClemens Wasser void IncrementerThread(CallbackArgument &callback_argument) {
37d6d569fcSNico Weber while (true) {
385ed03c1eSVitaly Buka callback_argument.counter++;
399991ab5dSClemens Wasser
405ed03c1eSVitaly Buka if (mutex.try_lock()) {
415ed03c1eSVitaly Buka mutex.unlock();
429991ab5dSClemens Wasser return;
43d6d569fcSNico Weber }
449991ab5dSClemens Wasser
459991ab5dSClemens Wasser std::this_thread::yield();
46d6d569fcSNico Weber }
47d6d569fcSNico Weber }
48d6d569fcSNico Weber
49d6d569fcSNico Weber // This callback checks that IncrementerThread is suspended at the time of its
50d6d569fcSNico Weber // execution.
Callback(const SuspendedThreadsList & suspended_threads_list,void * argument)51d6d569fcSNico Weber void Callback(const SuspendedThreadsList &suspended_threads_list,
52d6d569fcSNico Weber void *argument) {
53d6d569fcSNico Weber CallbackArgument *callback_argument = (CallbackArgument *)argument;
54d6d569fcSNico Weber callback_argument->callback_executed = true;
555ed03c1eSVitaly Buka int counter_at_init = callback_argument->counter;
56d6d569fcSNico Weber for (uptr i = 0; i < 1000; i++) {
579991ab5dSClemens Wasser std::this_thread::yield();
585ed03c1eSVitaly Buka if (callback_argument->counter != counter_at_init) {
59d6d569fcSNico Weber callback_argument->threads_stopped = false;
60d6d569fcSNico Weber return;
61d6d569fcSNico Weber }
62d6d569fcSNico Weber }
63d6d569fcSNico Weber callback_argument->threads_stopped = true;
64d6d569fcSNico Weber }
65d6d569fcSNico Weber
TEST(StopTheWorld,SuspendThreadsSimple)66d6d569fcSNico Weber TEST(StopTheWorld, SuspendThreadsSimple) {
67d6d569fcSNico Weber CallbackArgument argument;
689991ab5dSClemens Wasser std::thread thread;
695ed03c1eSVitaly Buka {
705ed03c1eSVitaly Buka std::lock_guard<std::mutex> lock(mutex);
715ed03c1eSVitaly Buka thread = std::thread(IncrementerThread, std::ref(argument));
72d6d569fcSNico Weber StopTheWorld(&Callback, &argument);
735ed03c1eSVitaly Buka }
74d6d569fcSNico Weber EXPECT_TRUE(argument.callback_executed);
75d6d569fcSNico Weber EXPECT_TRUE(argument.threads_stopped);
76d6d569fcSNico Weber // argument is on stack, so we have to wait for the incrementer thread to
77d6d569fcSNico Weber // terminate before we can return from this function.
789991ab5dSClemens Wasser ASSERT_NO_THROW(thread.join());
79d6d569fcSNico Weber }
80d6d569fcSNico Weber
81d6d569fcSNico Weber // A more comprehensive test where we spawn a bunch of threads while executing
82d6d569fcSNico Weber // StopTheWorld in parallel.
83d6d569fcSNico Weber static const uptr kThreadCount = 50;
84d6d569fcSNico Weber static const uptr kStopWorldAfter = 10; // let this many threads spawn first
85d6d569fcSNico Weber
86d6d569fcSNico Weber struct AdvancedCallbackArgument {
875ed03c1eSVitaly Buka std::atomic_uintptr_t thread_index = {};
885ed03c1eSVitaly Buka std::atomic_int counters[kThreadCount] = {};
899991ab5dSClemens Wasser std::thread threads[kThreadCount];
905ed03c1eSVitaly Buka std::atomic_bool threads_stopped = {};
915ed03c1eSVitaly Buka std::atomic_bool callback_executed = {};
92d6d569fcSNico Weber };
93d6d569fcSNico Weber
AdvancedIncrementerThread(AdvancedCallbackArgument & callback_argument)949991ab5dSClemens Wasser void AdvancedIncrementerThread(AdvancedCallbackArgument &callback_argument) {
955ed03c1eSVitaly Buka uptr this_thread_index = callback_argument.thread_index++;
96d6d569fcSNico Weber // Spawn the next thread.
97d6d569fcSNico Weber if (this_thread_index + 1 < kThreadCount) {
989991ab5dSClemens Wasser callback_argument.threads[this_thread_index + 1] =
999991ab5dSClemens Wasser std::thread(AdvancedIncrementerThread, std::ref(callback_argument));
100d6d569fcSNico Weber }
101d6d569fcSNico Weber // Do the actual work.
102d6d569fcSNico Weber while (true) {
1035ed03c1eSVitaly Buka callback_argument.counters[this_thread_index]++;
1045ed03c1eSVitaly Buka if (mutex.try_lock()) {
1055ed03c1eSVitaly Buka mutex.unlock();
1069991ab5dSClemens Wasser return;
107d6d569fcSNico Weber }
1089991ab5dSClemens Wasser
1099991ab5dSClemens Wasser std::this_thread::yield();
110d6d569fcSNico Weber }
111d6d569fcSNico Weber }
112d6d569fcSNico Weber
AdvancedCallback(const SuspendedThreadsList & suspended_threads_list,void * argument)113d6d569fcSNico Weber void AdvancedCallback(const SuspendedThreadsList &suspended_threads_list,
114d6d569fcSNico Weber void *argument) {
115d6d569fcSNico Weber AdvancedCallbackArgument *callback_argument =
116d6d569fcSNico Weber (AdvancedCallbackArgument *)argument;
117d6d569fcSNico Weber callback_argument->callback_executed = true;
118d6d569fcSNico Weber
119d6d569fcSNico Weber int counters_at_init[kThreadCount];
120d6d569fcSNico Weber for (uptr j = 0; j < kThreadCount; j++)
1215ed03c1eSVitaly Buka counters_at_init[j] = callback_argument->counters[j];
122d6d569fcSNico Weber for (uptr i = 0; i < 10; i++) {
1239991ab5dSClemens Wasser std::this_thread::yield();
124d6d569fcSNico Weber for (uptr j = 0; j < kThreadCount; j++)
1255ed03c1eSVitaly Buka if (callback_argument->counters[j] != counters_at_init[j]) {
126d6d569fcSNico Weber callback_argument->threads_stopped = false;
127d6d569fcSNico Weber return;
128d6d569fcSNico Weber }
129d6d569fcSNico Weber }
130d6d569fcSNico Weber callback_argument->threads_stopped = true;
131d6d569fcSNico Weber }
132d6d569fcSNico Weber
TEST(StopTheWorld,SuspendThreadsAdvanced)133d6d569fcSNico Weber TEST(StopTheWorld, SuspendThreadsAdvanced) {
134d6d569fcSNico Weber AdvancedCallbackArgument argument;
135d6d569fcSNico Weber
1365ed03c1eSVitaly Buka {
1375ed03c1eSVitaly Buka std::lock_guard<std::mutex> lock(mutex);
1389991ab5dSClemens Wasser argument.threads[0] =
1399991ab5dSClemens Wasser std::thread(AdvancedIncrementerThread, std::ref(argument));
140d6d569fcSNico Weber // Wait for several threads to spawn before proceeding.
1415ed03c1eSVitaly Buka while (argument.thread_index < kStopWorldAfter) std::this_thread::yield();
142d6d569fcSNico Weber StopTheWorld(&AdvancedCallback, &argument);
143d6d569fcSNico Weber EXPECT_TRUE(argument.callback_executed);
144d6d569fcSNico Weber EXPECT_TRUE(argument.threads_stopped);
145d6d569fcSNico Weber
146d6d569fcSNico Weber // Wait for all threads to spawn before we start terminating them.
1475ed03c1eSVitaly Buka while (argument.thread_index < kThreadCount) std::this_thread::yield();
1485ed03c1eSVitaly Buka }
149d6d569fcSNico Weber // Signal the threads to terminate.
1505ed03c1eSVitaly Buka for (auto &t : argument.threads) t.join();
151d6d569fcSNico Weber }
152d6d569fcSNico Weber
SegvCallback(const SuspendedThreadsList & suspended_threads_list,void * argument)153d6d569fcSNico Weber static void SegvCallback(const SuspendedThreadsList &suspended_threads_list,
154d6d569fcSNico Weber void *argument) {
155d6d569fcSNico Weber *(volatile int *)0x1234 = 0;
156d6d569fcSNico Weber }
157d6d569fcSNico Weber
158*9b4f179bSClemens Wasser # if SANITIZER_WINDOWS
159*9b4f179bSClemens Wasser # define MAYBE_SegvInCallback DISABLED_SegvInCallback
160*9b4f179bSClemens Wasser # else
161*9b4f179bSClemens Wasser # define MAYBE_SegvInCallback SegvInCallback
162*9b4f179bSClemens Wasser # endif
163*9b4f179bSClemens Wasser
TEST(StopTheWorld,MAYBE_SegvInCallback)164*9b4f179bSClemens Wasser TEST(StopTheWorld, MAYBE_SegvInCallback) {
165d6d569fcSNico Weber // Test that tracer thread catches SIGSEGV.
166d6d569fcSNico Weber StopTheWorld(&SegvCallback, NULL);
167d6d569fcSNico Weber }
168d6d569fcSNico Weber
169d6d569fcSNico Weber } // namespace __sanitizer
170d6d569fcSNico Weber
171d6d569fcSNico Weber #endif // SANITIZER_LINUX && defined(__x86_64__)
172