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;
CallbackArgument__sanitizer::CallbackArgument34 CallbackArgument()
35 : counter(0),
36 threads_stopped(false),
37 callback_executed(false) {}
38 };
39
IncrementerThread(void * argument)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.
Callback(const SuspendedThreadsList & suspended_threads_list,void * argument)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
TEST(StopTheWorld,SuspendThreadsSimple)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;
AdvancedCallbackArgument__sanitizer::AdvancedCallbackArgument104 AdvancedCallbackArgument()
105 : thread_index(0),
106 threads_stopped(false),
107 callback_executed(false),
108 fatal_error(false) {}
109 };
110
AdvancedIncrementerThread(void * argument)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
AdvancedCallback(const SuspendedThreadsList & suspended_threads_list,void * argument)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
TEST(StopTheWorld,SuspendThreadsAdvanced)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
SegvCallback(const SuspendedThreadsList & suspended_threads_list,void * argument)192 static void SegvCallback(const SuspendedThreadsList &suspended_threads_list,
193 void *argument) {
194 *(volatile int*)0x1234 = 0;
195 }
196
TEST(StopTheWorld,SegvInCallback)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