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