1 //===-- tsan_clock_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 // This file is a part of ThreadSanitizer (TSan), a race detector. 11 // 12 //===----------------------------------------------------------------------===// 13 #include "tsan_clock.h" 14 #include "tsan_rtl.h" 15 #include "gtest/gtest.h" 16 #include <sys/time.h> 17 #include <time.h> 18 19 namespace __tsan { 20 21 ClockCache cache; 22 23 TEST(Clock, VectorBasic) { 24 ThreadClock clk(0); 25 ASSERT_EQ(clk.size(), 1U); 26 clk.tick(); 27 ASSERT_EQ(clk.size(), 1U); 28 ASSERT_EQ(clk.get(0), 1U); 29 clk.set(3, clk.get(3) + 1); 30 ASSERT_EQ(clk.size(), 4U); 31 ASSERT_EQ(clk.get(0), 1U); 32 ASSERT_EQ(clk.get(1), 0U); 33 ASSERT_EQ(clk.get(2), 0U); 34 ASSERT_EQ(clk.get(3), 1U); 35 clk.set(3, clk.get(3) + 1); 36 ASSERT_EQ(clk.get(3), 2U); 37 } 38 39 TEST(Clock, ChunkedBasic) { 40 ThreadClock vector(0); 41 SyncClock chunked; 42 ASSERT_EQ(vector.size(), 1U); 43 ASSERT_EQ(chunked.size(), 0U); 44 vector.acquire(&cache, &chunked); 45 ASSERT_EQ(vector.size(), 1U); 46 ASSERT_EQ(chunked.size(), 0U); 47 vector.release(&cache, &chunked); 48 ASSERT_EQ(vector.size(), 1U); 49 ASSERT_EQ(chunked.size(), 1U); 50 vector.acq_rel(&cache, &chunked); 51 ASSERT_EQ(vector.size(), 1U); 52 ASSERT_EQ(chunked.size(), 1U); 53 chunked.Reset(&cache); 54 } 55 56 TEST(Clock, AcquireRelease) { 57 ThreadClock vector1(100); 58 vector1.tick(); 59 SyncClock chunked; 60 vector1.release(&cache, &chunked); 61 ASSERT_EQ(chunked.size(), 101U); 62 ThreadClock vector2(0); 63 vector2.acquire(&cache, &chunked); 64 ASSERT_EQ(vector2.size(), 101U); 65 ASSERT_EQ(vector2.get(0), 0U); 66 ASSERT_EQ(vector2.get(1), 0U); 67 ASSERT_EQ(vector2.get(99), 0U); 68 ASSERT_EQ(vector2.get(100), 1U); 69 chunked.Reset(&cache); 70 } 71 72 TEST(Clock, RepeatedAcquire) { 73 ThreadClock thr1(1); 74 thr1.tick(); 75 ThreadClock thr2(2); 76 thr2.tick(); 77 78 SyncClock sync; 79 thr1.ReleaseStore(&cache, &sync); 80 81 thr2.acquire(&cache, &sync); 82 thr2.acquire(&cache, &sync); 83 84 sync.Reset(&cache); 85 } 86 87 TEST(Clock, ManyThreads) { 88 SyncClock chunked; 89 for (unsigned i = 0; i < 100; i++) { 90 ThreadClock vector(0); 91 vector.tick(); 92 vector.set(i, 1); 93 vector.release(&cache, &chunked); 94 ASSERT_EQ(i + 1, chunked.size()); 95 vector.acquire(&cache, &chunked); 96 ASSERT_EQ(i + 1, vector.size()); 97 } 98 99 for (unsigned i = 0; i < 100; i++) 100 ASSERT_EQ(1U, chunked.get(i)); 101 102 ThreadClock vector(1); 103 vector.acquire(&cache, &chunked); 104 ASSERT_EQ(100U, vector.size()); 105 for (unsigned i = 0; i < 100; i++) 106 ASSERT_EQ(1U, vector.get(i)); 107 108 chunked.Reset(&cache); 109 } 110 111 TEST(Clock, DifferentSizes) { 112 { 113 ThreadClock vector1(10); 114 vector1.tick(); 115 ThreadClock vector2(20); 116 vector2.tick(); 117 { 118 SyncClock chunked; 119 vector1.release(&cache, &chunked); 120 ASSERT_EQ(chunked.size(), 11U); 121 vector2.release(&cache, &chunked); 122 ASSERT_EQ(chunked.size(), 21U); 123 chunked.Reset(&cache); 124 } 125 { 126 SyncClock chunked; 127 vector2.release(&cache, &chunked); 128 ASSERT_EQ(chunked.size(), 21U); 129 vector1.release(&cache, &chunked); 130 ASSERT_EQ(chunked.size(), 21U); 131 chunked.Reset(&cache); 132 } 133 { 134 SyncClock chunked; 135 vector1.release(&cache, &chunked); 136 vector2.acquire(&cache, &chunked); 137 ASSERT_EQ(vector2.size(), 21U); 138 chunked.Reset(&cache); 139 } 140 { 141 SyncClock chunked; 142 vector2.release(&cache, &chunked); 143 vector1.acquire(&cache, &chunked); 144 ASSERT_EQ(vector1.size(), 21U); 145 chunked.Reset(&cache); 146 } 147 } 148 } 149 150 TEST(Clock, Growth) { 151 { 152 ThreadClock vector(10); 153 vector.tick(); 154 vector.set(5, 42); 155 SyncClock sync; 156 vector.release(&cache, &sync); 157 ASSERT_EQ(sync.size(), 11U); 158 ASSERT_EQ(sync.get(0), 0ULL); 159 ASSERT_EQ(sync.get(1), 0ULL); 160 ASSERT_EQ(sync.get(5), 42ULL); 161 ASSERT_EQ(sync.get(9), 0ULL); 162 ASSERT_EQ(sync.get(10), 1ULL); 163 sync.Reset(&cache); 164 } 165 { 166 ThreadClock vector1(10); 167 vector1.tick(); 168 ThreadClock vector2(20); 169 vector2.tick(); 170 SyncClock sync; 171 vector1.release(&cache, &sync); 172 vector2.release(&cache, &sync); 173 ASSERT_EQ(sync.size(), 21U); 174 ASSERT_EQ(sync.get(0), 0ULL); 175 ASSERT_EQ(sync.get(10), 1ULL); 176 ASSERT_EQ(sync.get(19), 0ULL); 177 ASSERT_EQ(sync.get(20), 1ULL); 178 sync.Reset(&cache); 179 } 180 { 181 ThreadClock vector(100); 182 vector.tick(); 183 vector.set(5, 42); 184 vector.set(90, 84); 185 SyncClock sync; 186 vector.release(&cache, &sync); 187 ASSERT_EQ(sync.size(), 101U); 188 ASSERT_EQ(sync.get(0), 0ULL); 189 ASSERT_EQ(sync.get(1), 0ULL); 190 ASSERT_EQ(sync.get(5), 42ULL); 191 ASSERT_EQ(sync.get(60), 0ULL); 192 ASSERT_EQ(sync.get(70), 0ULL); 193 ASSERT_EQ(sync.get(90), 84ULL); 194 ASSERT_EQ(sync.get(99), 0ULL); 195 ASSERT_EQ(sync.get(100), 1ULL); 196 sync.Reset(&cache); 197 } 198 { 199 ThreadClock vector1(10); 200 vector1.tick(); 201 ThreadClock vector2(100); 202 vector2.tick(); 203 SyncClock sync; 204 vector1.release(&cache, &sync); 205 vector2.release(&cache, &sync); 206 ASSERT_EQ(sync.size(), 101U); 207 ASSERT_EQ(sync.get(0), 0ULL); 208 ASSERT_EQ(sync.get(10), 1ULL); 209 ASSERT_EQ(sync.get(99), 0ULL); 210 ASSERT_EQ(sync.get(100), 1ULL); 211 sync.Reset(&cache); 212 } 213 } 214 215 const uptr kThreads = 4; 216 const uptr kClocks = 4; 217 218 // SimpleSyncClock and SimpleThreadClock implement the same thing as 219 // SyncClock and ThreadClock, but in a very simple way. 220 struct SimpleSyncClock { 221 u64 clock[kThreads]; 222 uptr size; 223 224 SimpleSyncClock() { 225 Reset(); 226 } 227 228 void Reset() { 229 size = 0; 230 for (uptr i = 0; i < kThreads; i++) 231 clock[i] = 0; 232 } 233 234 bool verify(const SyncClock *other) const { 235 for (uptr i = 0; i < min(size, other->size()); i++) { 236 if (clock[i] != other->get(i)) 237 return false; 238 } 239 for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { 240 if (i < size && clock[i] != 0) 241 return false; 242 if (i < other->size() && other->get(i) != 0) 243 return false; 244 } 245 return true; 246 } 247 }; 248 249 struct SimpleThreadClock { 250 u64 clock[kThreads]; 251 uptr size; 252 unsigned tid; 253 254 explicit SimpleThreadClock(unsigned tid) { 255 this->tid = tid; 256 size = tid + 1; 257 for (uptr i = 0; i < kThreads; i++) 258 clock[i] = 0; 259 } 260 261 void tick() { 262 clock[tid]++; 263 } 264 265 void acquire(const SimpleSyncClock *src) { 266 if (size < src->size) 267 size = src->size; 268 for (uptr i = 0; i < kThreads; i++) 269 clock[i] = max(clock[i], src->clock[i]); 270 } 271 272 void release(SimpleSyncClock *dst) const { 273 if (dst->size < size) 274 dst->size = size; 275 for (uptr i = 0; i < kThreads; i++) 276 dst->clock[i] = max(dst->clock[i], clock[i]); 277 } 278 279 void acq_rel(SimpleSyncClock *dst) { 280 acquire(dst); 281 release(dst); 282 } 283 284 void ReleaseStore(SimpleSyncClock *dst) const { 285 if (dst->size < size) 286 dst->size = size; 287 for (uptr i = 0; i < kThreads; i++) 288 dst->clock[i] = clock[i]; 289 } 290 291 bool verify(const ThreadClock *other) const { 292 for (uptr i = 0; i < min(size, other->size()); i++) { 293 if (clock[i] != other->get(i)) 294 return false; 295 } 296 for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { 297 if (i < size && clock[i] != 0) 298 return false; 299 if (i < other->size() && other->get(i) != 0) 300 return false; 301 } 302 return true; 303 } 304 }; 305 306 static bool ClockFuzzer(bool printing) { 307 // Create kThreads thread clocks. 308 SimpleThreadClock *thr0[kThreads]; 309 ThreadClock *thr1[kThreads]; 310 unsigned reused[kThreads]; 311 for (unsigned i = 0; i < kThreads; i++) { 312 reused[i] = 0; 313 thr0[i] = new SimpleThreadClock(i); 314 thr1[i] = new ThreadClock(i, reused[i]); 315 } 316 317 // Create kClocks sync clocks. 318 SimpleSyncClock *sync0[kClocks]; 319 SyncClock *sync1[kClocks]; 320 for (unsigned i = 0; i < kClocks; i++) { 321 sync0[i] = new SimpleSyncClock(); 322 sync1[i] = new SyncClock(); 323 } 324 325 // Do N random operations (acquire, release, etc) and compare results 326 // for SimpleThread/SyncClock and real Thread/SyncClock. 327 for (int i = 0; i < 10000; i++) { 328 unsigned tid = rand() % kThreads; 329 unsigned cid = rand() % kClocks; 330 thr0[tid]->tick(); 331 thr1[tid]->tick(); 332 333 switch (rand() % 6) { 334 case 0: 335 if (printing) 336 printf("acquire thr%d <- clk%d\n", tid, cid); 337 thr0[tid]->acquire(sync0[cid]); 338 thr1[tid]->acquire(&cache, sync1[cid]); 339 break; 340 case 1: 341 if (printing) 342 printf("release thr%d -> clk%d\n", tid, cid); 343 thr0[tid]->release(sync0[cid]); 344 thr1[tid]->release(&cache, sync1[cid]); 345 break; 346 case 2: 347 if (printing) 348 printf("acq_rel thr%d <> clk%d\n", tid, cid); 349 thr0[tid]->acq_rel(sync0[cid]); 350 thr1[tid]->acq_rel(&cache, sync1[cid]); 351 break; 352 case 3: 353 if (printing) 354 printf("rel_str thr%d >> clk%d\n", tid, cid); 355 thr0[tid]->ReleaseStore(sync0[cid]); 356 thr1[tid]->ReleaseStore(&cache, sync1[cid]); 357 break; 358 case 4: 359 if (printing) 360 printf("reset clk%d\n", cid); 361 sync0[cid]->Reset(); 362 sync1[cid]->Reset(&cache); 363 break; 364 case 5: 365 if (printing) 366 printf("reset thr%d\n", tid); 367 u64 epoch = thr0[tid]->clock[tid] + 1; 368 reused[tid]++; 369 delete thr0[tid]; 370 thr0[tid] = new SimpleThreadClock(tid); 371 thr0[tid]->clock[tid] = epoch; 372 delete thr1[tid]; 373 thr1[tid] = new ThreadClock(tid, reused[tid]); 374 thr1[tid]->set(epoch); 375 break; 376 } 377 378 if (printing) { 379 for (unsigned i = 0; i < kThreads; i++) { 380 printf("thr%d: ", i); 381 thr1[i]->DebugDump(printf); 382 printf("\n"); 383 } 384 for (unsigned i = 0; i < kClocks; i++) { 385 printf("clk%d: ", i); 386 sync1[i]->DebugDump(printf); 387 printf("\n"); 388 } 389 390 printf("\n"); 391 } 392 393 if (!thr0[tid]->verify(thr1[tid]) || !sync0[cid]->verify(sync1[cid])) { 394 if (!printing) 395 return false; 396 printf("differs with model:\n"); 397 for (unsigned i = 0; i < kThreads; i++) { 398 printf("thr%d: clock=[", i); 399 for (uptr j = 0; j < thr0[i]->size; j++) 400 printf("%s%llu", j == 0 ? "" : ",", thr0[i]->clock[j]); 401 printf("]\n"); 402 } 403 for (unsigned i = 0; i < kClocks; i++) { 404 printf("clk%d: clock=[", i); 405 for (uptr j = 0; j < sync0[i]->size; j++) 406 printf("%s%llu", j == 0 ? "" : ",", sync0[i]->clock[j]); 407 printf("]\n"); 408 } 409 return false; 410 } 411 } 412 413 for (unsigned i = 0; i < kClocks; i++) { 414 sync1[i]->Reset(&cache); 415 } 416 return true; 417 } 418 419 TEST(Clock, Fuzzer) { 420 struct timeval tv; 421 gettimeofday(&tv, NULL); 422 int seed = tv.tv_sec + tv.tv_usec; 423 printf("seed=%d\n", seed); 424 srand(seed); 425 if (!ClockFuzzer(false)) { 426 // Redo the test with the same seed, but logging operations. 427 srand(seed); 428 ClockFuzzer(true); 429 ASSERT_TRUE(false); 430 } 431 } 432 433 } // namespace __tsan 434