Home | History | Annotate | Download | only in tests
      1 //===-- sanitizer_stoptheworld_test.cc ------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // Tests for sanitizer_stoptheworld.h
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "sanitizer_common/sanitizer_platform.h"
     15 #if SANITIZER_LINUX && defined(__x86_64__)
     16 
     17 #include "sanitizer_common/sanitizer_stoptheworld.h"
     18 #include "gtest/gtest.h"
     19 
     20 #include "sanitizer_common/sanitizer_libc.h"
     21 #include "sanitizer_common/sanitizer_common.h"
     22 
     23 #include <pthread.h>
     24 #include <sched.h>
     25 
     26 namespace __sanitizer {
     27 
     28 static pthread_mutex_t incrementer_thread_exit_mutex;
     29 
     30 struct CallbackArgument {
     31   volatile int counter;
     32   volatile bool threads_stopped;
     33   volatile bool callback_executed;
     34   CallbackArgument()
     35     : counter(0),
     36       threads_stopped(false),
     37       callback_executed(false) {}
     38 };
     39 
     40 void *IncrementerThread(void *argument) {
     41   CallbackArgument *callback_argument = (CallbackArgument *)argument;
     42   while (true) {
     43     __sync_fetch_and_add(&callback_argument->counter, 1);
     44     if (pthread_mutex_trylock(&incrementer_thread_exit_mutex) == 0) {
     45       pthread_mutex_unlock(&incrementer_thread_exit_mutex);
     46       return NULL;
     47     } else {
     48       sched_yield();
     49     }
     50   }
     51 }
     52 
     53 // This callback checks that IncrementerThread is suspended at the time of its
     54 // execution.
     55 void Callback(const SuspendedThreadsList &suspended_threads_list,
     56               void *argument) {
     57   CallbackArgument *callback_argument = (CallbackArgument *)argument;
     58   callback_argument->callback_executed = true;
     59   int counter_at_init = __sync_fetch_and_add(&callback_argument->counter, 0);
     60   for (uptr i = 0; i < 1000; i++) {
     61     sched_yield();
     62     if (__sync_fetch_and_add(&callback_argument->counter, 0) !=
     63           counter_at_init) {
     64       callback_argument->threads_stopped = false;
     65       return;
     66     }
     67   }
     68   callback_argument->threads_stopped = true;
     69 }
     70 
     71 TEST(StopTheWorld, SuspendThreadsSimple) {
     72   pthread_mutex_init(&incrementer_thread_exit_mutex, NULL);
     73   CallbackArgument argument;
     74   pthread_t thread_id;
     75   int pthread_create_result;
     76   pthread_mutex_lock(&incrementer_thread_exit_mutex);
     77   pthread_create_result = pthread_create(&thread_id, NULL, IncrementerThread,
     78                                          &argument);
     79   ASSERT_EQ(0, pthread_create_result);
     80   StopTheWorld(&Callback, &argument);
     81   pthread_mutex_unlock(&incrementer_thread_exit_mutex);
     82   EXPECT_TRUE(argument.callback_executed);
     83   EXPECT_TRUE(argument.threads_stopped);
     84   // argument is on stack, so we have to wait for the incrementer thread to
     85   // terminate before we can return from this function.
     86   ASSERT_EQ(0, pthread_join(thread_id, NULL));
     87   pthread_mutex_destroy(&incrementer_thread_exit_mutex);
     88 }
     89 
     90 // A more comprehensive test where we spawn a bunch of threads while executing
     91 // StopTheWorld in parallel.
     92 static const uptr kThreadCount = 50;
     93 static const uptr kStopWorldAfter = 10; // let this many threads spawn first
     94 
     95 static pthread_mutex_t advanced_incrementer_thread_exit_mutex;
     96 
     97 struct AdvancedCallbackArgument {
     98   volatile uptr thread_index;
     99   volatile int counters[kThreadCount];
    100   pthread_t thread_ids[kThreadCount];
    101   volatile bool threads_stopped;
    102   volatile bool callback_executed;
    103   volatile bool fatal_error;
    104   AdvancedCallbackArgument()
    105     : thread_index(0),
    106       threads_stopped(false),
    107       callback_executed(false),
    108       fatal_error(false) {}
    109 };
    110 
    111 void *AdvancedIncrementerThread(void *argument) {
    112   AdvancedCallbackArgument *callback_argument =
    113       (AdvancedCallbackArgument *)argument;
    114   uptr this_thread_index = __sync_fetch_and_add(
    115       &callback_argument->thread_index, 1);
    116   // Spawn the next thread.
    117   int pthread_create_result;
    118   if (this_thread_index + 1 < kThreadCount) {
    119     pthread_create_result =
    120         pthread_create(&callback_argument->thread_ids[this_thread_index + 1],
    121                        NULL, AdvancedIncrementerThread, argument);
    122     // Cannot use ASSERT_EQ in non-void-returning functions. If there's a
    123     // problem, defer failing to the main thread.
    124     if (pthread_create_result != 0) {
    125       callback_argument->fatal_error = true;
    126       __sync_fetch_and_add(&callback_argument->thread_index,
    127                            kThreadCount - callback_argument->thread_index);
    128     }
    129   }
    130   // Do the actual work.
    131   while (true) {
    132     __sync_fetch_and_add(&callback_argument->counters[this_thread_index], 1);
    133     if (pthread_mutex_trylock(&advanced_incrementer_thread_exit_mutex) == 0) {
    134       pthread_mutex_unlock(&advanced_incrementer_thread_exit_mutex);
    135       return NULL;
    136     } else {
    137       sched_yield();
    138     }
    139   }
    140 }
    141 
    142 void AdvancedCallback(const SuspendedThreadsList &suspended_threads_list,
    143                              void *argument) {
    144   AdvancedCallbackArgument *callback_argument =
    145       (AdvancedCallbackArgument *)argument;
    146   callback_argument->callback_executed = true;
    147 
    148   int counters_at_init[kThreadCount];
    149   for (uptr j = 0; j < kThreadCount; j++)
    150     counters_at_init[j] = __sync_fetch_and_add(&callback_argument->counters[j],
    151                                                0);
    152   for (uptr i = 0; i < 10; i++) {
    153     sched_yield();
    154     for (uptr j = 0; j < kThreadCount; j++)
    155       if (__sync_fetch_and_add(&callback_argument->counters[j], 0) !=
    156             counters_at_init[j]) {
    157         callback_argument->threads_stopped = false;
    158         return;
    159       }
    160   }
    161   callback_argument->threads_stopped = true;
    162 }
    163 
    164 TEST(StopTheWorld, SuspendThreadsAdvanced) {
    165   pthread_mutex_init(&advanced_incrementer_thread_exit_mutex, NULL);
    166   AdvancedCallbackArgument argument;
    167 
    168   pthread_mutex_lock(&advanced_incrementer_thread_exit_mutex);
    169   int pthread_create_result;
    170   pthread_create_result = pthread_create(&argument.thread_ids[0], NULL,
    171                                          AdvancedIncrementerThread,
    172                                          &argument);
    173   ASSERT_EQ(0, pthread_create_result);
    174   // Wait for several threads to spawn before proceeding.
    175   while (__sync_fetch_and_add(&argument.thread_index, 0) < kStopWorldAfter)
    176     sched_yield();
    177   StopTheWorld(&AdvancedCallback, &argument);
    178   EXPECT_TRUE(argument.callback_executed);
    179   EXPECT_TRUE(argument.threads_stopped);
    180 
    181   // Wait for all threads to spawn before we start terminating them.
    182   while (__sync_fetch_and_add(&argument.thread_index, 0) < kThreadCount)
    183     sched_yield();
    184   ASSERT_FALSE(argument.fatal_error); // a pthread_create has failed
    185   // Signal the threads to terminate.
    186   pthread_mutex_unlock(&advanced_incrementer_thread_exit_mutex);
    187   for (uptr i = 0; i < kThreadCount; i++)
    188     ASSERT_EQ(0, pthread_join(argument.thread_ids[i], NULL));
    189   pthread_mutex_destroy(&advanced_incrementer_thread_exit_mutex);
    190 }
    191 
    192 static void SegvCallback(const SuspendedThreadsList &suspended_threads_list,
    193                          void *argument) {
    194   *(volatile int*)0x1234 = 0;
    195 }
    196 
    197 TEST(StopTheWorld, SegvInCallback) {
    198   // Test that tracer thread catches SIGSEGV.
    199   StopTheWorld(&SegvCallback, NULL);
    200 }
    201 
    202 }  // namespace __sanitizer
    203 
    204 #endif  // SANITIZER_LINUX && defined(__x86_64__)
    205