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