172 lines · cpp
1//===-- sanitizer_stoptheworld_test.cpp -----------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Tests for sanitizer_stoptheworld.h10//11//===----------------------------------------------------------------------===//12 13#include "sanitizer_common/sanitizer_stoptheworld.h"14 15#include "sanitizer_common/sanitizer_platform.h"16#if (SANITIZER_LINUX || SANITIZER_WINDOWS) && defined(__x86_64__)17 18# include <atomic>19# include <mutex>20# include <thread>21 22# include "gtest/gtest.h"23# include "sanitizer_common/sanitizer_common.h"24# include "sanitizer_common/sanitizer_libc.h"25 26namespace __sanitizer {27 28static std::mutex mutex;29 30struct CallbackArgument {31 std::atomic_int counter = {};32 std::atomic_bool threads_stopped = {};33 std::atomic_bool callback_executed = {};34};35 36void IncrementerThread(CallbackArgument &callback_argument) {37 while (true) {38 callback_argument.counter++;39 40 if (mutex.try_lock()) {41 mutex.unlock();42 return;43 }44 45 std::this_thread::yield();46 }47}48 49// This callback checks that IncrementerThread is suspended at the time of its50// execution.51void Callback(const SuspendedThreadsList &suspended_threads_list,52 void *argument) {53 CallbackArgument *callback_argument = (CallbackArgument *)argument;54 callback_argument->callback_executed = true;55 int counter_at_init = callback_argument->counter;56 for (uptr i = 0; i < 1000; i++) {57 std::this_thread::yield();58 if (callback_argument->counter != counter_at_init) {59 callback_argument->threads_stopped = false;60 return;61 }62 }63 callback_argument->threads_stopped = true;64}65 66TEST(StopTheWorld, SuspendThreadsSimple) {67 CallbackArgument argument;68 std::thread thread;69 {70 std::lock_guard<std::mutex> lock(mutex);71 thread = std::thread(IncrementerThread, std::ref(argument));72 StopTheWorld(&Callback, &argument);73 }74 EXPECT_TRUE(argument.callback_executed);75 EXPECT_TRUE(argument.threads_stopped);76 // argument is on stack, so we have to wait for the incrementer thread to77 // terminate before we can return from this function.78 ASSERT_NO_THROW(thread.join());79}80 81// A more comprehensive test where we spawn a bunch of threads while executing82// StopTheWorld in parallel.83static const uptr kThreadCount = 50;84static const uptr kStopWorldAfter = 10; // let this many threads spawn first85 86struct AdvancedCallbackArgument {87 std::atomic_uintptr_t thread_index = {};88 std::atomic_int counters[kThreadCount] = {};89 std::thread threads[kThreadCount];90 std::atomic_bool threads_stopped = {};91 std::atomic_bool callback_executed = {};92};93 94void AdvancedIncrementerThread(AdvancedCallbackArgument &callback_argument) {95 uptr this_thread_index = callback_argument.thread_index++;96 // Spawn the next thread.97 if (this_thread_index + 1 < kThreadCount) {98 callback_argument.threads[this_thread_index + 1] =99 std::thread(AdvancedIncrementerThread, std::ref(callback_argument));100 }101 // Do the actual work.102 while (true) {103 callback_argument.counters[this_thread_index]++;104 if (mutex.try_lock()) {105 mutex.unlock();106 return;107 }108 109 std::this_thread::yield();110 }111}112 113void AdvancedCallback(const SuspendedThreadsList &suspended_threads_list,114 void *argument) {115 AdvancedCallbackArgument *callback_argument =116 (AdvancedCallbackArgument *)argument;117 callback_argument->callback_executed = true;118 119 int counters_at_init[kThreadCount];120 for (uptr j = 0; j < kThreadCount; j++)121 counters_at_init[j] = callback_argument->counters[j];122 for (uptr i = 0; i < 10; i++) {123 std::this_thread::yield();124 for (uptr j = 0; j < kThreadCount; j++)125 if (callback_argument->counters[j] != counters_at_init[j]) {126 callback_argument->threads_stopped = false;127 return;128 }129 }130 callback_argument->threads_stopped = true;131}132 133TEST(StopTheWorld, SuspendThreadsAdvanced) {134 AdvancedCallbackArgument argument;135 136 {137 std::lock_guard<std::mutex> lock(mutex);138 argument.threads[0] =139 std::thread(AdvancedIncrementerThread, std::ref(argument));140 // Wait for several threads to spawn before proceeding.141 while (argument.thread_index < kStopWorldAfter) std::this_thread::yield();142 StopTheWorld(&AdvancedCallback, &argument);143 EXPECT_TRUE(argument.callback_executed);144 EXPECT_TRUE(argument.threads_stopped);145 146 // Wait for all threads to spawn before we start terminating them.147 while (argument.thread_index < kThreadCount) std::this_thread::yield();148 }149 // Signal the threads to terminate.150 for (auto &t : argument.threads) t.join();151}152 153static void SegvCallback(const SuspendedThreadsList &suspended_threads_list,154 void *argument) {155 *(volatile int *)0x1234 = 0;156}157 158# if SANITIZER_WINDOWS159# define MAYBE_SegvInCallback DISABLED_SegvInCallback160# else161# define MAYBE_SegvInCallback SegvInCallback162# endif163 164TEST(StopTheWorld, MAYBE_SegvInCallback) {165 // Test that tracer thread catches SIGSEGV.166 StopTheWorld(&SegvCallback, NULL);167}168 169} // namespace __sanitizer170 171#endif // SANITIZER_LINUX && defined(__x86_64__)172