brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.2 KiB · c2a2690 Raw
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