From 5de29a4b0e5af8cae312e01c95d24cfe0e1c51c3 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 1 Aug 2019 14:26:37 +0000 Subject: compiler-rt: Rename .cc file in lib/tsan/tests/{rtl,unit} to .cpp Like r367463, but for tsan/tests/{rtl,unit}. llvm-svn: 367566 --- compiler-rt/lib/tsan/rtl/tsan_clock.cpp | 2 +- compiler-rt/lib/tsan/tests/rtl/CMakeLists.txt | 22 +- compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc | 104 ----- compiler-rt/lib/tsan/tests/rtl/tsan_bench.cpp | 104 +++++ compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc | 232 ---------- compiler-rt/lib/tsan/tests/rtl/tsan_mop.cpp | 232 ++++++++++ compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc | 220 --------- compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cpp | 220 +++++++++ compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc | 154 ------- compiler-rt/lib/tsan/tests/rtl/tsan_posix.cpp | 154 +++++++ compiler-rt/lib/tsan/tests/rtl/tsan_string.cc | 81 ---- compiler-rt/lib/tsan/tests/rtl/tsan_string.cpp | 81 ++++ compiler-rt/lib/tsan/tests/rtl/tsan_test.cc | 65 --- compiler-rt/lib/tsan/tests/rtl/tsan_test.cpp | 65 +++ .../lib/tsan/tests/rtl/tsan_test_util_posix.cc | 477 -------------------- .../lib/tsan/tests/rtl/tsan_test_util_posix.cpp | 477 ++++++++++++++++++++ compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc | 58 --- compiler-rt/lib/tsan/tests/rtl/tsan_thread.cpp | 58 +++ compiler-rt/lib/tsan/tests/unit/CMakeLists.txt | 17 +- compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc | 493 --------------------- .../lib/tsan/tests/unit/tsan_clock_test.cpp | 493 +++++++++++++++++++++ .../lib/tsan/tests/unit/tsan_dense_alloc_test.cc | 54 --- .../lib/tsan/tests/unit/tsan_dense_alloc_test.cpp | 54 +++ compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc | 173 -------- .../lib/tsan/tests/unit/tsan_flags_test.cpp | 173 ++++++++ compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc | 196 -------- compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cpp | 196 ++++++++ compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc | 125 ------ .../lib/tsan/tests/unit/tsan_mutex_test.cpp | 125 ++++++ .../lib/tsan/tests/unit/tsan_mutexset_test.cc | 126 ------ .../lib/tsan/tests/unit/tsan_mutexset_test.cpp | 126 ++++++ .../lib/tsan/tests/unit/tsan_shadow_test.cc | 77 ---- .../lib/tsan/tests/unit/tsan_shadow_test.cpp | 77 ++++ compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc | 94 ---- .../lib/tsan/tests/unit/tsan_stack_test.cpp | 94 ++++ compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc | 122 ----- compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cpp | 122 +++++ .../lib/tsan/tests/unit/tsan_unit_test_main.cc | 24 - .../lib/tsan/tests/unit/tsan_unit_test_main.cpp | 24 + 39 files changed, 2898 insertions(+), 2893 deletions(-) delete mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc create mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_bench.cpp delete mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc create mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_mop.cpp delete mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc create mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cpp delete mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc create mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_posix.cpp delete mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_string.cc create mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_string.cpp delete mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_test.cc create mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_test.cpp delete mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cc create mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cpp delete mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc create mode 100644 compiler-rt/lib/tsan/tests/rtl/tsan_thread.cpp delete mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc create mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cpp delete mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cc create mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cpp delete mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc create mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cpp delete mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc create mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cpp delete mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc create mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cpp delete mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc create mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cpp delete mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cc create mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cpp delete mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc create mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cpp delete mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc create mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cpp delete mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cc create mode 100644 compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cpp (limited to 'compiler-rt/lib/tsan') diff --git a/compiler-rt/lib/tsan/rtl/tsan_clock.cpp b/compiler-rt/lib/tsan/rtl/tsan_clock.cpp index 0db3737d696..4b7aa0653da 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_clock.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_clock.cpp @@ -40,7 +40,7 @@ // release(dst); // } // -// Conformance to this model is extensively verified in tsan_clock_test.cc. +// Conformance to this model is extensively verified in tsan_clock_test.cpp. // However, the implementation is significantly more complex. The complexity // allows to implement important classes of use cases in O(1) instead of O(N). // diff --git a/compiler-rt/lib/tsan/tests/rtl/CMakeLists.txt b/compiler-rt/lib/tsan/tests/rtl/CMakeLists.txt index a34f08ea965..92cec3ee41b 100644 --- a/compiler-rt/lib/tsan/tests/rtl/CMakeLists.txt +++ b/compiler-rt/lib/tsan/tests/rtl/CMakeLists.txt @@ -1,18 +1,22 @@ set(TSAN_RTL_TEST_SOURCES - tsan_bench.cc - tsan_mop.cc - tsan_mutex.cc - tsan_posix.cc - tsan_string.cc - tsan_test.cc - tsan_thread.cc) + tsan_bench.cpp + tsan_mop.cpp + tsan_mutex.cpp + tsan_posix.cpp + tsan_string.cpp + tsan_test.cpp + tsan_thread.cpp + ) if(UNIX) - list(APPEND TSAN_RTL_TEST_SOURCES tsan_test_util_posix.cc) + list(APPEND TSAN_RTL_TEST_SOURCES + tsan_test_util_posix.cpp + ) endif() set(TSAN_RTL_TEST_HEADERS - tsan_test_util.h) + tsan_test_util.h + ) add_tsan_unittest(TsanRtlTest SOURCES ${TSAN_RTL_TEST_SOURCES} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc b/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc deleted file mode 100644 index 0135101142f..00000000000 --- a/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc +++ /dev/null @@ -1,104 +0,0 @@ -//===-- tsan_bench.cc -----------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_test_util.h" -#include "tsan_interface.h" -#include "tsan_defs.h" -#include "gtest/gtest.h" -#include - -const int kSize = 128; -const int kRepeat = 2*1024*1024; - -void noinstr(void *p) {} - -template -static void Benchmark() { - volatile T data[kSize]; - for (int i = 0; i < kRepeat; i++) { - for (int j = 0; j < kSize; j++) { - __tsan_mop((void*)&data[j]); - data[j]++; - } - } -} - -TEST(DISABLED_BENCH, Mop1) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop1Read) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop1Write) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop2) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop2Read) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop2Write) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop4) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop4Read) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop4Write) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop8) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop8Read) { - Benchmark(); -} - -TEST(DISABLED_BENCH, Mop8Write) { - Benchmark(); -} - -TEST(DISABLED_BENCH, FuncCall) { - for (int i = 0; i < kRepeat; i++) { - for (int j = 0; j < kSize; j++) - __tsan_func_entry((void*)(uintptr_t)j); - for (int j = 0; j < kSize; j++) - __tsan_func_exit(); - } -} - -TEST(DISABLED_BENCH, MutexLocal) { - Mutex m; - ScopedThread().Create(m); - for (int i = 0; i < 50; i++) { - ScopedThread t; - t.Lock(m); - t.Unlock(m); - } - for (int i = 0; i < 16*1024*1024; i++) { - m.Lock(); - m.Unlock(); - } - ScopedThread().Destroy(m); -} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cpp b/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cpp new file mode 100644 index 00000000000..36ca9b5e035 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cpp @@ -0,0 +1,104 @@ +//===-- tsan_bench.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_test_util.h" +#include "tsan_interface.h" +#include "tsan_defs.h" +#include "gtest/gtest.h" +#include + +const int kSize = 128; +const int kRepeat = 2*1024*1024; + +void noinstr(void *p) {} + +template +static void Benchmark() { + volatile T data[kSize]; + for (int i = 0; i < kRepeat; i++) { + for (int j = 0; j < kSize; j++) { + __tsan_mop((void*)&data[j]); + data[j]++; + } + } +} + +TEST(DISABLED_BENCH, Mop1) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop1Read) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop1Write) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop2) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop2Read) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop2Write) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop4) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop4Read) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop4Write) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop8) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop8Read) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop8Write) { + Benchmark(); +} + +TEST(DISABLED_BENCH, FuncCall) { + for (int i = 0; i < kRepeat; i++) { + for (int j = 0; j < kSize; j++) + __tsan_func_entry((void*)(uintptr_t)j); + for (int j = 0; j < kSize; j++) + __tsan_func_exit(); + } +} + +TEST(DISABLED_BENCH, MutexLocal) { + Mutex m; + ScopedThread().Create(m); + for (int i = 0; i < 50; i++) { + ScopedThread t; + t.Lock(m); + t.Unlock(m); + } + for (int i = 0; i < 16*1024*1024; i++) { + m.Lock(); + m.Unlock(); + } + ScopedThread().Destroy(m); +} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc b/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc deleted file mode 100644 index a5b0bdda3cf..00000000000 --- a/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc +++ /dev/null @@ -1,232 +0,0 @@ -//===-- tsan_mop.cc -------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_interface.h" -#include "tsan_test_util.h" -#include "gtest/gtest.h" -#include -#include - -TEST(ThreadSanitizer, SimpleWrite) { - ScopedThread t; - MemLoc l; - t.Write1(l); -} - -TEST(ThreadSanitizer, SimpleWriteWrite) { - ScopedThread t1, t2; - MemLoc l1, l2; - t1.Write1(l1); - t2.Write1(l2); -} - -TEST(ThreadSanitizer, WriteWriteRace) { - ScopedThread t1, t2; - MemLoc l; - t1.Write1(l); - t2.Write1(l, true); -} - -TEST(ThreadSanitizer, ReadWriteRace) { - ScopedThread t1, t2; - MemLoc l; - t1.Read1(l); - t2.Write1(l, true); -} - -TEST(ThreadSanitizer, WriteReadRace) { - ScopedThread t1, t2; - MemLoc l; - t1.Write1(l); - t2.Read1(l, true); -} - -TEST(ThreadSanitizer, ReadReadNoRace) { - ScopedThread t1, t2; - MemLoc l; - t1.Read1(l); - t2.Read1(l); -} - -TEST(ThreadSanitizer, WriteThenRead) { - MemLoc l; - ScopedThread t1, t2; - t1.Write1(l); - t1.Read1(l); - t2.Read1(l, true); -} - -TEST(ThreadSanitizer, WriteThenLockedRead) { - Mutex m(Mutex::RW); - MainThread t0; - t0.Create(m); - MemLoc l; - { - ScopedThread t1, t2; - - t1.Write8(l); - - t1.Lock(m); - t1.Read8(l); - t1.Unlock(m); - - t2.Read8(l, true); - } - t0.Destroy(m); -} - -TEST(ThreadSanitizer, LockedWriteThenRead) { - Mutex m(Mutex::RW); - MainThread t0; - t0.Create(m); - MemLoc l; - { - ScopedThread t1, t2; - - t1.Lock(m); - t1.Write8(l); - t1.Unlock(m); - - t1.Read8(l); - - t2.Read8(l, true); - } - t0.Destroy(m); -} - - -TEST(ThreadSanitizer, RaceWithOffset) { - ScopedThread t1, t2; - { - MemLoc l; - t1.Access(l.loc(), true, 8, false); - t2.Access((char*)l.loc() + 4, true, 4, true); - } - { - MemLoc l; - t1.Access(l.loc(), true, 8, false); - t2.Access((char*)l.loc() + 7, true, 1, true); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 4, true, 4, false); - t2.Access((char*)l.loc() + 4, true, 2, true); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 4, true, 4, false); - t2.Access((char*)l.loc() + 6, true, 2, true); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 3, true, 2, false); - t2.Access((char*)l.loc() + 4, true, 1, true); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 1, true, 8, false); - t2.Access((char*)l.loc() + 3, true, 1, true); - } -} - -TEST(ThreadSanitizer, RaceWithOffset2) { - ScopedThread t1, t2; - { - MemLoc l; - t1.Access((char*)l.loc(), true, 4, false); - t2.Access((char*)l.loc() + 2, true, 1, true); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 2, true, 1, false); - t2.Access((char*)l.loc(), true, 4, true); - } -} - -TEST(ThreadSanitizer, NoRaceWithOffset) { - ScopedThread t1, t2; - { - MemLoc l; - t1.Access(l.loc(), true, 4, false); - t2.Access((char*)l.loc() + 4, true, 4, false); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 3, true, 2, false); - t2.Access((char*)l.loc() + 1, true, 2, false); - t2.Access((char*)l.loc() + 5, true, 2, false); - } -} - -TEST(ThreadSanitizer, RaceWithDeadThread) { - MemLoc l; - ScopedThread t; - ScopedThread().Write1(l); - t.Write1(l, true); -} - -TEST(ThreadSanitizer, BenignRaceOnVptr) { - void *vptr_storage; - MemLoc vptr(&vptr_storage), val; - vptr_storage = val.loc(); - ScopedThread t1, t2; - t1.VptrUpdate(vptr, val); - t2.Read8(vptr); -} - -TEST(ThreadSanitizer, HarmfulRaceOnVptr) { - void *vptr_storage; - MemLoc vptr(&vptr_storage), val1, val2; - vptr_storage = val1.loc(); - ScopedThread t1, t2; - t1.VptrUpdate(vptr, val2); - t2.Read8(vptr, true); -} - -static void foo() { - volatile int x = 42; - int x2 = x; - (void)x2; -} - -static void bar() { - volatile int x = 43; - int x2 = x; - (void)x2; -} - -TEST(ThreadSanitizer, ReportDeadThread) { - MemLoc l; - ScopedThread t1; - { - ScopedThread t2; - t2.Call(&foo); - t2.Call(&bar); - t2.Write1(l); - } - t1.Write1(l, true); -} - -struct ClassWithStatic { - static int Data[4]; -}; - -int ClassWithStatic::Data[4]; - -static void foobarbaz() {} - -TEST(ThreadSanitizer, ReportRace) { - ScopedThread t1; - MainThread().Access(&ClassWithStatic::Data, true, 4, false); - t1.Call(&foobarbaz); - t1.Access(&ClassWithStatic::Data, true, 2, true); - t1.Return(); -} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cpp b/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cpp new file mode 100644 index 00000000000..1825c96d743 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cpp @@ -0,0 +1,232 @@ +//===-- tsan_mop.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_interface.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include +#include + +TEST(ThreadSanitizer, SimpleWrite) { + ScopedThread t; + MemLoc l; + t.Write1(l); +} + +TEST(ThreadSanitizer, SimpleWriteWrite) { + ScopedThread t1, t2; + MemLoc l1, l2; + t1.Write1(l1); + t2.Write1(l2); +} + +TEST(ThreadSanitizer, WriteWriteRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Write1(l); + t2.Write1(l, true); +} + +TEST(ThreadSanitizer, ReadWriteRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Read1(l); + t2.Write1(l, true); +} + +TEST(ThreadSanitizer, WriteReadRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Write1(l); + t2.Read1(l, true); +} + +TEST(ThreadSanitizer, ReadReadNoRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Read1(l); + t2.Read1(l); +} + +TEST(ThreadSanitizer, WriteThenRead) { + MemLoc l; + ScopedThread t1, t2; + t1.Write1(l); + t1.Read1(l); + t2.Read1(l, true); +} + +TEST(ThreadSanitizer, WriteThenLockedRead) { + Mutex m(Mutex::RW); + MainThread t0; + t0.Create(m); + MemLoc l; + { + ScopedThread t1, t2; + + t1.Write8(l); + + t1.Lock(m); + t1.Read8(l); + t1.Unlock(m); + + t2.Read8(l, true); + } + t0.Destroy(m); +} + +TEST(ThreadSanitizer, LockedWriteThenRead) { + Mutex m(Mutex::RW); + MainThread t0; + t0.Create(m); + MemLoc l; + { + ScopedThread t1, t2; + + t1.Lock(m); + t1.Write8(l); + t1.Unlock(m); + + t1.Read8(l); + + t2.Read8(l, true); + } + t0.Destroy(m); +} + + +TEST(ThreadSanitizer, RaceWithOffset) { + ScopedThread t1, t2; + { + MemLoc l; + t1.Access(l.loc(), true, 8, false); + t2.Access((char*)l.loc() + 4, true, 4, true); + } + { + MemLoc l; + t1.Access(l.loc(), true, 8, false); + t2.Access((char*)l.loc() + 7, true, 1, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 4, true, 4, false); + t2.Access((char*)l.loc() + 4, true, 2, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 4, true, 4, false); + t2.Access((char*)l.loc() + 6, true, 2, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 3, true, 2, false); + t2.Access((char*)l.loc() + 4, true, 1, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 1, true, 8, false); + t2.Access((char*)l.loc() + 3, true, 1, true); + } +} + +TEST(ThreadSanitizer, RaceWithOffset2) { + ScopedThread t1, t2; + { + MemLoc l; + t1.Access((char*)l.loc(), true, 4, false); + t2.Access((char*)l.loc() + 2, true, 1, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 2, true, 1, false); + t2.Access((char*)l.loc(), true, 4, true); + } +} + +TEST(ThreadSanitizer, NoRaceWithOffset) { + ScopedThread t1, t2; + { + MemLoc l; + t1.Access(l.loc(), true, 4, false); + t2.Access((char*)l.loc() + 4, true, 4, false); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 3, true, 2, false); + t2.Access((char*)l.loc() + 1, true, 2, false); + t2.Access((char*)l.loc() + 5, true, 2, false); + } +} + +TEST(ThreadSanitizer, RaceWithDeadThread) { + MemLoc l; + ScopedThread t; + ScopedThread().Write1(l); + t.Write1(l, true); +} + +TEST(ThreadSanitizer, BenignRaceOnVptr) { + void *vptr_storage; + MemLoc vptr(&vptr_storage), val; + vptr_storage = val.loc(); + ScopedThread t1, t2; + t1.VptrUpdate(vptr, val); + t2.Read8(vptr); +} + +TEST(ThreadSanitizer, HarmfulRaceOnVptr) { + void *vptr_storage; + MemLoc vptr(&vptr_storage), val1, val2; + vptr_storage = val1.loc(); + ScopedThread t1, t2; + t1.VptrUpdate(vptr, val2); + t2.Read8(vptr, true); +} + +static void foo() { + volatile int x = 42; + int x2 = x; + (void)x2; +} + +static void bar() { + volatile int x = 43; + int x2 = x; + (void)x2; +} + +TEST(ThreadSanitizer, ReportDeadThread) { + MemLoc l; + ScopedThread t1; + { + ScopedThread t2; + t2.Call(&foo); + t2.Call(&bar); + t2.Write1(l); + } + t1.Write1(l, true); +} + +struct ClassWithStatic { + static int Data[4]; +}; + +int ClassWithStatic::Data[4]; + +static void foobarbaz() {} + +TEST(ThreadSanitizer, ReportRace) { + ScopedThread t1; + MainThread().Access(&ClassWithStatic::Data, true, 4, false); + t1.Call(&foobarbaz); + t1.Access(&ClassWithStatic::Data, true, 2, true); + t1.Return(); +} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc b/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc deleted file mode 100644 index af12e20b148..00000000000 --- a/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc +++ /dev/null @@ -1,220 +0,0 @@ -//===-- tsan_mutex.cc -----------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_atomic.h" -#include "tsan_interface.h" -#include "tsan_interface_ann.h" -#include "tsan_test_util.h" -#include "gtest/gtest.h" -#include - -namespace __tsan { - -TEST(ThreadSanitizer, BasicMutex) { - ScopedThread t; - Mutex m; - t.Create(m); - - t.Lock(m); - t.Unlock(m); - - CHECK(t.TryLock(m)); - t.Unlock(m); - - t.Lock(m); - CHECK(!t.TryLock(m)); - t.Unlock(m); - - t.Destroy(m); -} - -TEST(ThreadSanitizer, BasicSpinMutex) { - ScopedThread t; - Mutex m(Mutex::Spin); - t.Create(m); - - t.Lock(m); - t.Unlock(m); - - CHECK(t.TryLock(m)); - t.Unlock(m); - - t.Lock(m); - CHECK(!t.TryLock(m)); - t.Unlock(m); - - t.Destroy(m); -} - -TEST(ThreadSanitizer, BasicRwMutex) { - ScopedThread t; - Mutex m(Mutex::RW); - t.Create(m); - - t.Lock(m); - t.Unlock(m); - - CHECK(t.TryLock(m)); - t.Unlock(m); - - t.Lock(m); - CHECK(!t.TryLock(m)); - t.Unlock(m); - - t.ReadLock(m); - t.ReadUnlock(m); - - CHECK(t.TryReadLock(m)); - t.ReadUnlock(m); - - t.Lock(m); - CHECK(!t.TryReadLock(m)); - t.Unlock(m); - - t.ReadLock(m); - CHECK(!t.TryLock(m)); - t.ReadUnlock(m); - - t.ReadLock(m); - CHECK(t.TryReadLock(m)); - t.ReadUnlock(m); - t.ReadUnlock(m); - - t.Destroy(m); -} - -TEST(ThreadSanitizer, Mutex) { - Mutex m; - MainThread t0; - t0.Create(m); - - ScopedThread t1, t2; - MemLoc l; - t1.Lock(m); - t1.Write1(l); - t1.Unlock(m); - t2.Lock(m); - t2.Write1(l); - t2.Unlock(m); - t2.Destroy(m); -} - -TEST(ThreadSanitizer, SpinMutex) { - Mutex m(Mutex::Spin); - MainThread t0; - t0.Create(m); - - ScopedThread t1, t2; - MemLoc l; - t1.Lock(m); - t1.Write1(l); - t1.Unlock(m); - t2.Lock(m); - t2.Write1(l); - t2.Unlock(m); - t2.Destroy(m); -} - -TEST(ThreadSanitizer, RwMutex) { - Mutex m(Mutex::RW); - MainThread t0; - t0.Create(m); - - ScopedThread t1, t2, t3; - MemLoc l; - t1.Lock(m); - t1.Write1(l); - t1.Unlock(m); - t2.Lock(m); - t2.Write1(l); - t2.Unlock(m); - t1.ReadLock(m); - t3.ReadLock(m); - t1.Read1(l); - t3.Read1(l); - t1.ReadUnlock(m); - t3.ReadUnlock(m); - t2.Lock(m); - t2.Write1(l); - t2.Unlock(m); - t2.Destroy(m); -} - -TEST(ThreadSanitizer, StaticMutex) { - // Emulates statically initialized mutex. - Mutex m; - m.StaticInit(); - { - ScopedThread t1, t2; - t1.Lock(m); - t1.Unlock(m); - t2.Lock(m); - t2.Unlock(m); - } - MainThread().Destroy(m); -} - -static void *singleton_thread(void *param) { - atomic_uintptr_t *singleton = (atomic_uintptr_t *)param; - for (int i = 0; i < 4*1024*1024; i++) { - int *val = (int *)atomic_load(singleton, memory_order_acquire); - __tsan_acquire(singleton); - __tsan_read4(val); - CHECK_EQ(*val, 42); - } - return 0; -} - -TEST(DISABLED_BENCH_ThreadSanitizer, Singleton) { - const int kClockSize = 100; - const int kThreadCount = 8; - - // Puff off thread's clock. - for (int i = 0; i < kClockSize; i++) { - ScopedThread t1; - (void)t1; - } - // Create the singleton. - int val = 42; - __tsan_write4(&val); - atomic_uintptr_t singleton; - __tsan_release(&singleton); - atomic_store(&singleton, (uintptr_t)&val, memory_order_release); - // Create reader threads. - pthread_t threads[kThreadCount]; - for (int t = 0; t < kThreadCount; t++) - pthread_create(&threads[t], 0, singleton_thread, &singleton); - for (int t = 0; t < kThreadCount; t++) - pthread_join(threads[t], 0); -} - -TEST(DISABLED_BENCH_ThreadSanitizer, StopFlag) { - const int kClockSize = 100; - const int kIters = 16*1024*1024; - - // Puff off thread's clock. - for (int i = 0; i < kClockSize; i++) { - ScopedThread t1; - (void)t1; - } - // Create the stop flag. - atomic_uintptr_t flag; - __tsan_release(&flag); - atomic_store(&flag, 0, memory_order_release); - // Read it a lot. - for (int i = 0; i < kIters; i++) { - uptr v = atomic_load(&flag, memory_order_acquire); - __tsan_acquire(&flag); - CHECK_EQ(v, 0); - } -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cpp b/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cpp new file mode 100644 index 00000000000..dae9c94c339 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cpp @@ -0,0 +1,220 @@ +//===-- tsan_mutex.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_atomic.h" +#include "tsan_interface.h" +#include "tsan_interface_ann.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include + +namespace __tsan { + +TEST(ThreadSanitizer, BasicMutex) { + ScopedThread t; + Mutex m; + t.Create(m); + + t.Lock(m); + t.Unlock(m); + + CHECK(t.TryLock(m)); + t.Unlock(m); + + t.Lock(m); + CHECK(!t.TryLock(m)); + t.Unlock(m); + + t.Destroy(m); +} + +TEST(ThreadSanitizer, BasicSpinMutex) { + ScopedThread t; + Mutex m(Mutex::Spin); + t.Create(m); + + t.Lock(m); + t.Unlock(m); + + CHECK(t.TryLock(m)); + t.Unlock(m); + + t.Lock(m); + CHECK(!t.TryLock(m)); + t.Unlock(m); + + t.Destroy(m); +} + +TEST(ThreadSanitizer, BasicRwMutex) { + ScopedThread t; + Mutex m(Mutex::RW); + t.Create(m); + + t.Lock(m); + t.Unlock(m); + + CHECK(t.TryLock(m)); + t.Unlock(m); + + t.Lock(m); + CHECK(!t.TryLock(m)); + t.Unlock(m); + + t.ReadLock(m); + t.ReadUnlock(m); + + CHECK(t.TryReadLock(m)); + t.ReadUnlock(m); + + t.Lock(m); + CHECK(!t.TryReadLock(m)); + t.Unlock(m); + + t.ReadLock(m); + CHECK(!t.TryLock(m)); + t.ReadUnlock(m); + + t.ReadLock(m); + CHECK(t.TryReadLock(m)); + t.ReadUnlock(m); + t.ReadUnlock(m); + + t.Destroy(m); +} + +TEST(ThreadSanitizer, Mutex) { + Mutex m; + MainThread t0; + t0.Create(m); + + ScopedThread t1, t2; + MemLoc l; + t1.Lock(m); + t1.Write1(l); + t1.Unlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t2.Destroy(m); +} + +TEST(ThreadSanitizer, SpinMutex) { + Mutex m(Mutex::Spin); + MainThread t0; + t0.Create(m); + + ScopedThread t1, t2; + MemLoc l; + t1.Lock(m); + t1.Write1(l); + t1.Unlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t2.Destroy(m); +} + +TEST(ThreadSanitizer, RwMutex) { + Mutex m(Mutex::RW); + MainThread t0; + t0.Create(m); + + ScopedThread t1, t2, t3; + MemLoc l; + t1.Lock(m); + t1.Write1(l); + t1.Unlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t1.ReadLock(m); + t3.ReadLock(m); + t1.Read1(l); + t3.Read1(l); + t1.ReadUnlock(m); + t3.ReadUnlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t2.Destroy(m); +} + +TEST(ThreadSanitizer, StaticMutex) { + // Emulates statically initialized mutex. + Mutex m; + m.StaticInit(); + { + ScopedThread t1, t2; + t1.Lock(m); + t1.Unlock(m); + t2.Lock(m); + t2.Unlock(m); + } + MainThread().Destroy(m); +} + +static void *singleton_thread(void *param) { + atomic_uintptr_t *singleton = (atomic_uintptr_t *)param; + for (int i = 0; i < 4*1024*1024; i++) { + int *val = (int *)atomic_load(singleton, memory_order_acquire); + __tsan_acquire(singleton); + __tsan_read4(val); + CHECK_EQ(*val, 42); + } + return 0; +} + +TEST(DISABLED_BENCH_ThreadSanitizer, Singleton) { + const int kClockSize = 100; + const int kThreadCount = 8; + + // Puff off thread's clock. + for (int i = 0; i < kClockSize; i++) { + ScopedThread t1; + (void)t1; + } + // Create the singleton. + int val = 42; + __tsan_write4(&val); + atomic_uintptr_t singleton; + __tsan_release(&singleton); + atomic_store(&singleton, (uintptr_t)&val, memory_order_release); + // Create reader threads. + pthread_t threads[kThreadCount]; + for (int t = 0; t < kThreadCount; t++) + pthread_create(&threads[t], 0, singleton_thread, &singleton); + for (int t = 0; t < kThreadCount; t++) + pthread_join(threads[t], 0); +} + +TEST(DISABLED_BENCH_ThreadSanitizer, StopFlag) { + const int kClockSize = 100; + const int kIters = 16*1024*1024; + + // Puff off thread's clock. + for (int i = 0; i < kClockSize; i++) { + ScopedThread t1; + (void)t1; + } + // Create the stop flag. + atomic_uintptr_t flag; + __tsan_release(&flag); + atomic_store(&flag, 0, memory_order_release); + // Read it a lot. + for (int i = 0; i < kIters; i++) { + uptr v = atomic_load(&flag, memory_order_acquire); + __tsan_acquire(&flag); + CHECK_EQ(v, 0); + } +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc b/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc deleted file mode 100644 index d1940452cc8..00000000000 --- a/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc +++ /dev/null @@ -1,154 +0,0 @@ -//===-- tsan_posix.cc -----------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_interface.h" -#include "tsan_posix_util.h" -#include "tsan_test_util.h" -#include "gtest/gtest.h" -#include - -struct thread_key { - pthread_key_t key; - pthread_mutex_t *mtx; - int val; - int *cnt; - thread_key(pthread_key_t key, pthread_mutex_t *mtx, int val, int *cnt) - : key(key) - , mtx(mtx) - , val(val) - , cnt(cnt) { - } -}; - -static void thread_secific_dtor(void *v) { - thread_key *k = (thread_key *)v; - EXPECT_EQ(__interceptor_pthread_mutex_lock(k->mtx), 0); - (*k->cnt)++; - __tsan_write4(&k->cnt); - EXPECT_EQ(__interceptor_pthread_mutex_unlock(k->mtx), 0); - if (k->val == 42) { - // Okay. - } else if (k->val == 43 || k->val == 44) { - k->val--; - EXPECT_EQ(pthread_setspecific(k->key, k), 0); - } else { - ASSERT_TRUE(false); - } -} - -static void *dtors_thread(void *p) { - thread_key *k = (thread_key *)p; - EXPECT_EQ(pthread_setspecific(k->key, k), 0); - return 0; -} - -TEST(Posix, ThreadSpecificDtors) { - int cnt = 0; - pthread_key_t key; - EXPECT_EQ(pthread_key_create(&key, thread_secific_dtor), 0); - pthread_mutex_t mtx; - EXPECT_EQ(__interceptor_pthread_mutex_init(&mtx, 0), 0); - pthread_t th[3]; - thread_key k1 = thread_key(key, &mtx, 42, &cnt); - thread_key k2 = thread_key(key, &mtx, 43, &cnt); - thread_key k3 = thread_key(key, &mtx, 44, &cnt); - EXPECT_EQ(__interceptor_pthread_create(&th[0], 0, dtors_thread, &k1), 0); - EXPECT_EQ(__interceptor_pthread_create(&th[1], 0, dtors_thread, &k2), 0); - EXPECT_EQ(__interceptor_pthread_join(th[0], 0), 0); - EXPECT_EQ(__interceptor_pthread_create(&th[2], 0, dtors_thread, &k3), 0); - EXPECT_EQ(__interceptor_pthread_join(th[1], 0), 0); - EXPECT_EQ(__interceptor_pthread_join(th[2], 0), 0); - EXPECT_EQ(pthread_key_delete(key), 0); - EXPECT_EQ(6, cnt); -} - -#if !defined(__aarch64__) && !defined(__APPLE__) -static __thread int local_var; - -static void *local_thread(void *p) { - __tsan_write1(&local_var); - __tsan_write1(&p); - if (p == 0) - return 0; - const int kThreads = 4; - pthread_t th[kThreads]; - for (int i = 0; i < kThreads; i++) - EXPECT_EQ(__interceptor_pthread_create(&th[i], 0, local_thread, - (void*)((long)p - 1)), 0); // NOLINT - for (int i = 0; i < kThreads; i++) - EXPECT_EQ(__interceptor_pthread_join(th[i], 0), 0); - return 0; -} -#endif - -TEST(Posix, ThreadLocalAccesses) { -// The test is failing with high thread count for aarch64. -// FIXME: track down the issue and re-enable the test. -// On Darwin, we're running unit tests without interceptors and __thread is -// using malloc and free, which causes false data race reports. On rare -// occasions on powerpc64le this test also fails. -#if !defined(__aarch64__) && !defined(__APPLE__) && !defined(powerpc64le) - local_thread((void*)2); -#endif -} - -struct CondContext { - pthread_mutex_t m; - pthread_cond_t c; - int data; -}; - -static void *cond_thread(void *p) { - CondContext &ctx = *static_cast(p); - - EXPECT_EQ(__interceptor_pthread_mutex_lock(&ctx.m), 0); - EXPECT_EQ(ctx.data, 0); - ctx.data = 1; - EXPECT_EQ(__interceptor_pthread_cond_signal(&ctx.c), 0); - EXPECT_EQ(__interceptor_pthread_mutex_unlock(&ctx.m), 0); - - EXPECT_EQ(__interceptor_pthread_mutex_lock(&ctx.m), 0); - while (ctx.data != 2) - EXPECT_EQ(__interceptor_pthread_cond_wait(&ctx.c, &ctx.m), 0); - EXPECT_EQ(__interceptor_pthread_mutex_unlock(&ctx.m), 0); - - EXPECT_EQ(__interceptor_pthread_mutex_lock(&ctx.m), 0); - ctx.data = 3; - EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0); - EXPECT_EQ(__interceptor_pthread_mutex_unlock(&ctx.m), 0); - - return 0; -} - -TEST(Posix, CondBasic) { - CondContext ctx; - EXPECT_EQ(__interceptor_pthread_mutex_init(&ctx.m, 0), 0); - EXPECT_EQ(__interceptor_pthread_cond_init(&ctx.c, 0), 0); - ctx.data = 0; - pthread_t th; - EXPECT_EQ(__interceptor_pthread_create(&th, 0, cond_thread, &ctx), 0); - - EXPECT_EQ(__interceptor_pthread_mutex_lock(&ctx.m), 0); - while (ctx.data != 1) - EXPECT_EQ(__interceptor_pthread_cond_wait(&ctx.c, &ctx.m), 0); - ctx.data = 2; - EXPECT_EQ(__interceptor_pthread_mutex_unlock(&ctx.m), 0); - EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0); - - EXPECT_EQ(__interceptor_pthread_mutex_lock(&ctx.m), 0); - while (ctx.data != 3) - EXPECT_EQ(__interceptor_pthread_cond_wait(&ctx.c, &ctx.m), 0); - EXPECT_EQ(__interceptor_pthread_mutex_unlock(&ctx.m), 0); - - EXPECT_EQ(__interceptor_pthread_join(th, 0), 0); - EXPECT_EQ(__interceptor_pthread_cond_destroy(&ctx.c), 0); - EXPECT_EQ(__interceptor_pthread_mutex_destroy(&ctx.m), 0); -} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cpp b/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cpp new file mode 100644 index 00000000000..0eb8dc14df7 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cpp @@ -0,0 +1,154 @@ +//===-- tsan_posix.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_interface.h" +#include "tsan_posix_util.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include + +struct thread_key { + pthread_key_t key; + pthread_mutex_t *mtx; + int val; + int *cnt; + thread_key(pthread_key_t key, pthread_mutex_t *mtx, int val, int *cnt) + : key(key) + , mtx(mtx) + , val(val) + , cnt(cnt) { + } +}; + +static void thread_secific_dtor(void *v) { + thread_key *k = (thread_key *)v; + EXPECT_EQ(__interceptor_pthread_mutex_lock(k->mtx), 0); + (*k->cnt)++; + __tsan_write4(&k->cnt); + EXPECT_EQ(__interceptor_pthread_mutex_unlock(k->mtx), 0); + if (k->val == 42) { + // Okay. + } else if (k->val == 43 || k->val == 44) { + k->val--; + EXPECT_EQ(pthread_setspecific(k->key, k), 0); + } else { + ASSERT_TRUE(false); + } +} + +static void *dtors_thread(void *p) { + thread_key *k = (thread_key *)p; + EXPECT_EQ(pthread_setspecific(k->key, k), 0); + return 0; +} + +TEST(Posix, ThreadSpecificDtors) { + int cnt = 0; + pthread_key_t key; + EXPECT_EQ(pthread_key_create(&key, thread_secific_dtor), 0); + pthread_mutex_t mtx; + EXPECT_EQ(__interceptor_pthread_mutex_init(&mtx, 0), 0); + pthread_t th[3]; + thread_key k1 = thread_key(key, &mtx, 42, &cnt); + thread_key k2 = thread_key(key, &mtx, 43, &cnt); + thread_key k3 = thread_key(key, &mtx, 44, &cnt); + EXPECT_EQ(__interceptor_pthread_create(&th[0], 0, dtors_thread, &k1), 0); + EXPECT_EQ(__interceptor_pthread_create(&th[1], 0, dtors_thread, &k2), 0); + EXPECT_EQ(__interceptor_pthread_join(th[0], 0), 0); + EXPECT_EQ(__interceptor_pthread_create(&th[2], 0, dtors_thread, &k3), 0); + EXPECT_EQ(__interceptor_pthread_join(th[1], 0), 0); + EXPECT_EQ(__interceptor_pthread_join(th[2], 0), 0); + EXPECT_EQ(pthread_key_delete(key), 0); + EXPECT_EQ(6, cnt); +} + +#if !defined(__aarch64__) && !defined(__APPLE__) +static __thread int local_var; + +static void *local_thread(void *p) { + __tsan_write1(&local_var); + __tsan_write1(&p); + if (p == 0) + return 0; + const int kThreads = 4; + pthread_t th[kThreads]; + for (int i = 0; i < kThreads; i++) + EXPECT_EQ(__interceptor_pthread_create(&th[i], 0, local_thread, + (void*)((long)p - 1)), 0); // NOLINT + for (int i = 0; i < kThreads; i++) + EXPECT_EQ(__interceptor_pthread_join(th[i], 0), 0); + return 0; +} +#endif + +TEST(Posix, ThreadLocalAccesses) { +// The test is failing with high thread count for aarch64. +// FIXME: track down the issue and re-enable the test. +// On Darwin, we're running unit tests without interceptors and __thread is +// using malloc and free, which causes false data race reports. On rare +// occasions on powerpc64le this test also fails. +#if !defined(__aarch64__) && !defined(__APPLE__) && !defined(powerpc64le) + local_thread((void*)2); +#endif +} + +struct CondContext { + pthread_mutex_t m; + pthread_cond_t c; + int data; +}; + +static void *cond_thread(void *p) { + CondContext &ctx = *static_cast(p); + + EXPECT_EQ(__interceptor_pthread_mutex_lock(&ctx.m), 0); + EXPECT_EQ(ctx.data, 0); + ctx.data = 1; + EXPECT_EQ(__interceptor_pthread_cond_signal(&ctx.c), 0); + EXPECT_EQ(__interceptor_pthread_mutex_unlock(&ctx.m), 0); + + EXPECT_EQ(__interceptor_pthread_mutex_lock(&ctx.m), 0); + while (ctx.data != 2) + EXPECT_EQ(__interceptor_pthread_cond_wait(&ctx.c, &ctx.m), 0); + EXPECT_EQ(__interceptor_pthread_mutex_unlock(&ctx.m), 0); + + EXPECT_EQ(__interceptor_pthread_mutex_lock(&ctx.m), 0); + ctx.data = 3; + EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0); + EXPECT_EQ(__interceptor_pthread_mutex_unlock(&ctx.m), 0); + + return 0; +} + +TEST(Posix, CondBasic) { + CondContext ctx; + EXPECT_EQ(__interceptor_pthread_mutex_init(&ctx.m, 0), 0); + EXPECT_EQ(__interceptor_pthread_cond_init(&ctx.c, 0), 0); + ctx.data = 0; + pthread_t th; + EXPECT_EQ(__interceptor_pthread_create(&th, 0, cond_thread, &ctx), 0); + + EXPECT_EQ(__interceptor_pthread_mutex_lock(&ctx.m), 0); + while (ctx.data != 1) + EXPECT_EQ(__interceptor_pthread_cond_wait(&ctx.c, &ctx.m), 0); + ctx.data = 2; + EXPECT_EQ(__interceptor_pthread_mutex_unlock(&ctx.m), 0); + EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0); + + EXPECT_EQ(__interceptor_pthread_mutex_lock(&ctx.m), 0); + while (ctx.data != 3) + EXPECT_EQ(__interceptor_pthread_cond_wait(&ctx.c, &ctx.m), 0); + EXPECT_EQ(__interceptor_pthread_mutex_unlock(&ctx.m), 0); + + EXPECT_EQ(__interceptor_pthread_join(th, 0), 0); + EXPECT_EQ(__interceptor_pthread_cond_destroy(&ctx.c), 0); + EXPECT_EQ(__interceptor_pthread_mutex_destroy(&ctx.m), 0); +} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc b/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc deleted file mode 100644 index b236d46310e..00000000000 --- a/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc +++ /dev/null @@ -1,81 +0,0 @@ -//===-- tsan_string.cc ----------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_test_util.h" -#include "gtest/gtest.h" -#include - -namespace __tsan { - -TEST(ThreadSanitizer, Memcpy) { - char data0[7] = {1, 2, 3, 4, 5, 6, 7}; - char data[7] = {42, 42, 42, 42, 42, 42, 42}; - MainThread().Memcpy(data+1, data0+1, 5); - EXPECT_EQ(data[0], 42); - EXPECT_EQ(data[1], 2); - EXPECT_EQ(data[2], 3); - EXPECT_EQ(data[3], 4); - EXPECT_EQ(data[4], 5); - EXPECT_EQ(data[5], 6); - EXPECT_EQ(data[6], 42); - MainThread().Memset(data+1, 13, 5); - EXPECT_EQ(data[0], 42); - EXPECT_EQ(data[1], 13); - EXPECT_EQ(data[2], 13); - EXPECT_EQ(data[3], 13); - EXPECT_EQ(data[4], 13); - EXPECT_EQ(data[5], 13); - EXPECT_EQ(data[6], 42); -} - -TEST(ThreadSanitizer, MemcpyRace1) { - char *data = new char[10]; - char *data1 = new char[10]; - char *data2 = new char[10]; - ScopedThread t1, t2; - t1.Memcpy(data, data1, 10); - t2.Memcpy(data, data2, 10, true); -} - -TEST(ThreadSanitizer, MemcpyRace2) { - char *data = new char[10]; - char *data1 = new char[10]; - char *data2 = new char[10]; - ScopedThread t1, t2; - t1.Memcpy(data+5, data1, 1); - t2.Memcpy(data+3, data2, 4, true); -} - -TEST(ThreadSanitizer, MemcpyRace3) { - char *data = new char[10]; - char *data1 = new char[10]; - char *data2 = new char[10]; - ScopedThread t1, t2; - t1.Memcpy(data, data1, 10); - t2.Memcpy(data1, data2, 10, true); -} - -TEST(ThreadSanitizer, MemcpyStack) { - char *data = new char[10]; - char *data1 = new char[10]; - ScopedThread t1, t2; - t1.Memcpy(data, data1, 10); - t2.Memcpy(data, data1, 10, true); -} - -TEST(ThreadSanitizer, MemsetRace1) { - char *data = new char[10]; - ScopedThread t1, t2; - t1.Memset(data, 1, 10); - t2.Memset(data, 2, 10, true); -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_string.cpp b/compiler-rt/lib/tsan/tests/rtl/tsan_string.cpp new file mode 100644 index 00000000000..4c31389298a --- /dev/null +++ b/compiler-rt/lib/tsan/tests/rtl/tsan_string.cpp @@ -0,0 +1,81 @@ +//===-- tsan_string.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include + +namespace __tsan { + +TEST(ThreadSanitizer, Memcpy) { + char data0[7] = {1, 2, 3, 4, 5, 6, 7}; + char data[7] = {42, 42, 42, 42, 42, 42, 42}; + MainThread().Memcpy(data+1, data0+1, 5); + EXPECT_EQ(data[0], 42); + EXPECT_EQ(data[1], 2); + EXPECT_EQ(data[2], 3); + EXPECT_EQ(data[3], 4); + EXPECT_EQ(data[4], 5); + EXPECT_EQ(data[5], 6); + EXPECT_EQ(data[6], 42); + MainThread().Memset(data+1, 13, 5); + EXPECT_EQ(data[0], 42); + EXPECT_EQ(data[1], 13); + EXPECT_EQ(data[2], 13); + EXPECT_EQ(data[3], 13); + EXPECT_EQ(data[4], 13); + EXPECT_EQ(data[5], 13); + EXPECT_EQ(data[6], 42); +} + +TEST(ThreadSanitizer, MemcpyRace1) { + char *data = new char[10]; + char *data1 = new char[10]; + char *data2 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data, data1, 10); + t2.Memcpy(data, data2, 10, true); +} + +TEST(ThreadSanitizer, MemcpyRace2) { + char *data = new char[10]; + char *data1 = new char[10]; + char *data2 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data+5, data1, 1); + t2.Memcpy(data+3, data2, 4, true); +} + +TEST(ThreadSanitizer, MemcpyRace3) { + char *data = new char[10]; + char *data1 = new char[10]; + char *data2 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data, data1, 10); + t2.Memcpy(data1, data2, 10, true); +} + +TEST(ThreadSanitizer, MemcpyStack) { + char *data = new char[10]; + char *data1 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data, data1, 10); + t2.Memcpy(data, data1, 10, true); +} + +TEST(ThreadSanitizer, MemsetRace1) { + char *data = new char[10]; + ScopedThread t1, t2; + t1.Memset(data, 1, 10); + t2.Memset(data, 2, 10, true); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_test.cc b/compiler-rt/lib/tsan/tests/rtl/tsan_test.cc deleted file mode 100644 index 51a3b273114..00000000000 --- a/compiler-rt/lib/tsan/tests/rtl/tsan_test.cc +++ /dev/null @@ -1,65 +0,0 @@ -//===-- tsan_test.cc ------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_interface.h" -#include "tsan_test_util.h" -#include "gtest/gtest.h" - -static void foo() {} -static void bar() {} - -TEST(ThreadSanitizer, FuncCall) { - ScopedThread t1, t2; - MemLoc l; - t1.Write1(l); - t2.Call(foo); - t2.Call(bar); - t2.Write1(l, true); - t2.Return(); - t2.Return(); -} - -// We use this function instead of main, as ISO C++ forbids taking the address -// of main, which we need to pass inside __tsan_func_entry. -int run_tests(int argc, char **argv) { - TestMutexBeforeInit(); // Mutexes must be usable before __tsan_init(); - __tsan_init(); - __tsan_func_entry(__builtin_return_address(0)); - __tsan_func_entry((void*)((intptr_t)&run_tests + 1)); - - testing::GTEST_FLAG(death_test_style) = "threadsafe"; - testing::InitGoogleTest(&argc, argv); - int res = RUN_ALL_TESTS(); - - __tsan_func_exit(); - __tsan_func_exit(); - return res; -} - -const char *argv0; - -#ifdef __APPLE__ -// On Darwin, turns off symbolication and crash logs to make tests faster. -extern "C" const char* __tsan_default_options() { - return "symbolize=false:abort_on_error=0"; -} -#endif - -namespace __sanitizer { -bool ReexecDisabled() { - return true; -} -} - -int main(int argc, char **argv) { - argv0 = argv[0]; - return run_tests(argc, argv); -} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_test.cpp b/compiler-rt/lib/tsan/tests/rtl/tsan_test.cpp new file mode 100644 index 00000000000..84e6bbcfe47 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/rtl/tsan_test.cpp @@ -0,0 +1,65 @@ +//===-- tsan_test.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_interface.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" + +static void foo() {} +static void bar() {} + +TEST(ThreadSanitizer, FuncCall) { + ScopedThread t1, t2; + MemLoc l; + t1.Write1(l); + t2.Call(foo); + t2.Call(bar); + t2.Write1(l, true); + t2.Return(); + t2.Return(); +} + +// We use this function instead of main, as ISO C++ forbids taking the address +// of main, which we need to pass inside __tsan_func_entry. +int run_tests(int argc, char **argv) { + TestMutexBeforeInit(); // Mutexes must be usable before __tsan_init(); + __tsan_init(); + __tsan_func_entry(__builtin_return_address(0)); + __tsan_func_entry((void*)((intptr_t)&run_tests + 1)); + + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + testing::InitGoogleTest(&argc, argv); + int res = RUN_ALL_TESTS(); + + __tsan_func_exit(); + __tsan_func_exit(); + return res; +} + +const char *argv0; + +#ifdef __APPLE__ +// On Darwin, turns off symbolication and crash logs to make tests faster. +extern "C" const char* __tsan_default_options() { + return "symbolize=false:abort_on_error=0"; +} +#endif + +namespace __sanitizer { +bool ReexecDisabled() { + return true; +} +} + +int main(int argc, char **argv) { + argv0 = argv[0]; + return run_tests(argc, argv); +} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cc b/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cc deleted file mode 100644 index 767c8294dbd..00000000000 --- a/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cc +++ /dev/null @@ -1,477 +0,0 @@ -//===-- tsan_test_util_posix.cc -------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -// Test utils, Linux, FreeBSD, NetBSD and Darwin implementation. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_atomic.h" -#include "tsan_interface.h" -#include "tsan_posix_util.h" -#include "tsan_test_util.h" -#include "tsan_report.h" - -#include "gtest/gtest.h" - -#include -#include -#include -#include -#include -#include -#include - -using namespace __tsan; // NOLINT - -static __thread bool expect_report; -static __thread bool expect_report_reported; -static __thread ReportType expect_report_type; - -static void *BeforeInitThread(void *param) { - (void)param; - return 0; -} - -static void AtExit() { -} - -void TestMutexBeforeInit() { - // Mutexes must be usable before __tsan_init(); - pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; - __interceptor_pthread_mutex_lock(&mtx); - __interceptor_pthread_mutex_unlock(&mtx); - __interceptor_pthread_mutex_destroy(&mtx); - pthread_t thr; - __interceptor_pthread_create(&thr, 0, BeforeInitThread, 0); - __interceptor_pthread_join(thr, 0); - atexit(AtExit); -} - -namespace __tsan { -bool OnReport(const ReportDesc *rep, bool suppressed) { - if (expect_report) { - if (rep->typ != expect_report_type) { - printf("Expected report of type %d, got type %d\n", - (int)expect_report_type, (int)rep->typ); - EXPECT_TRUE(false) << "Wrong report type"; - return false; - } - } else { - EXPECT_TRUE(false) << "Unexpected report"; - return false; - } - expect_report_reported = true; - return true; -} -} // namespace __tsan - -static void* allocate_addr(int size, int offset_from_aligned = 0) { - static uintptr_t foo; - static atomic_uintptr_t uniq = {(uintptr_t)&foo}; // Some real address. - const int kAlign = 16; - CHECK(offset_from_aligned < kAlign); - size = (size + 2 * kAlign) & ~(kAlign - 1); - uintptr_t addr = atomic_fetch_add(&uniq, size, memory_order_relaxed); - return (void*)(addr + offset_from_aligned); -} - -MemLoc::MemLoc(int offset_from_aligned) - : loc_(allocate_addr(16, offset_from_aligned)) { -} - -MemLoc::~MemLoc() { -} - -Mutex::Mutex(Type type) - : alive_() - , type_(type) { -} - -Mutex::~Mutex() { - CHECK(!alive_); -} - -void Mutex::Init() { - CHECK(!alive_); - alive_ = true; - if (type_ == Normal) - CHECK_EQ(__interceptor_pthread_mutex_init((pthread_mutex_t*)mtx_, 0), 0); -#ifndef __APPLE__ - else if (type_ == Spin) - CHECK_EQ(pthread_spin_init((pthread_spinlock_t*)mtx_, 0), 0); -#endif - else if (type_ == RW) - CHECK_EQ(__interceptor_pthread_rwlock_init((pthread_rwlock_t*)mtx_, 0), 0); - else - CHECK(0); -} - -void Mutex::StaticInit() { - CHECK(!alive_); - CHECK(type_ == Normal); - alive_ = true; - pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER; - memcpy(mtx_, &tmp, sizeof(tmp)); -} - -void Mutex::Destroy() { - CHECK(alive_); - alive_ = false; - if (type_ == Normal) - CHECK_EQ(__interceptor_pthread_mutex_destroy((pthread_mutex_t*)mtx_), 0); -#ifndef __APPLE__ - else if (type_ == Spin) - CHECK_EQ(pthread_spin_destroy((pthread_spinlock_t*)mtx_), 0); -#endif - else if (type_ == RW) - CHECK_EQ(__interceptor_pthread_rwlock_destroy((pthread_rwlock_t*)mtx_), 0); -} - -void Mutex::Lock() { - CHECK(alive_); - if (type_ == Normal) - CHECK_EQ(__interceptor_pthread_mutex_lock((pthread_mutex_t*)mtx_), 0); -#ifndef __APPLE__ - else if (type_ == Spin) - CHECK_EQ(pthread_spin_lock((pthread_spinlock_t*)mtx_), 0); -#endif - else if (type_ == RW) - CHECK_EQ(__interceptor_pthread_rwlock_wrlock((pthread_rwlock_t*)mtx_), 0); -} - -bool Mutex::TryLock() { - CHECK(alive_); - if (type_ == Normal) - return __interceptor_pthread_mutex_trylock((pthread_mutex_t*)mtx_) == 0; -#ifndef __APPLE__ - else if (type_ == Spin) - return pthread_spin_trylock((pthread_spinlock_t*)mtx_) == 0; -#endif - else if (type_ == RW) - return __interceptor_pthread_rwlock_trywrlock((pthread_rwlock_t*)mtx_) == 0; - return false; -} - -void Mutex::Unlock() { - CHECK(alive_); - if (type_ == Normal) - CHECK_EQ(__interceptor_pthread_mutex_unlock((pthread_mutex_t*)mtx_), 0); -#ifndef __APPLE__ - else if (type_ == Spin) - CHECK_EQ(pthread_spin_unlock((pthread_spinlock_t*)mtx_), 0); -#endif - else if (type_ == RW) - CHECK_EQ(__interceptor_pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0); -} - -void Mutex::ReadLock() { - CHECK(alive_); - CHECK(type_ == RW); - CHECK_EQ(__interceptor_pthread_rwlock_rdlock((pthread_rwlock_t*)mtx_), 0); -} - -bool Mutex::TryReadLock() { - CHECK(alive_); - CHECK(type_ == RW); - return __interceptor_pthread_rwlock_tryrdlock((pthread_rwlock_t*)mtx_) == 0; -} - -void Mutex::ReadUnlock() { - CHECK(alive_); - CHECK(type_ == RW); - CHECK_EQ(__interceptor_pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0); -} - -struct Event { - enum Type { - SHUTDOWN, - READ, - WRITE, - VPTR_UPDATE, - CALL, - RETURN, - MUTEX_CREATE, - MUTEX_DESTROY, - MUTEX_LOCK, - MUTEX_TRYLOCK, - MUTEX_UNLOCK, - MUTEX_READLOCK, - MUTEX_TRYREADLOCK, - MUTEX_READUNLOCK, - MEMCPY, - MEMSET - }; - Type type; - void *ptr; - uptr arg; - uptr arg2; - bool res; - bool expect_report; - ReportType report_type; - - Event(Type type, const void *ptr = 0, uptr arg = 0, uptr arg2 = 0) - : type(type) - , ptr(const_cast(ptr)) - , arg(arg) - , arg2(arg2) - , res() - , expect_report() - , report_type() { - } - - void ExpectReport(ReportType type) { - expect_report = true; - report_type = type; - } -}; - -struct ScopedThread::Impl { - pthread_t thread; - bool main; - bool detached; - atomic_uintptr_t event; // Event* - - static void *ScopedThreadCallback(void *arg); - void send(Event *ev); - void HandleEvent(Event *ev); -}; - -void ScopedThread::Impl::HandleEvent(Event *ev) { - CHECK_EQ(expect_report, false); - expect_report = ev->expect_report; - expect_report_reported = false; - expect_report_type = ev->report_type; - switch (ev->type) { - case Event::READ: - case Event::WRITE: { - void (*tsan_mop)(void *addr) = 0; - if (ev->type == Event::READ) { - switch (ev->arg /*size*/) { - case 1: tsan_mop = __tsan_read1; break; - case 2: tsan_mop = __tsan_read2; break; - case 4: tsan_mop = __tsan_read4; break; - case 8: tsan_mop = __tsan_read8; break; - case 16: tsan_mop = __tsan_read16; break; - } - } else { - switch (ev->arg /*size*/) { - case 1: tsan_mop = __tsan_write1; break; - case 2: tsan_mop = __tsan_write2; break; - case 4: tsan_mop = __tsan_write4; break; - case 8: tsan_mop = __tsan_write8; break; - case 16: tsan_mop = __tsan_write16; break; - } - } - CHECK_NE(tsan_mop, 0); -#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__NetBSD__) - const int ErrCode = ESOCKTNOSUPPORT; -#else - const int ErrCode = ECHRNG; -#endif - errno = ErrCode; - tsan_mop(ev->ptr); - CHECK_EQ(ErrCode, errno); // In no case must errno be changed. - break; - } - case Event::VPTR_UPDATE: - __tsan_vptr_update((void**)ev->ptr, (void*)ev->arg); - break; - case Event::CALL: - __tsan_func_entry((void*)((uptr)ev->ptr)); - break; - case Event::RETURN: - __tsan_func_exit(); - break; - case Event::MUTEX_CREATE: - static_cast(ev->ptr)->Init(); - break; - case Event::MUTEX_DESTROY: - static_cast(ev->ptr)->Destroy(); - break; - case Event::MUTEX_LOCK: - static_cast(ev->ptr)->Lock(); - break; - case Event::MUTEX_TRYLOCK: - ev->res = static_cast(ev->ptr)->TryLock(); - break; - case Event::MUTEX_UNLOCK: - static_cast(ev->ptr)->Unlock(); - break; - case Event::MUTEX_READLOCK: - static_cast(ev->ptr)->ReadLock(); - break; - case Event::MUTEX_TRYREADLOCK: - ev->res = static_cast(ev->ptr)->TryReadLock(); - break; - case Event::MUTEX_READUNLOCK: - static_cast(ev->ptr)->ReadUnlock(); - break; - case Event::MEMCPY: - __interceptor_memcpy(ev->ptr, (void*)ev->arg, ev->arg2); - break; - case Event::MEMSET: - __interceptor_memset(ev->ptr, ev->arg, ev->arg2); - break; - default: CHECK(0); - } - if (expect_report && !expect_report_reported) { - printf("Missed expected report of type %d\n", (int)ev->report_type); - EXPECT_TRUE(false) << "Missed expected race"; - } - expect_report = false; -} - -void *ScopedThread::Impl::ScopedThreadCallback(void *arg) { - __tsan_func_entry(__builtin_return_address(0)); - Impl *impl = (Impl*)arg; - for (;;) { - Event* ev = (Event*)atomic_load(&impl->event, memory_order_acquire); - if (ev == 0) { - sched_yield(); - continue; - } - if (ev->type == Event::SHUTDOWN) { - atomic_store(&impl->event, 0, memory_order_release); - break; - } - impl->HandleEvent(ev); - atomic_store(&impl->event, 0, memory_order_release); - } - __tsan_func_exit(); - return 0; -} - -void ScopedThread::Impl::send(Event *e) { - if (main) { - HandleEvent(e); - } else { - CHECK_EQ(atomic_load(&event, memory_order_relaxed), 0); - atomic_store(&event, (uintptr_t)e, memory_order_release); - while (atomic_load(&event, memory_order_acquire) != 0) - sched_yield(); - } -} - -ScopedThread::ScopedThread(bool detached, bool main) { - impl_ = new Impl; - impl_->main = main; - impl_->detached = detached; - atomic_store(&impl_->event, 0, memory_order_relaxed); - if (!main) { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate( - &attr, detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE); - pthread_attr_setstacksize(&attr, 64*1024); - __interceptor_pthread_create(&impl_->thread, &attr, - ScopedThread::Impl::ScopedThreadCallback, impl_); - } -} - -ScopedThread::~ScopedThread() { - if (!impl_->main) { - Event event(Event::SHUTDOWN); - impl_->send(&event); - if (!impl_->detached) - __interceptor_pthread_join(impl_->thread, 0); - } - delete impl_; -} - -void ScopedThread::Detach() { - CHECK(!impl_->main); - CHECK(!impl_->detached); - impl_->detached = true; - __interceptor_pthread_detach(impl_->thread); -} - -void ScopedThread::Access(void *addr, bool is_write, - int size, bool expect_race) { - Event event(is_write ? Event::WRITE : Event::READ, addr, size); - if (expect_race) - event.ExpectReport(ReportTypeRace); - impl_->send(&event); -} - -void ScopedThread::VptrUpdate(const MemLoc &vptr, - const MemLoc &new_val, - bool expect_race) { - Event event(Event::VPTR_UPDATE, vptr.loc(), (uptr)new_val.loc()); - if (expect_race) - event.ExpectReport(ReportTypeRace); - impl_->send(&event); -} - -void ScopedThread::Call(void(*pc)()) { - Event event(Event::CALL, (void*)((uintptr_t)pc)); - impl_->send(&event); -} - -void ScopedThread::Return() { - Event event(Event::RETURN); - impl_->send(&event); -} - -void ScopedThread::Create(const Mutex &m) { - Event event(Event::MUTEX_CREATE, &m); - impl_->send(&event); -} - -void ScopedThread::Destroy(const Mutex &m) { - Event event(Event::MUTEX_DESTROY, &m); - impl_->send(&event); -} - -void ScopedThread::Lock(const Mutex &m) { - Event event(Event::MUTEX_LOCK, &m); - impl_->send(&event); -} - -bool ScopedThread::TryLock(const Mutex &m) { - Event event(Event::MUTEX_TRYLOCK, &m); - impl_->send(&event); - return event.res; -} - -void ScopedThread::Unlock(const Mutex &m) { - Event event(Event::MUTEX_UNLOCK, &m); - impl_->send(&event); -} - -void ScopedThread::ReadLock(const Mutex &m) { - Event event(Event::MUTEX_READLOCK, &m); - impl_->send(&event); -} - -bool ScopedThread::TryReadLock(const Mutex &m) { - Event event(Event::MUTEX_TRYREADLOCK, &m); - impl_->send(&event); - return event.res; -} - -void ScopedThread::ReadUnlock(const Mutex &m) { - Event event(Event::MUTEX_READUNLOCK, &m); - impl_->send(&event); -} - -void ScopedThread::Memcpy(void *dst, const void *src, int size, - bool expect_race) { - Event event(Event::MEMCPY, dst, (uptr)src, size); - if (expect_race) - event.ExpectReport(ReportTypeRace); - impl_->send(&event); -} - -void ScopedThread::Memset(void *dst, int val, int size, - bool expect_race) { - Event event(Event::MEMSET, dst, val, size); - if (expect_race) - event.ExpectReport(ReportTypeRace); - impl_->send(&event); -} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cpp b/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cpp new file mode 100644 index 00000000000..73c7e9235d0 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cpp @@ -0,0 +1,477 @@ +//===-- tsan_test_util_posix.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Test utils, Linux, FreeBSD, NetBSD and Darwin implementation. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_atomic.h" +#include "tsan_interface.h" +#include "tsan_posix_util.h" +#include "tsan_test_util.h" +#include "tsan_report.h" + +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace __tsan; // NOLINT + +static __thread bool expect_report; +static __thread bool expect_report_reported; +static __thread ReportType expect_report_type; + +static void *BeforeInitThread(void *param) { + (void)param; + return 0; +} + +static void AtExit() { +} + +void TestMutexBeforeInit() { + // Mutexes must be usable before __tsan_init(); + pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + __interceptor_pthread_mutex_lock(&mtx); + __interceptor_pthread_mutex_unlock(&mtx); + __interceptor_pthread_mutex_destroy(&mtx); + pthread_t thr; + __interceptor_pthread_create(&thr, 0, BeforeInitThread, 0); + __interceptor_pthread_join(thr, 0); + atexit(AtExit); +} + +namespace __tsan { +bool OnReport(const ReportDesc *rep, bool suppressed) { + if (expect_report) { + if (rep->typ != expect_report_type) { + printf("Expected report of type %d, got type %d\n", + (int)expect_report_type, (int)rep->typ); + EXPECT_TRUE(false) << "Wrong report type"; + return false; + } + } else { + EXPECT_TRUE(false) << "Unexpected report"; + return false; + } + expect_report_reported = true; + return true; +} +} // namespace __tsan + +static void* allocate_addr(int size, int offset_from_aligned = 0) { + static uintptr_t foo; + static atomic_uintptr_t uniq = {(uintptr_t)&foo}; // Some real address. + const int kAlign = 16; + CHECK(offset_from_aligned < kAlign); + size = (size + 2 * kAlign) & ~(kAlign - 1); + uintptr_t addr = atomic_fetch_add(&uniq, size, memory_order_relaxed); + return (void*)(addr + offset_from_aligned); +} + +MemLoc::MemLoc(int offset_from_aligned) + : loc_(allocate_addr(16, offset_from_aligned)) { +} + +MemLoc::~MemLoc() { +} + +Mutex::Mutex(Type type) + : alive_() + , type_(type) { +} + +Mutex::~Mutex() { + CHECK(!alive_); +} + +void Mutex::Init() { + CHECK(!alive_); + alive_ = true; + if (type_ == Normal) + CHECK_EQ(__interceptor_pthread_mutex_init((pthread_mutex_t*)mtx_, 0), 0); +#ifndef __APPLE__ + else if (type_ == Spin) + CHECK_EQ(pthread_spin_init((pthread_spinlock_t*)mtx_, 0), 0); +#endif + else if (type_ == RW) + CHECK_EQ(__interceptor_pthread_rwlock_init((pthread_rwlock_t*)mtx_, 0), 0); + else + CHECK(0); +} + +void Mutex::StaticInit() { + CHECK(!alive_); + CHECK(type_ == Normal); + alive_ = true; + pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER; + memcpy(mtx_, &tmp, sizeof(tmp)); +} + +void Mutex::Destroy() { + CHECK(alive_); + alive_ = false; + if (type_ == Normal) + CHECK_EQ(__interceptor_pthread_mutex_destroy((pthread_mutex_t*)mtx_), 0); +#ifndef __APPLE__ + else if (type_ == Spin) + CHECK_EQ(pthread_spin_destroy((pthread_spinlock_t*)mtx_), 0); +#endif + else if (type_ == RW) + CHECK_EQ(__interceptor_pthread_rwlock_destroy((pthread_rwlock_t*)mtx_), 0); +} + +void Mutex::Lock() { + CHECK(alive_); + if (type_ == Normal) + CHECK_EQ(__interceptor_pthread_mutex_lock((pthread_mutex_t*)mtx_), 0); +#ifndef __APPLE__ + else if (type_ == Spin) + CHECK_EQ(pthread_spin_lock((pthread_spinlock_t*)mtx_), 0); +#endif + else if (type_ == RW) + CHECK_EQ(__interceptor_pthread_rwlock_wrlock((pthread_rwlock_t*)mtx_), 0); +} + +bool Mutex::TryLock() { + CHECK(alive_); + if (type_ == Normal) + return __interceptor_pthread_mutex_trylock((pthread_mutex_t*)mtx_) == 0; +#ifndef __APPLE__ + else if (type_ == Spin) + return pthread_spin_trylock((pthread_spinlock_t*)mtx_) == 0; +#endif + else if (type_ == RW) + return __interceptor_pthread_rwlock_trywrlock((pthread_rwlock_t*)mtx_) == 0; + return false; +} + +void Mutex::Unlock() { + CHECK(alive_); + if (type_ == Normal) + CHECK_EQ(__interceptor_pthread_mutex_unlock((pthread_mutex_t*)mtx_), 0); +#ifndef __APPLE__ + else if (type_ == Spin) + CHECK_EQ(pthread_spin_unlock((pthread_spinlock_t*)mtx_), 0); +#endif + else if (type_ == RW) + CHECK_EQ(__interceptor_pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0); +} + +void Mutex::ReadLock() { + CHECK(alive_); + CHECK(type_ == RW); + CHECK_EQ(__interceptor_pthread_rwlock_rdlock((pthread_rwlock_t*)mtx_), 0); +} + +bool Mutex::TryReadLock() { + CHECK(alive_); + CHECK(type_ == RW); + return __interceptor_pthread_rwlock_tryrdlock((pthread_rwlock_t*)mtx_) == 0; +} + +void Mutex::ReadUnlock() { + CHECK(alive_); + CHECK(type_ == RW); + CHECK_EQ(__interceptor_pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0); +} + +struct Event { + enum Type { + SHUTDOWN, + READ, + WRITE, + VPTR_UPDATE, + CALL, + RETURN, + MUTEX_CREATE, + MUTEX_DESTROY, + MUTEX_LOCK, + MUTEX_TRYLOCK, + MUTEX_UNLOCK, + MUTEX_READLOCK, + MUTEX_TRYREADLOCK, + MUTEX_READUNLOCK, + MEMCPY, + MEMSET + }; + Type type; + void *ptr; + uptr arg; + uptr arg2; + bool res; + bool expect_report; + ReportType report_type; + + Event(Type type, const void *ptr = 0, uptr arg = 0, uptr arg2 = 0) + : type(type) + , ptr(const_cast(ptr)) + , arg(arg) + , arg2(arg2) + , res() + , expect_report() + , report_type() { + } + + void ExpectReport(ReportType type) { + expect_report = true; + report_type = type; + } +}; + +struct ScopedThread::Impl { + pthread_t thread; + bool main; + bool detached; + atomic_uintptr_t event; // Event* + + static void *ScopedThreadCallback(void *arg); + void send(Event *ev); + void HandleEvent(Event *ev); +}; + +void ScopedThread::Impl::HandleEvent(Event *ev) { + CHECK_EQ(expect_report, false); + expect_report = ev->expect_report; + expect_report_reported = false; + expect_report_type = ev->report_type; + switch (ev->type) { + case Event::READ: + case Event::WRITE: { + void (*tsan_mop)(void *addr) = 0; + if (ev->type == Event::READ) { + switch (ev->arg /*size*/) { + case 1: tsan_mop = __tsan_read1; break; + case 2: tsan_mop = __tsan_read2; break; + case 4: tsan_mop = __tsan_read4; break; + case 8: tsan_mop = __tsan_read8; break; + case 16: tsan_mop = __tsan_read16; break; + } + } else { + switch (ev->arg /*size*/) { + case 1: tsan_mop = __tsan_write1; break; + case 2: tsan_mop = __tsan_write2; break; + case 4: tsan_mop = __tsan_write4; break; + case 8: tsan_mop = __tsan_write8; break; + case 16: tsan_mop = __tsan_write16; break; + } + } + CHECK_NE(tsan_mop, 0); +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__NetBSD__) + const int ErrCode = ESOCKTNOSUPPORT; +#else + const int ErrCode = ECHRNG; +#endif + errno = ErrCode; + tsan_mop(ev->ptr); + CHECK_EQ(ErrCode, errno); // In no case must errno be changed. + break; + } + case Event::VPTR_UPDATE: + __tsan_vptr_update((void**)ev->ptr, (void*)ev->arg); + break; + case Event::CALL: + __tsan_func_entry((void*)((uptr)ev->ptr)); + break; + case Event::RETURN: + __tsan_func_exit(); + break; + case Event::MUTEX_CREATE: + static_cast(ev->ptr)->Init(); + break; + case Event::MUTEX_DESTROY: + static_cast(ev->ptr)->Destroy(); + break; + case Event::MUTEX_LOCK: + static_cast(ev->ptr)->Lock(); + break; + case Event::MUTEX_TRYLOCK: + ev->res = static_cast(ev->ptr)->TryLock(); + break; + case Event::MUTEX_UNLOCK: + static_cast(ev->ptr)->Unlock(); + break; + case Event::MUTEX_READLOCK: + static_cast(ev->ptr)->ReadLock(); + break; + case Event::MUTEX_TRYREADLOCK: + ev->res = static_cast(ev->ptr)->TryReadLock(); + break; + case Event::MUTEX_READUNLOCK: + static_cast(ev->ptr)->ReadUnlock(); + break; + case Event::MEMCPY: + __interceptor_memcpy(ev->ptr, (void*)ev->arg, ev->arg2); + break; + case Event::MEMSET: + __interceptor_memset(ev->ptr, ev->arg, ev->arg2); + break; + default: CHECK(0); + } + if (expect_report && !expect_report_reported) { + printf("Missed expected report of type %d\n", (int)ev->report_type); + EXPECT_TRUE(false) << "Missed expected race"; + } + expect_report = false; +} + +void *ScopedThread::Impl::ScopedThreadCallback(void *arg) { + __tsan_func_entry(__builtin_return_address(0)); + Impl *impl = (Impl*)arg; + for (;;) { + Event* ev = (Event*)atomic_load(&impl->event, memory_order_acquire); + if (ev == 0) { + sched_yield(); + continue; + } + if (ev->type == Event::SHUTDOWN) { + atomic_store(&impl->event, 0, memory_order_release); + break; + } + impl->HandleEvent(ev); + atomic_store(&impl->event, 0, memory_order_release); + } + __tsan_func_exit(); + return 0; +} + +void ScopedThread::Impl::send(Event *e) { + if (main) { + HandleEvent(e); + } else { + CHECK_EQ(atomic_load(&event, memory_order_relaxed), 0); + atomic_store(&event, (uintptr_t)e, memory_order_release); + while (atomic_load(&event, memory_order_acquire) != 0) + sched_yield(); + } +} + +ScopedThread::ScopedThread(bool detached, bool main) { + impl_ = new Impl; + impl_->main = main; + impl_->detached = detached; + atomic_store(&impl_->event, 0, memory_order_relaxed); + if (!main) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate( + &attr, detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE); + pthread_attr_setstacksize(&attr, 64*1024); + __interceptor_pthread_create(&impl_->thread, &attr, + ScopedThread::Impl::ScopedThreadCallback, impl_); + } +} + +ScopedThread::~ScopedThread() { + if (!impl_->main) { + Event event(Event::SHUTDOWN); + impl_->send(&event); + if (!impl_->detached) + __interceptor_pthread_join(impl_->thread, 0); + } + delete impl_; +} + +void ScopedThread::Detach() { + CHECK(!impl_->main); + CHECK(!impl_->detached); + impl_->detached = true; + __interceptor_pthread_detach(impl_->thread); +} + +void ScopedThread::Access(void *addr, bool is_write, + int size, bool expect_race) { + Event event(is_write ? Event::WRITE : Event::READ, addr, size); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} + +void ScopedThread::VptrUpdate(const MemLoc &vptr, + const MemLoc &new_val, + bool expect_race) { + Event event(Event::VPTR_UPDATE, vptr.loc(), (uptr)new_val.loc()); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} + +void ScopedThread::Call(void(*pc)()) { + Event event(Event::CALL, (void*)((uintptr_t)pc)); + impl_->send(&event); +} + +void ScopedThread::Return() { + Event event(Event::RETURN); + impl_->send(&event); +} + +void ScopedThread::Create(const Mutex &m) { + Event event(Event::MUTEX_CREATE, &m); + impl_->send(&event); +} + +void ScopedThread::Destroy(const Mutex &m) { + Event event(Event::MUTEX_DESTROY, &m); + impl_->send(&event); +} + +void ScopedThread::Lock(const Mutex &m) { + Event event(Event::MUTEX_LOCK, &m); + impl_->send(&event); +} + +bool ScopedThread::TryLock(const Mutex &m) { + Event event(Event::MUTEX_TRYLOCK, &m); + impl_->send(&event); + return event.res; +} + +void ScopedThread::Unlock(const Mutex &m) { + Event event(Event::MUTEX_UNLOCK, &m); + impl_->send(&event); +} + +void ScopedThread::ReadLock(const Mutex &m) { + Event event(Event::MUTEX_READLOCK, &m); + impl_->send(&event); +} + +bool ScopedThread::TryReadLock(const Mutex &m) { + Event event(Event::MUTEX_TRYREADLOCK, &m); + impl_->send(&event); + return event.res; +} + +void ScopedThread::ReadUnlock(const Mutex &m) { + Event event(Event::MUTEX_READUNLOCK, &m); + impl_->send(&event); +} + +void ScopedThread::Memcpy(void *dst, const void *src, int size, + bool expect_race) { + Event event(Event::MEMCPY, dst, (uptr)src, size); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} + +void ScopedThread::Memset(void *dst, int val, int size, + bool expect_race) { + Event event(Event::MEMSET, dst, val, size); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc b/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc deleted file mode 100644 index 9e2da912e9b..00000000000 --- a/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc +++ /dev/null @@ -1,58 +0,0 @@ -//===-- tsan_thread.cc ----------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_test_util.h" -#include "gtest/gtest.h" - -TEST(ThreadSanitizer, ThreadSync) { - MainThread t0; - MemLoc l; - t0.Write1(l); - { - ScopedThread t1; - t1.Write1(l); - } - t0.Write1(l); -} - -TEST(ThreadSanitizer, ThreadDetach1) { - ScopedThread t1(true); - MemLoc l; - t1.Write1(l); -} - -TEST(ThreadSanitizer, ThreadDetach2) { - ScopedThread t1; - MemLoc l; - t1.Write1(l); - t1.Detach(); -} - -static void *thread_alot_func(void *arg) { - (void)arg; - int usleep(unsigned); - usleep(50); - return 0; -} - -TEST(DISABLED_SLOW_ThreadSanitizer, ThreadALot) { - const int kThreads = 70000; - const int kAlive = 1000; - pthread_t threads[kAlive] = {}; - for (int i = 0; i < kThreads; i++) { - if (threads[i % kAlive]) - pthread_join(threads[i % kAlive], 0); - pthread_create(&threads[i % kAlive], 0, thread_alot_func, 0); - } - for (int i = 0; i < kAlive; i++) { - pthread_join(threads[i], 0); - } -} diff --git a/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cpp b/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cpp new file mode 100644 index 00000000000..9d79e0e9429 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cpp @@ -0,0 +1,58 @@ +//===-- tsan_thread.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_test_util.h" +#include "gtest/gtest.h" + +TEST(ThreadSanitizer, ThreadSync) { + MainThread t0; + MemLoc l; + t0.Write1(l); + { + ScopedThread t1; + t1.Write1(l); + } + t0.Write1(l); +} + +TEST(ThreadSanitizer, ThreadDetach1) { + ScopedThread t1(true); + MemLoc l; + t1.Write1(l); +} + +TEST(ThreadSanitizer, ThreadDetach2) { + ScopedThread t1; + MemLoc l; + t1.Write1(l); + t1.Detach(); +} + +static void *thread_alot_func(void *arg) { + (void)arg; + int usleep(unsigned); + usleep(50); + return 0; +} + +TEST(DISABLED_SLOW_ThreadSanitizer, ThreadALot) { + const int kThreads = 70000; + const int kAlive = 1000; + pthread_t threads[kAlive] = {}; + for (int i = 0; i < kThreads; i++) { + if (threads[i % kAlive]) + pthread_join(threads[i % kAlive], 0); + pthread_create(&threads[i % kAlive], 0, thread_alot_func, 0); + } + for (int i = 0; i < kAlive; i++) { + pthread_join(threads[i], 0); + } +} diff --git a/compiler-rt/lib/tsan/tests/unit/CMakeLists.txt b/compiler-rt/lib/tsan/tests/unit/CMakeLists.txt index c08508d5079..79e334a2c66 100644 --- a/compiler-rt/lib/tsan/tests/unit/CMakeLists.txt +++ b/compiler-rt/lib/tsan/tests/unit/CMakeLists.txt @@ -1,12 +1,13 @@ set(TSAN_UNIT_TEST_SOURCES - tsan_clock_test.cc - tsan_flags_test.cc - tsan_mman_test.cc - tsan_mutex_test.cc - tsan_shadow_test.cc - tsan_stack_test.cc - tsan_sync_test.cc - tsan_unit_test_main.cc) + tsan_clock_test.cpp + tsan_flags_test.cpp + tsan_mman_test.cpp + tsan_mutex_test.cpp + tsan_shadow_test.cpp + tsan_stack_test.cpp + tsan_sync_test.cpp + tsan_unit_test_main.cpp + ) add_tsan_unittest(TsanUnitTest SOURCES ${TSAN_UNIT_TEST_SOURCES}) diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc deleted file mode 100644 index 43d0a012c96..00000000000 --- a/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc +++ /dev/null @@ -1,493 +0,0 @@ -//===-- tsan_clock_test.cc ------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_clock.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" -#include -#include - -namespace __tsan { - -ClockCache cache; - -TEST(Clock, VectorBasic) { - ThreadClock clk(0); - ASSERT_EQ(clk.size(), 1U); - clk.tick(); - ASSERT_EQ(clk.size(), 1U); - ASSERT_EQ(clk.get(0), 1U); - clk.set(&cache, 3, clk.get(3) + 1); - ASSERT_EQ(clk.size(), 4U); - ASSERT_EQ(clk.get(0), 1U); - ASSERT_EQ(clk.get(1), 0U); - ASSERT_EQ(clk.get(2), 0U); - ASSERT_EQ(clk.get(3), 1U); - clk.set(&cache, 3, clk.get(3) + 1); - ASSERT_EQ(clk.get(3), 2U); -} - -TEST(Clock, ChunkedBasic) { - ThreadClock vector(0); - SyncClock chunked; - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 0U); - vector.acquire(&cache, &chunked); - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 0U); - vector.release(&cache, &chunked); - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 1U); - vector.acq_rel(&cache, &chunked); - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 1U); - chunked.Reset(&cache); -} - -static const uptr interesting_sizes[] = {0, 1, 2, 30, 61, 62, 63, 64, 65, 66, - 100, 124, 125, 126, 127, 128, 129, 130, 188, 189, 190, 191, 192, 193, 254, - 255}; - -TEST(Clock, Iter) { - const uptr n = ARRAY_SIZE(interesting_sizes); - for (uptr fi = 0; fi < n; fi++) { - const uptr size = interesting_sizes[fi]; - SyncClock sync; - ThreadClock vector(0); - for (uptr i = 0; i < size; i++) - vector.set(&cache, i, i + 1); - if (size != 0) - vector.release(&cache, &sync); - uptr i = 0; - for (ClockElem &ce : sync) { - ASSERT_LT(i, size); - ASSERT_EQ(sync.get_clean(i), ce.epoch); - i++; - } - ASSERT_EQ(i, size); - sync.Reset(&cache); - } -} - -TEST(Clock, AcquireRelease) { - ThreadClock vector1(100); - vector1.tick(); - SyncClock chunked; - vector1.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 101U); - ThreadClock vector2(0); - vector2.acquire(&cache, &chunked); - ASSERT_EQ(vector2.size(), 101U); - ASSERT_EQ(vector2.get(0), 0U); - ASSERT_EQ(vector2.get(1), 0U); - ASSERT_EQ(vector2.get(99), 0U); - ASSERT_EQ(vector2.get(100), 1U); - chunked.Reset(&cache); -} - -TEST(Clock, RepeatedAcquire) { - ThreadClock thr1(1); - thr1.tick(); - ThreadClock thr2(2); - thr2.tick(); - - SyncClock sync; - thr1.ReleaseStore(&cache, &sync); - - thr2.acquire(&cache, &sync); - thr2.acquire(&cache, &sync); - - sync.Reset(&cache); -} - -TEST(Clock, ManyThreads) { - SyncClock chunked; - for (unsigned i = 0; i < 200; i++) { - ThreadClock vector(0); - vector.tick(); - vector.set(&cache, i, i + 1); - vector.release(&cache, &chunked); - ASSERT_EQ(i + 1, chunked.size()); - vector.acquire(&cache, &chunked); - ASSERT_EQ(i + 1, vector.size()); - } - - for (unsigned i = 0; i < 200; i++) { - printf("i=%d\n", i); - ASSERT_EQ(i + 1, chunked.get(i)); - } - - ThreadClock vector(1); - vector.acquire(&cache, &chunked); - ASSERT_EQ(200U, vector.size()); - for (unsigned i = 0; i < 200; i++) - ASSERT_EQ(i + 1, vector.get(i)); - - chunked.Reset(&cache); -} - -TEST(Clock, DifferentSizes) { - { - ThreadClock vector1(10); - vector1.tick(); - ThreadClock vector2(20); - vector2.tick(); - { - SyncClock chunked; - vector1.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 11U); - vector2.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 21U); - chunked.Reset(&cache); - } - { - SyncClock chunked; - vector2.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 21U); - vector1.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 21U); - chunked.Reset(&cache); - } - { - SyncClock chunked; - vector1.release(&cache, &chunked); - vector2.acquire(&cache, &chunked); - ASSERT_EQ(vector2.size(), 21U); - chunked.Reset(&cache); - } - { - SyncClock chunked; - vector2.release(&cache, &chunked); - vector1.acquire(&cache, &chunked); - ASSERT_EQ(vector1.size(), 21U); - chunked.Reset(&cache); - } - } -} - -TEST(Clock, Growth) { - { - ThreadClock vector(10); - vector.tick(); - vector.set(&cache, 5, 42); - SyncClock sync; - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), 11U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(1), 0ULL); - ASSERT_EQ(sync.get(5), 42ULL); - ASSERT_EQ(sync.get(9), 0ULL); - ASSERT_EQ(sync.get(10), 1ULL); - sync.Reset(&cache); - } - { - ThreadClock vector1(10); - vector1.tick(); - ThreadClock vector2(20); - vector2.tick(); - SyncClock sync; - vector1.release(&cache, &sync); - vector2.release(&cache, &sync); - ASSERT_EQ(sync.size(), 21U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(10), 1ULL); - ASSERT_EQ(sync.get(19), 0ULL); - ASSERT_EQ(sync.get(20), 1ULL); - sync.Reset(&cache); - } - { - ThreadClock vector(100); - vector.tick(); - vector.set(&cache, 5, 42); - vector.set(&cache, 90, 84); - SyncClock sync; - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), 101U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(1), 0ULL); - ASSERT_EQ(sync.get(5), 42ULL); - ASSERT_EQ(sync.get(60), 0ULL); - ASSERT_EQ(sync.get(70), 0ULL); - ASSERT_EQ(sync.get(90), 84ULL); - ASSERT_EQ(sync.get(99), 0ULL); - ASSERT_EQ(sync.get(100), 1ULL); - sync.Reset(&cache); - } - { - ThreadClock vector1(10); - vector1.tick(); - ThreadClock vector2(100); - vector2.tick(); - SyncClock sync; - vector1.release(&cache, &sync); - vector2.release(&cache, &sync); - ASSERT_EQ(sync.size(), 101U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(10), 1ULL); - ASSERT_EQ(sync.get(99), 0ULL); - ASSERT_EQ(sync.get(100), 1ULL); - sync.Reset(&cache); - } -} - -TEST(Clock, Growth2) { - // Test clock growth for every pair of sizes: - const uptr n = ARRAY_SIZE(interesting_sizes); - for (uptr fi = 0; fi < n; fi++) { - for (uptr ti = fi + 1; ti < n; ti++) { - const uptr from = interesting_sizes[fi]; - const uptr to = interesting_sizes[ti]; - SyncClock sync; - ThreadClock vector(0); - for (uptr i = 0; i < from; i++) - vector.set(&cache, i, i + 1); - if (from != 0) - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), from); - for (uptr i = 0; i < from; i++) - ASSERT_EQ(sync.get(i), i + 1); - for (uptr i = 0; i < to; i++) - vector.set(&cache, i, i + 1); - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), to); - for (uptr i = 0; i < to; i++) - ASSERT_EQ(sync.get(i), i + 1); - vector.set(&cache, to + 1, to + 1); - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), to + 2); - for (uptr i = 0; i < to; i++) - ASSERT_EQ(sync.get(i), i + 1); - ASSERT_EQ(sync.get(to), 0U); - ASSERT_EQ(sync.get(to + 1), to + 1); - sync.Reset(&cache); - } - } -} - -const uptr kThreads = 4; -const uptr kClocks = 4; - -// SimpleSyncClock and SimpleThreadClock implement the same thing as -// SyncClock and ThreadClock, but in a very simple way. -struct SimpleSyncClock { - u64 clock[kThreads]; - uptr size; - - SimpleSyncClock() { - Reset(); - } - - void Reset() { - size = 0; - for (uptr i = 0; i < kThreads; i++) - clock[i] = 0; - } - - bool verify(const SyncClock *other) const { - for (uptr i = 0; i < min(size, other->size()); i++) { - if (clock[i] != other->get(i)) - return false; - } - for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { - if (i < size && clock[i] != 0) - return false; - if (i < other->size() && other->get(i) != 0) - return false; - } - return true; - } -}; - -struct SimpleThreadClock { - u64 clock[kThreads]; - uptr size; - unsigned tid; - - explicit SimpleThreadClock(unsigned tid) { - this->tid = tid; - size = tid + 1; - for (uptr i = 0; i < kThreads; i++) - clock[i] = 0; - } - - void tick() { - clock[tid]++; - } - - void acquire(const SimpleSyncClock *src) { - if (size < src->size) - size = src->size; - for (uptr i = 0; i < kThreads; i++) - clock[i] = max(clock[i], src->clock[i]); - } - - void release(SimpleSyncClock *dst) const { - if (dst->size < size) - dst->size = size; - for (uptr i = 0; i < kThreads; i++) - dst->clock[i] = max(dst->clock[i], clock[i]); - } - - void acq_rel(SimpleSyncClock *dst) { - acquire(dst); - release(dst); - } - - void ReleaseStore(SimpleSyncClock *dst) const { - if (dst->size < size) - dst->size = size; - for (uptr i = 0; i < kThreads; i++) - dst->clock[i] = clock[i]; - } - - bool verify(const ThreadClock *other) const { - for (uptr i = 0; i < min(size, other->size()); i++) { - if (clock[i] != other->get(i)) - return false; - } - for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { - if (i < size && clock[i] != 0) - return false; - if (i < other->size() && other->get(i) != 0) - return false; - } - return true; - } -}; - -static bool ClockFuzzer(bool printing) { - // Create kThreads thread clocks. - SimpleThreadClock *thr0[kThreads]; - ThreadClock *thr1[kThreads]; - unsigned reused[kThreads]; - for (unsigned i = 0; i < kThreads; i++) { - reused[i] = 0; - thr0[i] = new SimpleThreadClock(i); - thr1[i] = new ThreadClock(i, reused[i]); - } - - // Create kClocks sync clocks. - SimpleSyncClock *sync0[kClocks]; - SyncClock *sync1[kClocks]; - for (unsigned i = 0; i < kClocks; i++) { - sync0[i] = new SimpleSyncClock(); - sync1[i] = new SyncClock(); - } - - // Do N random operations (acquire, release, etc) and compare results - // for SimpleThread/SyncClock and real Thread/SyncClock. - for (int i = 0; i < 10000; i++) { - unsigned tid = rand() % kThreads; - unsigned cid = rand() % kClocks; - thr0[tid]->tick(); - thr1[tid]->tick(); - - switch (rand() % 6) { - case 0: - if (printing) - printf("acquire thr%d <- clk%d\n", tid, cid); - thr0[tid]->acquire(sync0[cid]); - thr1[tid]->acquire(&cache, sync1[cid]); - break; - case 1: - if (printing) - printf("release thr%d -> clk%d\n", tid, cid); - thr0[tid]->release(sync0[cid]); - thr1[tid]->release(&cache, sync1[cid]); - break; - case 2: - if (printing) - printf("acq_rel thr%d <> clk%d\n", tid, cid); - thr0[tid]->acq_rel(sync0[cid]); - thr1[tid]->acq_rel(&cache, sync1[cid]); - break; - case 3: - if (printing) - printf("rel_str thr%d >> clk%d\n", tid, cid); - thr0[tid]->ReleaseStore(sync0[cid]); - thr1[tid]->ReleaseStore(&cache, sync1[cid]); - break; - case 4: - if (printing) - printf("reset clk%d\n", cid); - sync0[cid]->Reset(); - sync1[cid]->Reset(&cache); - break; - case 5: - if (printing) - printf("reset thr%d\n", tid); - u64 epoch = thr0[tid]->clock[tid] + 1; - reused[tid]++; - delete thr0[tid]; - thr0[tid] = new SimpleThreadClock(tid); - thr0[tid]->clock[tid] = epoch; - delete thr1[tid]; - thr1[tid] = new ThreadClock(tid, reused[tid]); - thr1[tid]->set(epoch); - break; - } - - if (printing) { - for (unsigned i = 0; i < kThreads; i++) { - printf("thr%d: ", i); - thr1[i]->DebugDump(printf); - printf("\n"); - } - for (unsigned i = 0; i < kClocks; i++) { - printf("clk%d: ", i); - sync1[i]->DebugDump(printf); - printf("\n"); - } - - printf("\n"); - } - - if (!thr0[tid]->verify(thr1[tid]) || !sync0[cid]->verify(sync1[cid])) { - if (!printing) - return false; - printf("differs with model:\n"); - for (unsigned i = 0; i < kThreads; i++) { - printf("thr%d: clock=[", i); - for (uptr j = 0; j < thr0[i]->size; j++) - printf("%s%llu", j == 0 ? "" : ",", thr0[i]->clock[j]); - printf("]\n"); - } - for (unsigned i = 0; i < kClocks; i++) { - printf("clk%d: clock=[", i); - for (uptr j = 0; j < sync0[i]->size; j++) - printf("%s%llu", j == 0 ? "" : ",", sync0[i]->clock[j]); - printf("]\n"); - } - return false; - } - } - - for (unsigned i = 0; i < kClocks; i++) { - sync1[i]->Reset(&cache); - } - return true; -} - -TEST(Clock, Fuzzer) { - struct timeval tv; - gettimeofday(&tv, NULL); - int seed = tv.tv_sec + tv.tv_usec; - printf("seed=%d\n", seed); - srand(seed); - if (!ClockFuzzer(false)) { - // Redo the test with the same seed, but logging operations. - srand(seed); - ClockFuzzer(true); - ASSERT_TRUE(false); - } -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cpp new file mode 100644 index 00000000000..6d835ba85c3 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cpp @@ -0,0 +1,493 @@ +//===-- tsan_clock_test.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_clock.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" +#include +#include + +namespace __tsan { + +ClockCache cache; + +TEST(Clock, VectorBasic) { + ThreadClock clk(0); + ASSERT_EQ(clk.size(), 1U); + clk.tick(); + ASSERT_EQ(clk.size(), 1U); + ASSERT_EQ(clk.get(0), 1U); + clk.set(&cache, 3, clk.get(3) + 1); + ASSERT_EQ(clk.size(), 4U); + ASSERT_EQ(clk.get(0), 1U); + ASSERT_EQ(clk.get(1), 0U); + ASSERT_EQ(clk.get(2), 0U); + ASSERT_EQ(clk.get(3), 1U); + clk.set(&cache, 3, clk.get(3) + 1); + ASSERT_EQ(clk.get(3), 2U); +} + +TEST(Clock, ChunkedBasic) { + ThreadClock vector(0); + SyncClock chunked; + ASSERT_EQ(vector.size(), 1U); + ASSERT_EQ(chunked.size(), 0U); + vector.acquire(&cache, &chunked); + ASSERT_EQ(vector.size(), 1U); + ASSERT_EQ(chunked.size(), 0U); + vector.release(&cache, &chunked); + ASSERT_EQ(vector.size(), 1U); + ASSERT_EQ(chunked.size(), 1U); + vector.acq_rel(&cache, &chunked); + ASSERT_EQ(vector.size(), 1U); + ASSERT_EQ(chunked.size(), 1U); + chunked.Reset(&cache); +} + +static const uptr interesting_sizes[] = {0, 1, 2, 30, 61, 62, 63, 64, 65, 66, + 100, 124, 125, 126, 127, 128, 129, 130, 188, 189, 190, 191, 192, 193, 254, + 255}; + +TEST(Clock, Iter) { + const uptr n = ARRAY_SIZE(interesting_sizes); + for (uptr fi = 0; fi < n; fi++) { + const uptr size = interesting_sizes[fi]; + SyncClock sync; + ThreadClock vector(0); + for (uptr i = 0; i < size; i++) + vector.set(&cache, i, i + 1); + if (size != 0) + vector.release(&cache, &sync); + uptr i = 0; + for (ClockElem &ce : sync) { + ASSERT_LT(i, size); + ASSERT_EQ(sync.get_clean(i), ce.epoch); + i++; + } + ASSERT_EQ(i, size); + sync.Reset(&cache); + } +} + +TEST(Clock, AcquireRelease) { + ThreadClock vector1(100); + vector1.tick(); + SyncClock chunked; + vector1.release(&cache, &chunked); + ASSERT_EQ(chunked.size(), 101U); + ThreadClock vector2(0); + vector2.acquire(&cache, &chunked); + ASSERT_EQ(vector2.size(), 101U); + ASSERT_EQ(vector2.get(0), 0U); + ASSERT_EQ(vector2.get(1), 0U); + ASSERT_EQ(vector2.get(99), 0U); + ASSERT_EQ(vector2.get(100), 1U); + chunked.Reset(&cache); +} + +TEST(Clock, RepeatedAcquire) { + ThreadClock thr1(1); + thr1.tick(); + ThreadClock thr2(2); + thr2.tick(); + + SyncClock sync; + thr1.ReleaseStore(&cache, &sync); + + thr2.acquire(&cache, &sync); + thr2.acquire(&cache, &sync); + + sync.Reset(&cache); +} + +TEST(Clock, ManyThreads) { + SyncClock chunked; + for (unsigned i = 0; i < 200; i++) { + ThreadClock vector(0); + vector.tick(); + vector.set(&cache, i, i + 1); + vector.release(&cache, &chunked); + ASSERT_EQ(i + 1, chunked.size()); + vector.acquire(&cache, &chunked); + ASSERT_EQ(i + 1, vector.size()); + } + + for (unsigned i = 0; i < 200; i++) { + printf("i=%d\n", i); + ASSERT_EQ(i + 1, chunked.get(i)); + } + + ThreadClock vector(1); + vector.acquire(&cache, &chunked); + ASSERT_EQ(200U, vector.size()); + for (unsigned i = 0; i < 200; i++) + ASSERT_EQ(i + 1, vector.get(i)); + + chunked.Reset(&cache); +} + +TEST(Clock, DifferentSizes) { + { + ThreadClock vector1(10); + vector1.tick(); + ThreadClock vector2(20); + vector2.tick(); + { + SyncClock chunked; + vector1.release(&cache, &chunked); + ASSERT_EQ(chunked.size(), 11U); + vector2.release(&cache, &chunked); + ASSERT_EQ(chunked.size(), 21U); + chunked.Reset(&cache); + } + { + SyncClock chunked; + vector2.release(&cache, &chunked); + ASSERT_EQ(chunked.size(), 21U); + vector1.release(&cache, &chunked); + ASSERT_EQ(chunked.size(), 21U); + chunked.Reset(&cache); + } + { + SyncClock chunked; + vector1.release(&cache, &chunked); + vector2.acquire(&cache, &chunked); + ASSERT_EQ(vector2.size(), 21U); + chunked.Reset(&cache); + } + { + SyncClock chunked; + vector2.release(&cache, &chunked); + vector1.acquire(&cache, &chunked); + ASSERT_EQ(vector1.size(), 21U); + chunked.Reset(&cache); + } + } +} + +TEST(Clock, Growth) { + { + ThreadClock vector(10); + vector.tick(); + vector.set(&cache, 5, 42); + SyncClock sync; + vector.release(&cache, &sync); + ASSERT_EQ(sync.size(), 11U); + ASSERT_EQ(sync.get(0), 0ULL); + ASSERT_EQ(sync.get(1), 0ULL); + ASSERT_EQ(sync.get(5), 42ULL); + ASSERT_EQ(sync.get(9), 0ULL); + ASSERT_EQ(sync.get(10), 1ULL); + sync.Reset(&cache); + } + { + ThreadClock vector1(10); + vector1.tick(); + ThreadClock vector2(20); + vector2.tick(); + SyncClock sync; + vector1.release(&cache, &sync); + vector2.release(&cache, &sync); + ASSERT_EQ(sync.size(), 21U); + ASSERT_EQ(sync.get(0), 0ULL); + ASSERT_EQ(sync.get(10), 1ULL); + ASSERT_EQ(sync.get(19), 0ULL); + ASSERT_EQ(sync.get(20), 1ULL); + sync.Reset(&cache); + } + { + ThreadClock vector(100); + vector.tick(); + vector.set(&cache, 5, 42); + vector.set(&cache, 90, 84); + SyncClock sync; + vector.release(&cache, &sync); + ASSERT_EQ(sync.size(), 101U); + ASSERT_EQ(sync.get(0), 0ULL); + ASSERT_EQ(sync.get(1), 0ULL); + ASSERT_EQ(sync.get(5), 42ULL); + ASSERT_EQ(sync.get(60), 0ULL); + ASSERT_EQ(sync.get(70), 0ULL); + ASSERT_EQ(sync.get(90), 84ULL); + ASSERT_EQ(sync.get(99), 0ULL); + ASSERT_EQ(sync.get(100), 1ULL); + sync.Reset(&cache); + } + { + ThreadClock vector1(10); + vector1.tick(); + ThreadClock vector2(100); + vector2.tick(); + SyncClock sync; + vector1.release(&cache, &sync); + vector2.release(&cache, &sync); + ASSERT_EQ(sync.size(), 101U); + ASSERT_EQ(sync.get(0), 0ULL); + ASSERT_EQ(sync.get(10), 1ULL); + ASSERT_EQ(sync.get(99), 0ULL); + ASSERT_EQ(sync.get(100), 1ULL); + sync.Reset(&cache); + } +} + +TEST(Clock, Growth2) { + // Test clock growth for every pair of sizes: + const uptr n = ARRAY_SIZE(interesting_sizes); + for (uptr fi = 0; fi < n; fi++) { + for (uptr ti = fi + 1; ti < n; ti++) { + const uptr from = interesting_sizes[fi]; + const uptr to = interesting_sizes[ti]; + SyncClock sync; + ThreadClock vector(0); + for (uptr i = 0; i < from; i++) + vector.set(&cache, i, i + 1); + if (from != 0) + vector.release(&cache, &sync); + ASSERT_EQ(sync.size(), from); + for (uptr i = 0; i < from; i++) + ASSERT_EQ(sync.get(i), i + 1); + for (uptr i = 0; i < to; i++) + vector.set(&cache, i, i + 1); + vector.release(&cache, &sync); + ASSERT_EQ(sync.size(), to); + for (uptr i = 0; i < to; i++) + ASSERT_EQ(sync.get(i), i + 1); + vector.set(&cache, to + 1, to + 1); + vector.release(&cache, &sync); + ASSERT_EQ(sync.size(), to + 2); + for (uptr i = 0; i < to; i++) + ASSERT_EQ(sync.get(i), i + 1); + ASSERT_EQ(sync.get(to), 0U); + ASSERT_EQ(sync.get(to + 1), to + 1); + sync.Reset(&cache); + } + } +} + +const uptr kThreads = 4; +const uptr kClocks = 4; + +// SimpleSyncClock and SimpleThreadClock implement the same thing as +// SyncClock and ThreadClock, but in a very simple way. +struct SimpleSyncClock { + u64 clock[kThreads]; + uptr size; + + SimpleSyncClock() { + Reset(); + } + + void Reset() { + size = 0; + for (uptr i = 0; i < kThreads; i++) + clock[i] = 0; + } + + bool verify(const SyncClock *other) const { + for (uptr i = 0; i < min(size, other->size()); i++) { + if (clock[i] != other->get(i)) + return false; + } + for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { + if (i < size && clock[i] != 0) + return false; + if (i < other->size() && other->get(i) != 0) + return false; + } + return true; + } +}; + +struct SimpleThreadClock { + u64 clock[kThreads]; + uptr size; + unsigned tid; + + explicit SimpleThreadClock(unsigned tid) { + this->tid = tid; + size = tid + 1; + for (uptr i = 0; i < kThreads; i++) + clock[i] = 0; + } + + void tick() { + clock[tid]++; + } + + void acquire(const SimpleSyncClock *src) { + if (size < src->size) + size = src->size; + for (uptr i = 0; i < kThreads; i++) + clock[i] = max(clock[i], src->clock[i]); + } + + void release(SimpleSyncClock *dst) const { + if (dst->size < size) + dst->size = size; + for (uptr i = 0; i < kThreads; i++) + dst->clock[i] = max(dst->clock[i], clock[i]); + } + + void acq_rel(SimpleSyncClock *dst) { + acquire(dst); + release(dst); + } + + void ReleaseStore(SimpleSyncClock *dst) const { + if (dst->size < size) + dst->size = size; + for (uptr i = 0; i < kThreads; i++) + dst->clock[i] = clock[i]; + } + + bool verify(const ThreadClock *other) const { + for (uptr i = 0; i < min(size, other->size()); i++) { + if (clock[i] != other->get(i)) + return false; + } + for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { + if (i < size && clock[i] != 0) + return false; + if (i < other->size() && other->get(i) != 0) + return false; + } + return true; + } +}; + +static bool ClockFuzzer(bool printing) { + // Create kThreads thread clocks. + SimpleThreadClock *thr0[kThreads]; + ThreadClock *thr1[kThreads]; + unsigned reused[kThreads]; + for (unsigned i = 0; i < kThreads; i++) { + reused[i] = 0; + thr0[i] = new SimpleThreadClock(i); + thr1[i] = new ThreadClock(i, reused[i]); + } + + // Create kClocks sync clocks. + SimpleSyncClock *sync0[kClocks]; + SyncClock *sync1[kClocks]; + for (unsigned i = 0; i < kClocks; i++) { + sync0[i] = new SimpleSyncClock(); + sync1[i] = new SyncClock(); + } + + // Do N random operations (acquire, release, etc) and compare results + // for SimpleThread/SyncClock and real Thread/SyncClock. + for (int i = 0; i < 10000; i++) { + unsigned tid = rand() % kThreads; + unsigned cid = rand() % kClocks; + thr0[tid]->tick(); + thr1[tid]->tick(); + + switch (rand() % 6) { + case 0: + if (printing) + printf("acquire thr%d <- clk%d\n", tid, cid); + thr0[tid]->acquire(sync0[cid]); + thr1[tid]->acquire(&cache, sync1[cid]); + break; + case 1: + if (printing) + printf("release thr%d -> clk%d\n", tid, cid); + thr0[tid]->release(sync0[cid]); + thr1[tid]->release(&cache, sync1[cid]); + break; + case 2: + if (printing) + printf("acq_rel thr%d <> clk%d\n", tid, cid); + thr0[tid]->acq_rel(sync0[cid]); + thr1[tid]->acq_rel(&cache, sync1[cid]); + break; + case 3: + if (printing) + printf("rel_str thr%d >> clk%d\n", tid, cid); + thr0[tid]->ReleaseStore(sync0[cid]); + thr1[tid]->ReleaseStore(&cache, sync1[cid]); + break; + case 4: + if (printing) + printf("reset clk%d\n", cid); + sync0[cid]->Reset(); + sync1[cid]->Reset(&cache); + break; + case 5: + if (printing) + printf("reset thr%d\n", tid); + u64 epoch = thr0[tid]->clock[tid] + 1; + reused[tid]++; + delete thr0[tid]; + thr0[tid] = new SimpleThreadClock(tid); + thr0[tid]->clock[tid] = epoch; + delete thr1[tid]; + thr1[tid] = new ThreadClock(tid, reused[tid]); + thr1[tid]->set(epoch); + break; + } + + if (printing) { + for (unsigned i = 0; i < kThreads; i++) { + printf("thr%d: ", i); + thr1[i]->DebugDump(printf); + printf("\n"); + } + for (unsigned i = 0; i < kClocks; i++) { + printf("clk%d: ", i); + sync1[i]->DebugDump(printf); + printf("\n"); + } + + printf("\n"); + } + + if (!thr0[tid]->verify(thr1[tid]) || !sync0[cid]->verify(sync1[cid])) { + if (!printing) + return false; + printf("differs with model:\n"); + for (unsigned i = 0; i < kThreads; i++) { + printf("thr%d: clock=[", i); + for (uptr j = 0; j < thr0[i]->size; j++) + printf("%s%llu", j == 0 ? "" : ",", thr0[i]->clock[j]); + printf("]\n"); + } + for (unsigned i = 0; i < kClocks; i++) { + printf("clk%d: clock=[", i); + for (uptr j = 0; j < sync0[i]->size; j++) + printf("%s%llu", j == 0 ? "" : ",", sync0[i]->clock[j]); + printf("]\n"); + } + return false; + } + } + + for (unsigned i = 0; i < kClocks; i++) { + sync1[i]->Reset(&cache); + } + return true; +} + +TEST(Clock, Fuzzer) { + struct timeval tv; + gettimeofday(&tv, NULL); + int seed = tv.tv_sec + tv.tv_usec; + printf("seed=%d\n", seed); + srand(seed); + if (!ClockFuzzer(false)) { + // Redo the test with the same seed, but logging operations. + srand(seed); + ClockFuzzer(true); + ASSERT_TRUE(false); + } +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cc deleted file mode 100644 index 8c544b0d737..00000000000 --- a/compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cc +++ /dev/null @@ -1,54 +0,0 @@ -//===-- tsan_dense_alloc_test.cc ------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_dense_alloc.h" -#include "tsan_rtl.h" -#include "tsan_mman.h" -#include "gtest/gtest.h" - -#include -#include -#include - -namespace __tsan { - -TEST(DenseSlabAlloc, Basic) { - typedef DenseSlabAlloc Alloc; - typedef Alloc::Cache Cache; - typedef Alloc::IndexT IndexT; - const int N = 1000; - - Alloc alloc; - Cache cache; - alloc.InitCache(&cache); - - IndexT blocks[N]; - for (int ntry = 0; ntry < 3; ntry++) { - for (int i = 0; i < N; i++) { - IndexT idx = alloc.Alloc(&cache); - blocks[i] = idx; - EXPECT_NE(idx, 0U); - int *v = alloc.Map(idx); - *v = i; - } - - for (int i = 0; i < N; i++) { - IndexT idx = blocks[i]; - int *v = alloc.Map(idx); - EXPECT_EQ(*v, i); - alloc.Free(&cache, idx); - } - - alloc.FlushCache(&cache); - } -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cpp new file mode 100644 index 00000000000..05045622881 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cpp @@ -0,0 +1,54 @@ +//===-- tsan_dense_alloc_test.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_dense_alloc.h" +#include "tsan_rtl.h" +#include "tsan_mman.h" +#include "gtest/gtest.h" + +#include +#include +#include + +namespace __tsan { + +TEST(DenseSlabAlloc, Basic) { + typedef DenseSlabAlloc Alloc; + typedef Alloc::Cache Cache; + typedef Alloc::IndexT IndexT; + const int N = 1000; + + Alloc alloc; + Cache cache; + alloc.InitCache(&cache); + + IndexT blocks[N]; + for (int ntry = 0; ntry < 3; ntry++) { + for (int i = 0; i < N; i++) { + IndexT idx = alloc.Alloc(&cache); + blocks[i] = idx; + EXPECT_NE(idx, 0U); + int *v = alloc.Map(idx); + *v = i; + } + + for (int i = 0; i < N; i++) { + IndexT idx = blocks[i]; + int *v = alloc.Map(idx); + EXPECT_EQ(*v, i); + alloc.Free(&cache, idx); + } + + alloc.FlushCache(&cache); + } +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc deleted file mode 100644 index ebdb28f6dff..00000000000 --- a/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc +++ /dev/null @@ -1,173 +0,0 @@ -//===-- tsan_flags_test.cc ------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_flags.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" -#include - -namespace __tsan { - -TEST(Flags, Basic) { - // At least should not crash. - Flags f; - InitializeFlags(&f, 0); - InitializeFlags(&f, ""); -} - -TEST(Flags, DefaultValues) { - Flags f; - - f.enable_annotations = false; - InitializeFlags(&f, ""); - EXPECT_EQ(true, f.enable_annotations); -} - -static const char *options1 = - " enable_annotations=0" - " suppress_equal_stacks=0" - " suppress_equal_addresses=0" - " report_bugs=0" - " report_thread_leaks=0" - " report_destroy_locked=0" - " report_mutex_bugs=0" - " report_signal_unsafe=0" - " report_atomic_races=0" - " force_seq_cst_atomics=0" - " print_benign=0" - " halt_on_error=0" - " atexit_sleep_ms=222" - " profile_memory=qqq" - " flush_memory_ms=444" - " flush_symbolizer_ms=555" - " memory_limit_mb=666" - " stop_on_start=0" - " running_on_valgrind=0" - " history_size=5" - " io_sync=1" - " die_after_fork=true" - ""; - -static const char *options2 = - " enable_annotations=true" - " suppress_equal_stacks=true" - " suppress_equal_addresses=true" - " report_bugs=true" - " report_thread_leaks=true" - " report_destroy_locked=true" - " report_mutex_bugs=true" - " report_signal_unsafe=true" - " report_atomic_races=true" - " force_seq_cst_atomics=true" - " print_benign=true" - " halt_on_error=true" - " atexit_sleep_ms=123" - " profile_memory=bbbbb" - " flush_memory_ms=234" - " flush_symbolizer_ms=345" - " memory_limit_mb=456" - " stop_on_start=true" - " running_on_valgrind=true" - " history_size=6" - " io_sync=2" - " die_after_fork=false" - ""; - -void VerifyOptions1(Flags *f) { - EXPECT_EQ(f->enable_annotations, 0); - EXPECT_EQ(f->suppress_equal_stacks, 0); - EXPECT_EQ(f->suppress_equal_addresses, 0); - EXPECT_EQ(f->report_bugs, 0); - EXPECT_EQ(f->report_thread_leaks, 0); - EXPECT_EQ(f->report_destroy_locked, 0); - EXPECT_EQ(f->report_mutex_bugs, 0); - EXPECT_EQ(f->report_signal_unsafe, 0); - EXPECT_EQ(f->report_atomic_races, 0); - EXPECT_EQ(f->force_seq_cst_atomics, 0); - EXPECT_EQ(f->print_benign, 0); - EXPECT_EQ(f->halt_on_error, 0); - EXPECT_EQ(f->atexit_sleep_ms, 222); - EXPECT_EQ(f->profile_memory, std::string("qqq")); - EXPECT_EQ(f->flush_memory_ms, 444); - EXPECT_EQ(f->flush_symbolizer_ms, 555); - EXPECT_EQ(f->memory_limit_mb, 666); - EXPECT_EQ(f->stop_on_start, 0); - EXPECT_EQ(f->running_on_valgrind, 0); - EXPECT_EQ(f->history_size, 5); - EXPECT_EQ(f->io_sync, 1); - EXPECT_EQ(f->die_after_fork, true); -} - -void VerifyOptions2(Flags *f) { - EXPECT_EQ(f->enable_annotations, true); - EXPECT_EQ(f->suppress_equal_stacks, true); - EXPECT_EQ(f->suppress_equal_addresses, true); - EXPECT_EQ(f->report_bugs, true); - EXPECT_EQ(f->report_thread_leaks, true); - EXPECT_EQ(f->report_destroy_locked, true); - EXPECT_EQ(f->report_mutex_bugs, true); - EXPECT_EQ(f->report_signal_unsafe, true); - EXPECT_EQ(f->report_atomic_races, true); - EXPECT_EQ(f->force_seq_cst_atomics, true); - EXPECT_EQ(f->print_benign, true); - EXPECT_EQ(f->halt_on_error, true); - EXPECT_EQ(f->atexit_sleep_ms, 123); - EXPECT_EQ(f->profile_memory, std::string("bbbbb")); - EXPECT_EQ(f->flush_memory_ms, 234); - EXPECT_EQ(f->flush_symbolizer_ms, 345); - EXPECT_EQ(f->memory_limit_mb, 456); - EXPECT_EQ(f->stop_on_start, true); - EXPECT_EQ(f->running_on_valgrind, true); - EXPECT_EQ(f->history_size, 6); - EXPECT_EQ(f->io_sync, 2); - EXPECT_EQ(f->die_after_fork, false); -} - -static const char *test_default_options; -extern "C" const char *__tsan_default_options() { - return test_default_options; -} - -TEST(Flags, ParseDefaultOptions) { - Flags f; - - test_default_options = options1; - InitializeFlags(&f, ""); - VerifyOptions1(&f); - - test_default_options = options2; - InitializeFlags(&f, ""); - VerifyOptions2(&f); -} - -TEST(Flags, ParseEnvOptions) { - Flags f; - - InitializeFlags(&f, options1); - VerifyOptions1(&f); - - InitializeFlags(&f, options2); - VerifyOptions2(&f); -} - -TEST(Flags, ParsePriority) { - Flags f; - - test_default_options = options2; - InitializeFlags(&f, options1); - VerifyOptions1(&f); - - test_default_options = options1; - InitializeFlags(&f, options2); - VerifyOptions2(&f); -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cpp new file mode 100644 index 00000000000..ace760071e9 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cpp @@ -0,0 +1,173 @@ +//===-- tsan_flags_test.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_flags.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" +#include + +namespace __tsan { + +TEST(Flags, Basic) { + // At least should not crash. + Flags f; + InitializeFlags(&f, 0); + InitializeFlags(&f, ""); +} + +TEST(Flags, DefaultValues) { + Flags f; + + f.enable_annotations = false; + InitializeFlags(&f, ""); + EXPECT_EQ(true, f.enable_annotations); +} + +static const char *options1 = + " enable_annotations=0" + " suppress_equal_stacks=0" + " suppress_equal_addresses=0" + " report_bugs=0" + " report_thread_leaks=0" + " report_destroy_locked=0" + " report_mutex_bugs=0" + " report_signal_unsafe=0" + " report_atomic_races=0" + " force_seq_cst_atomics=0" + " print_benign=0" + " halt_on_error=0" + " atexit_sleep_ms=222" + " profile_memory=qqq" + " flush_memory_ms=444" + " flush_symbolizer_ms=555" + " memory_limit_mb=666" + " stop_on_start=0" + " running_on_valgrind=0" + " history_size=5" + " io_sync=1" + " die_after_fork=true" + ""; + +static const char *options2 = + " enable_annotations=true" + " suppress_equal_stacks=true" + " suppress_equal_addresses=true" + " report_bugs=true" + " report_thread_leaks=true" + " report_destroy_locked=true" + " report_mutex_bugs=true" + " report_signal_unsafe=true" + " report_atomic_races=true" + " force_seq_cst_atomics=true" + " print_benign=true" + " halt_on_error=true" + " atexit_sleep_ms=123" + " profile_memory=bbbbb" + " flush_memory_ms=234" + " flush_symbolizer_ms=345" + " memory_limit_mb=456" + " stop_on_start=true" + " running_on_valgrind=true" + " history_size=6" + " io_sync=2" + " die_after_fork=false" + ""; + +void VerifyOptions1(Flags *f) { + EXPECT_EQ(f->enable_annotations, 0); + EXPECT_EQ(f->suppress_equal_stacks, 0); + EXPECT_EQ(f->suppress_equal_addresses, 0); + EXPECT_EQ(f->report_bugs, 0); + EXPECT_EQ(f->report_thread_leaks, 0); + EXPECT_EQ(f->report_destroy_locked, 0); + EXPECT_EQ(f->report_mutex_bugs, 0); + EXPECT_EQ(f->report_signal_unsafe, 0); + EXPECT_EQ(f->report_atomic_races, 0); + EXPECT_EQ(f->force_seq_cst_atomics, 0); + EXPECT_EQ(f->print_benign, 0); + EXPECT_EQ(f->halt_on_error, 0); + EXPECT_EQ(f->atexit_sleep_ms, 222); + EXPECT_EQ(f->profile_memory, std::string("qqq")); + EXPECT_EQ(f->flush_memory_ms, 444); + EXPECT_EQ(f->flush_symbolizer_ms, 555); + EXPECT_EQ(f->memory_limit_mb, 666); + EXPECT_EQ(f->stop_on_start, 0); + EXPECT_EQ(f->running_on_valgrind, 0); + EXPECT_EQ(f->history_size, 5); + EXPECT_EQ(f->io_sync, 1); + EXPECT_EQ(f->die_after_fork, true); +} + +void VerifyOptions2(Flags *f) { + EXPECT_EQ(f->enable_annotations, true); + EXPECT_EQ(f->suppress_equal_stacks, true); + EXPECT_EQ(f->suppress_equal_addresses, true); + EXPECT_EQ(f->report_bugs, true); + EXPECT_EQ(f->report_thread_leaks, true); + EXPECT_EQ(f->report_destroy_locked, true); + EXPECT_EQ(f->report_mutex_bugs, true); + EXPECT_EQ(f->report_signal_unsafe, true); + EXPECT_EQ(f->report_atomic_races, true); + EXPECT_EQ(f->force_seq_cst_atomics, true); + EXPECT_EQ(f->print_benign, true); + EXPECT_EQ(f->halt_on_error, true); + EXPECT_EQ(f->atexit_sleep_ms, 123); + EXPECT_EQ(f->profile_memory, std::string("bbbbb")); + EXPECT_EQ(f->flush_memory_ms, 234); + EXPECT_EQ(f->flush_symbolizer_ms, 345); + EXPECT_EQ(f->memory_limit_mb, 456); + EXPECT_EQ(f->stop_on_start, true); + EXPECT_EQ(f->running_on_valgrind, true); + EXPECT_EQ(f->history_size, 6); + EXPECT_EQ(f->io_sync, 2); + EXPECT_EQ(f->die_after_fork, false); +} + +static const char *test_default_options; +extern "C" const char *__tsan_default_options() { + return test_default_options; +} + +TEST(Flags, ParseDefaultOptions) { + Flags f; + + test_default_options = options1; + InitializeFlags(&f, ""); + VerifyOptions1(&f); + + test_default_options = options2; + InitializeFlags(&f, ""); + VerifyOptions2(&f); +} + +TEST(Flags, ParseEnvOptions) { + Flags f; + + InitializeFlags(&f, options1); + VerifyOptions1(&f); + + InitializeFlags(&f, options2); + VerifyOptions2(&f); +} + +TEST(Flags, ParsePriority) { + Flags f; + + test_default_options = options2; + InitializeFlags(&f, options1); + VerifyOptions1(&f); + + test_default_options = options1; + InitializeFlags(&f, options2); + VerifyOptions2(&f); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc deleted file mode 100644 index b2789b70fc7..00000000000 --- a/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc +++ /dev/null @@ -1,196 +0,0 @@ -//===-- tsan_mman_test.cc -------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include -#include -#include "tsan_mman.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" - -namespace __tsan { - -TEST(Mman, Internal) { - char *p = (char*)internal_alloc(MBlockScopedBuf, 10); - EXPECT_NE(p, (char*)0); - char *p2 = (char*)internal_alloc(MBlockScopedBuf, 20); - EXPECT_NE(p2, (char*)0); - EXPECT_NE(p2, p); - for (int i = 0; i < 10; i++) { - p[i] = 42; - } - for (int i = 0; i < 20; i++) { - ((char*)p2)[i] = 42; - } - internal_free(p); - internal_free(p2); -} - -TEST(Mman, User) { - ThreadState *thr = cur_thread(); - uptr pc = 0; - char *p = (char*)user_alloc(thr, pc, 10); - EXPECT_NE(p, (char*)0); - char *p2 = (char*)user_alloc(thr, pc, 20); - EXPECT_NE(p2, (char*)0); - EXPECT_NE(p2, p); - EXPECT_EQ(10U, user_alloc_usable_size(p)); - EXPECT_EQ(20U, user_alloc_usable_size(p2)); - user_free(thr, pc, p); - user_free(thr, pc, p2); -} - -TEST(Mman, UserRealloc) { - ThreadState *thr = cur_thread(); - uptr pc = 0; - { - void *p = user_realloc(thr, pc, 0, 0); - // Realloc(NULL, N) is equivalent to malloc(N), thus must return - // non-NULL pointer. - EXPECT_NE(p, (void*)0); - user_free(thr, pc, p); - } - { - void *p = user_realloc(thr, pc, 0, 100); - EXPECT_NE(p, (void*)0); - memset(p, 0xde, 100); - user_free(thr, pc, p); - } - { - void *p = user_alloc(thr, pc, 100); - EXPECT_NE(p, (void*)0); - memset(p, 0xde, 100); - // Realloc(P, 0) is equivalent to free(P) and returns NULL. - void *p2 = user_realloc(thr, pc, p, 0); - EXPECT_EQ(p2, (void*)0); - } - { - void *p = user_realloc(thr, pc, 0, 100); - EXPECT_NE(p, (void*)0); - memset(p, 0xde, 100); - void *p2 = user_realloc(thr, pc, p, 10000); - EXPECT_NE(p2, (void*)0); - for (int i = 0; i < 100; i++) - EXPECT_EQ(((char*)p2)[i], (char)0xde); - memset(p2, 0xde, 10000); - user_free(thr, pc, p2); - } - { - void *p = user_realloc(thr, pc, 0, 10000); - EXPECT_NE(p, (void*)0); - memset(p, 0xde, 10000); - void *p2 = user_realloc(thr, pc, p, 10); - EXPECT_NE(p2, (void*)0); - for (int i = 0; i < 10; i++) - EXPECT_EQ(((char*)p2)[i], (char)0xde); - user_free(thr, pc, p2); - } -} - -TEST(Mman, UsableSize) { - ThreadState *thr = cur_thread(); - uptr pc = 0; - char *p = (char*)user_alloc(thr, pc, 10); - char *p2 = (char*)user_alloc(thr, pc, 20); - EXPECT_EQ(0U, user_alloc_usable_size(NULL)); - EXPECT_EQ(10U, user_alloc_usable_size(p)); - EXPECT_EQ(20U, user_alloc_usable_size(p2)); - user_free(thr, pc, p); - user_free(thr, pc, p2); - EXPECT_EQ(0U, user_alloc_usable_size((void*)0x4123)); -} - -TEST(Mman, Stats) { - ThreadState *thr = cur_thread(); - - uptr alloc0 = __sanitizer_get_current_allocated_bytes(); - uptr heap0 = __sanitizer_get_heap_size(); - uptr free0 = __sanitizer_get_free_bytes(); - uptr unmapped0 = __sanitizer_get_unmapped_bytes(); - - EXPECT_EQ(10U, __sanitizer_get_estimated_allocated_size(10)); - EXPECT_EQ(20U, __sanitizer_get_estimated_allocated_size(20)); - EXPECT_EQ(100U, __sanitizer_get_estimated_allocated_size(100)); - - char *p = (char*)user_alloc(thr, 0, 10); - EXPECT_TRUE(__sanitizer_get_ownership(p)); - EXPECT_EQ(10U, __sanitizer_get_allocated_size(p)); - - EXPECT_EQ(alloc0 + 16, __sanitizer_get_current_allocated_bytes()); - EXPECT_GE(__sanitizer_get_heap_size(), heap0); - EXPECT_EQ(free0, __sanitizer_get_free_bytes()); - EXPECT_EQ(unmapped0, __sanitizer_get_unmapped_bytes()); - - user_free(thr, 0, p); - - EXPECT_EQ(alloc0, __sanitizer_get_current_allocated_bytes()); - EXPECT_GE(__sanitizer_get_heap_size(), heap0); - EXPECT_EQ(free0, __sanitizer_get_free_bytes()); - EXPECT_EQ(unmapped0, __sanitizer_get_unmapped_bytes()); -} - -TEST(Mman, Valloc) { - ThreadState *thr = cur_thread(); - uptr page_size = GetPageSizeCached(); - - void *p = user_valloc(thr, 0, 100); - EXPECT_NE(p, (void*)0); - user_free(thr, 0, p); - - p = user_pvalloc(thr, 0, 100); - EXPECT_NE(p, (void*)0); - user_free(thr, 0, p); - - p = user_pvalloc(thr, 0, 0); - EXPECT_NE(p, (void*)0); - EXPECT_EQ(page_size, __sanitizer_get_allocated_size(p)); - user_free(thr, 0, p); -} - -#if !SANITIZER_DEBUG -// EXPECT_DEATH clones a thread with 4K stack, -// which is overflown by tsan memory accesses functions in debug mode. - -TEST(Mman, Memalign) { - ThreadState *thr = cur_thread(); - - void *p = user_memalign(thr, 0, 8, 100); - EXPECT_NE(p, (void*)0); - user_free(thr, 0, p); - - // TODO(alekseyshl): Remove this death test when memalign is verified by - // tests in sanitizer_common. - p = NULL; - EXPECT_DEATH(p = user_memalign(thr, 0, 7, 100), - "invalid-allocation-alignment"); - EXPECT_EQ(0L, p); -} - -#endif - -TEST(Mman, PosixMemalign) { - ThreadState *thr = cur_thread(); - - void *p = NULL; - int res = user_posix_memalign(thr, 0, &p, 8, 100); - EXPECT_NE(p, (void*)0); - EXPECT_EQ(res, 0); - user_free(thr, 0, p); -} - -TEST(Mman, AlignedAlloc) { - ThreadState *thr = cur_thread(); - - void *p = user_aligned_alloc(thr, 0, 8, 64); - EXPECT_NE(p, (void*)0); - user_free(thr, 0, p); -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cpp new file mode 100644 index 00000000000..a8a88b37abe --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cpp @@ -0,0 +1,196 @@ +//===-- tsan_mman_test.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include +#include +#include "tsan_mman.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Mman, Internal) { + char *p = (char*)internal_alloc(MBlockScopedBuf, 10); + EXPECT_NE(p, (char*)0); + char *p2 = (char*)internal_alloc(MBlockScopedBuf, 20); + EXPECT_NE(p2, (char*)0); + EXPECT_NE(p2, p); + for (int i = 0; i < 10; i++) { + p[i] = 42; + } + for (int i = 0; i < 20; i++) { + ((char*)p2)[i] = 42; + } + internal_free(p); + internal_free(p2); +} + +TEST(Mman, User) { + ThreadState *thr = cur_thread(); + uptr pc = 0; + char *p = (char*)user_alloc(thr, pc, 10); + EXPECT_NE(p, (char*)0); + char *p2 = (char*)user_alloc(thr, pc, 20); + EXPECT_NE(p2, (char*)0); + EXPECT_NE(p2, p); + EXPECT_EQ(10U, user_alloc_usable_size(p)); + EXPECT_EQ(20U, user_alloc_usable_size(p2)); + user_free(thr, pc, p); + user_free(thr, pc, p2); +} + +TEST(Mman, UserRealloc) { + ThreadState *thr = cur_thread(); + uptr pc = 0; + { + void *p = user_realloc(thr, pc, 0, 0); + // Realloc(NULL, N) is equivalent to malloc(N), thus must return + // non-NULL pointer. + EXPECT_NE(p, (void*)0); + user_free(thr, pc, p); + } + { + void *p = user_realloc(thr, pc, 0, 100); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 100); + user_free(thr, pc, p); + } + { + void *p = user_alloc(thr, pc, 100); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 100); + // Realloc(P, 0) is equivalent to free(P) and returns NULL. + void *p2 = user_realloc(thr, pc, p, 0); + EXPECT_EQ(p2, (void*)0); + } + { + void *p = user_realloc(thr, pc, 0, 100); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 100); + void *p2 = user_realloc(thr, pc, p, 10000); + EXPECT_NE(p2, (void*)0); + for (int i = 0; i < 100; i++) + EXPECT_EQ(((char*)p2)[i], (char)0xde); + memset(p2, 0xde, 10000); + user_free(thr, pc, p2); + } + { + void *p = user_realloc(thr, pc, 0, 10000); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 10000); + void *p2 = user_realloc(thr, pc, p, 10); + EXPECT_NE(p2, (void*)0); + for (int i = 0; i < 10; i++) + EXPECT_EQ(((char*)p2)[i], (char)0xde); + user_free(thr, pc, p2); + } +} + +TEST(Mman, UsableSize) { + ThreadState *thr = cur_thread(); + uptr pc = 0; + char *p = (char*)user_alloc(thr, pc, 10); + char *p2 = (char*)user_alloc(thr, pc, 20); + EXPECT_EQ(0U, user_alloc_usable_size(NULL)); + EXPECT_EQ(10U, user_alloc_usable_size(p)); + EXPECT_EQ(20U, user_alloc_usable_size(p2)); + user_free(thr, pc, p); + user_free(thr, pc, p2); + EXPECT_EQ(0U, user_alloc_usable_size((void*)0x4123)); +} + +TEST(Mman, Stats) { + ThreadState *thr = cur_thread(); + + uptr alloc0 = __sanitizer_get_current_allocated_bytes(); + uptr heap0 = __sanitizer_get_heap_size(); + uptr free0 = __sanitizer_get_free_bytes(); + uptr unmapped0 = __sanitizer_get_unmapped_bytes(); + + EXPECT_EQ(10U, __sanitizer_get_estimated_allocated_size(10)); + EXPECT_EQ(20U, __sanitizer_get_estimated_allocated_size(20)); + EXPECT_EQ(100U, __sanitizer_get_estimated_allocated_size(100)); + + char *p = (char*)user_alloc(thr, 0, 10); + EXPECT_TRUE(__sanitizer_get_ownership(p)); + EXPECT_EQ(10U, __sanitizer_get_allocated_size(p)); + + EXPECT_EQ(alloc0 + 16, __sanitizer_get_current_allocated_bytes()); + EXPECT_GE(__sanitizer_get_heap_size(), heap0); + EXPECT_EQ(free0, __sanitizer_get_free_bytes()); + EXPECT_EQ(unmapped0, __sanitizer_get_unmapped_bytes()); + + user_free(thr, 0, p); + + EXPECT_EQ(alloc0, __sanitizer_get_current_allocated_bytes()); + EXPECT_GE(__sanitizer_get_heap_size(), heap0); + EXPECT_EQ(free0, __sanitizer_get_free_bytes()); + EXPECT_EQ(unmapped0, __sanitizer_get_unmapped_bytes()); +} + +TEST(Mman, Valloc) { + ThreadState *thr = cur_thread(); + uptr page_size = GetPageSizeCached(); + + void *p = user_valloc(thr, 0, 100); + EXPECT_NE(p, (void*)0); + user_free(thr, 0, p); + + p = user_pvalloc(thr, 0, 100); + EXPECT_NE(p, (void*)0); + user_free(thr, 0, p); + + p = user_pvalloc(thr, 0, 0); + EXPECT_NE(p, (void*)0); + EXPECT_EQ(page_size, __sanitizer_get_allocated_size(p)); + user_free(thr, 0, p); +} + +#if !SANITIZER_DEBUG +// EXPECT_DEATH clones a thread with 4K stack, +// which is overflown by tsan memory accesses functions in debug mode. + +TEST(Mman, Memalign) { + ThreadState *thr = cur_thread(); + + void *p = user_memalign(thr, 0, 8, 100); + EXPECT_NE(p, (void*)0); + user_free(thr, 0, p); + + // TODO(alekseyshl): Remove this death test when memalign is verified by + // tests in sanitizer_common. + p = NULL; + EXPECT_DEATH(p = user_memalign(thr, 0, 7, 100), + "invalid-allocation-alignment"); + EXPECT_EQ(0L, p); +} + +#endif + +TEST(Mman, PosixMemalign) { + ThreadState *thr = cur_thread(); + + void *p = NULL; + int res = user_posix_memalign(thr, 0, &p, 8, 100); + EXPECT_NE(p, (void*)0); + EXPECT_EQ(res, 0); + user_free(thr, 0, p); +} + +TEST(Mman, AlignedAlloc) { + ThreadState *thr = cur_thread(); + + void *p = user_aligned_alloc(thr, 0, 8, 64); + EXPECT_NE(p, (void*)0); + user_free(thr, 0, p); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc deleted file mode 100644 index 0649c8aa589..00000000000 --- a/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc +++ /dev/null @@ -1,125 +0,0 @@ -//===-- tsan_mutex_test.cc ------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_internal_defs.h" -#include "sanitizer_common/sanitizer_atomic.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_mutex.h" -#include "tsan_mutex.h" -#include "gtest/gtest.h" - -namespace __tsan { - -template -class TestData { - public: - explicit TestData(MutexType *mtx) - : mtx_(mtx) { - for (int i = 0; i < kSize; i++) - data_[i] = 0; - } - - void Write() { - Lock l(mtx_); - T v0 = data_[0]; - for (int i = 0; i < kSize; i++) { - CHECK_EQ(data_[i], v0); - data_[i]++; - } - } - - void Read() { - ReadLock l(mtx_); - T v0 = data_[0]; - for (int i = 0; i < kSize; i++) { - CHECK_EQ(data_[i], v0); - } - } - - void Backoff() { - volatile T data[kSize] = {}; - for (int i = 0; i < kSize; i++) { - data[i]++; - CHECK_EQ(data[i], 1); - } - } - - private: - typedef GenericScopedLock Lock; - static const int kSize = 64; - typedef u64 T; - MutexType *mtx_; - char pad_[kCacheLineSize]; - T data_[kSize]; -}; - -const int kThreads = 8; -const int kWriteRate = 1024; -#if SANITIZER_DEBUG -const int kIters = 16*1024; -#else -const int kIters = 64*1024; -#endif - -template -static void *write_mutex_thread(void *param) { - TestData *data = (TestData*)param; - for (int i = 0; i < kIters; i++) { - data->Write(); - data->Backoff(); - } - return 0; -} - -template -static void *read_mutex_thread(void *param) { - TestData *data = (TestData*)param; - for (int i = 0; i < kIters; i++) { - if ((i % kWriteRate) == 0) - data->Write(); - else - data->Read(); - data->Backoff(); - } - return 0; -} - -TEST(Mutex, Write) { - Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations); - TestData data(&mtx); - pthread_t threads[kThreads]; - for (int i = 0; i < kThreads; i++) - pthread_create(&threads[i], 0, write_mutex_thread, &data); - for (int i = 0; i < kThreads; i++) - pthread_join(threads[i], 0); -} - -TEST(Mutex, ReadWrite) { - Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations); - TestData data(&mtx); - pthread_t threads[kThreads]; - for (int i = 0; i < kThreads; i++) - pthread_create(&threads[i], 0, read_mutex_thread, &data); - for (int i = 0; i < kThreads; i++) - pthread_join(threads[i], 0); -} - -TEST(Mutex, SpinWrite) { - SpinMutex mtx; - TestData data(&mtx); - pthread_t threads[kThreads]; - for (int i = 0; i < kThreads; i++) - pthread_create(&threads[i], 0, write_mutex_thread, &data); - for (int i = 0; i < kThreads; i++) - pthread_join(threads[i], 0); -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cpp new file mode 100644 index 00000000000..cb29a7cc0e6 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cpp @@ -0,0 +1,125 @@ +//===-- tsan_mutex_test.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_mutex.h" +#include "tsan_mutex.h" +#include "gtest/gtest.h" + +namespace __tsan { + +template +class TestData { + public: + explicit TestData(MutexType *mtx) + : mtx_(mtx) { + for (int i = 0; i < kSize; i++) + data_[i] = 0; + } + + void Write() { + Lock l(mtx_); + T v0 = data_[0]; + for (int i = 0; i < kSize; i++) { + CHECK_EQ(data_[i], v0); + data_[i]++; + } + } + + void Read() { + ReadLock l(mtx_); + T v0 = data_[0]; + for (int i = 0; i < kSize; i++) { + CHECK_EQ(data_[i], v0); + } + } + + void Backoff() { + volatile T data[kSize] = {}; + for (int i = 0; i < kSize; i++) { + data[i]++; + CHECK_EQ(data[i], 1); + } + } + + private: + typedef GenericScopedLock Lock; + static const int kSize = 64; + typedef u64 T; + MutexType *mtx_; + char pad_[kCacheLineSize]; + T data_[kSize]; +}; + +const int kThreads = 8; +const int kWriteRate = 1024; +#if SANITIZER_DEBUG +const int kIters = 16*1024; +#else +const int kIters = 64*1024; +#endif + +template +static void *write_mutex_thread(void *param) { + TestData *data = (TestData*)param; + for (int i = 0; i < kIters; i++) { + data->Write(); + data->Backoff(); + } + return 0; +} + +template +static void *read_mutex_thread(void *param) { + TestData *data = (TestData*)param; + for (int i = 0; i < kIters; i++) { + if ((i % kWriteRate) == 0) + data->Write(); + else + data->Read(); + data->Backoff(); + } + return 0; +} + +TEST(Mutex, Write) { + Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations); + TestData data(&mtx); + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, write_mutex_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); +} + +TEST(Mutex, ReadWrite) { + Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations); + TestData data(&mtx); + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, read_mutex_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); +} + +TEST(Mutex, SpinWrite) { + SpinMutex mtx; + TestData data(&mtx); + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, write_mutex_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc deleted file mode 100644 index 5862138a82f..00000000000 --- a/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc +++ /dev/null @@ -1,126 +0,0 @@ -//===-- tsan_mutexset_test.cc ---------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_mutexset.h" -#include "gtest/gtest.h" - -namespace __tsan { - -static void Expect(const MutexSet &mset, uptr i, u64 id, bool write, u64 epoch, - int count) { - MutexSet::Desc d = mset.Get(i); - EXPECT_EQ(id, d.id); - EXPECT_EQ(write, d.write); - EXPECT_EQ(epoch, d.epoch); - EXPECT_EQ(count, d.count); -} - -TEST(MutexSet, Basic) { - MutexSet mset; - EXPECT_EQ(mset.Size(), (uptr)0); - - mset.Add(1, true, 2); - EXPECT_EQ(mset.Size(), (uptr)1); - Expect(mset, 0, 1, true, 2, 1); - mset.Del(1, true); - EXPECT_EQ(mset.Size(), (uptr)0); - - mset.Add(3, true, 4); - mset.Add(5, false, 6); - EXPECT_EQ(mset.Size(), (uptr)2); - Expect(mset, 0, 3, true, 4, 1); - Expect(mset, 1, 5, false, 6, 1); - mset.Del(3, true); - EXPECT_EQ(mset.Size(), (uptr)1); - mset.Del(5, false); - EXPECT_EQ(mset.Size(), (uptr)0); -} - -TEST(MutexSet, DoubleAdd) { - MutexSet mset; - mset.Add(1, true, 2); - EXPECT_EQ(mset.Size(), (uptr)1); - Expect(mset, 0, 1, true, 2, 1); - - mset.Add(1, true, 2); - EXPECT_EQ(mset.Size(), (uptr)1); - Expect(mset, 0, 1, true, 2, 2); - - mset.Del(1, true); - EXPECT_EQ(mset.Size(), (uptr)1); - Expect(mset, 0, 1, true, 2, 1); - - mset.Del(1, true); - EXPECT_EQ(mset.Size(), (uptr)0); -} - -TEST(MutexSet, DoubleDel) { - MutexSet mset; - mset.Add(1, true, 2); - EXPECT_EQ(mset.Size(), (uptr)1); - mset.Del(1, true); - EXPECT_EQ(mset.Size(), (uptr)0); - mset.Del(1, true); - EXPECT_EQ(mset.Size(), (uptr)0); -} - -TEST(MutexSet, Remove) { - MutexSet mset; - mset.Add(1, true, 2); - mset.Add(1, true, 2); - mset.Add(3, true, 4); - mset.Add(3, true, 4); - EXPECT_EQ(mset.Size(), (uptr)2); - - mset.Remove(1); - EXPECT_EQ(mset.Size(), (uptr)1); - Expect(mset, 0, 3, true, 4, 2); -} - -TEST(MutexSet, Full) { - MutexSet mset; - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - mset.Add(i, true, i + 1); - } - EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - Expect(mset, i, i, true, i + 1, 1); - } - - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - mset.Add(i, true, i + 1); - } - EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - Expect(mset, i, i, true, i + 1, 2); - } -} - -TEST(MutexSet, Overflow) { - MutexSet mset; - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - mset.Add(i, true, i + 1); - mset.Add(i, true, i + 1); - } - mset.Add(100, true, 200); - EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - if (i == 0) - Expect(mset, i, MutexSet::kMaxSize - 1, - true, MutexSet::kMaxSize, 2); - else if (i == MutexSet::kMaxSize - 1) - Expect(mset, i, 100, true, 200, 1); - else - Expect(mset, i, i, true, i + 1, 2); - } -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cpp new file mode 100644 index 00000000000..2b531b021fc --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cpp @@ -0,0 +1,126 @@ +//===-- tsan_mutexset_test.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_mutexset.h" +#include "gtest/gtest.h" + +namespace __tsan { + +static void Expect(const MutexSet &mset, uptr i, u64 id, bool write, u64 epoch, + int count) { + MutexSet::Desc d = mset.Get(i); + EXPECT_EQ(id, d.id); + EXPECT_EQ(write, d.write); + EXPECT_EQ(epoch, d.epoch); + EXPECT_EQ(count, d.count); +} + +TEST(MutexSet, Basic) { + MutexSet mset; + EXPECT_EQ(mset.Size(), (uptr)0); + + mset.Add(1, true, 2); + EXPECT_EQ(mset.Size(), (uptr)1); + Expect(mset, 0, 1, true, 2, 1); + mset.Del(1, true); + EXPECT_EQ(mset.Size(), (uptr)0); + + mset.Add(3, true, 4); + mset.Add(5, false, 6); + EXPECT_EQ(mset.Size(), (uptr)2); + Expect(mset, 0, 3, true, 4, 1); + Expect(mset, 1, 5, false, 6, 1); + mset.Del(3, true); + EXPECT_EQ(mset.Size(), (uptr)1); + mset.Del(5, false); + EXPECT_EQ(mset.Size(), (uptr)0); +} + +TEST(MutexSet, DoubleAdd) { + MutexSet mset; + mset.Add(1, true, 2); + EXPECT_EQ(mset.Size(), (uptr)1); + Expect(mset, 0, 1, true, 2, 1); + + mset.Add(1, true, 2); + EXPECT_EQ(mset.Size(), (uptr)1); + Expect(mset, 0, 1, true, 2, 2); + + mset.Del(1, true); + EXPECT_EQ(mset.Size(), (uptr)1); + Expect(mset, 0, 1, true, 2, 1); + + mset.Del(1, true); + EXPECT_EQ(mset.Size(), (uptr)0); +} + +TEST(MutexSet, DoubleDel) { + MutexSet mset; + mset.Add(1, true, 2); + EXPECT_EQ(mset.Size(), (uptr)1); + mset.Del(1, true); + EXPECT_EQ(mset.Size(), (uptr)0); + mset.Del(1, true); + EXPECT_EQ(mset.Size(), (uptr)0); +} + +TEST(MutexSet, Remove) { + MutexSet mset; + mset.Add(1, true, 2); + mset.Add(1, true, 2); + mset.Add(3, true, 4); + mset.Add(3, true, 4); + EXPECT_EQ(mset.Size(), (uptr)2); + + mset.Remove(1); + EXPECT_EQ(mset.Size(), (uptr)1); + Expect(mset, 0, 3, true, 4, 2); +} + +TEST(MutexSet, Full) { + MutexSet mset; + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + mset.Add(i, true, i + 1); + } + EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + Expect(mset, i, i, true, i + 1, 1); + } + + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + mset.Add(i, true, i + 1); + } + EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + Expect(mset, i, i, true, i + 1, 2); + } +} + +TEST(MutexSet, Overflow) { + MutexSet mset; + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + mset.Add(i, true, i + 1); + mset.Add(i, true, i + 1); + } + mset.Add(100, true, 200); + EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + if (i == 0) + Expect(mset, i, MutexSet::kMaxSize - 1, + true, MutexSet::kMaxSize, 2); + else if (i == MutexSet::kMaxSize - 1) + Expect(mset, i, 100, true, 200, 1); + else + Expect(mset, i, i, true, i + 1, 2); + } +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cc deleted file mode 100644 index 21a4ddf973b..00000000000 --- a/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cc +++ /dev/null @@ -1,77 +0,0 @@ -//===-- tsan_shadow_test.cc -----------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_platform.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" - -namespace __tsan { - -TEST(Shadow, FastState) { - Shadow s(FastState(11, 22)); - EXPECT_EQ(s.tid(), (u64)11); - EXPECT_EQ(s.epoch(), (u64)22); - EXPECT_EQ(s.GetIgnoreBit(), false); - EXPECT_EQ(s.GetFreedAndReset(), false); - EXPECT_EQ(s.GetHistorySize(), 0); - EXPECT_EQ(s.addr0(), (u64)0); - EXPECT_EQ(s.size(), (u64)1); - EXPECT_EQ(s.IsWrite(), true); - - s.IncrementEpoch(); - EXPECT_EQ(s.epoch(), (u64)23); - s.IncrementEpoch(); - EXPECT_EQ(s.epoch(), (u64)24); - - s.SetIgnoreBit(); - EXPECT_EQ(s.GetIgnoreBit(), true); - s.ClearIgnoreBit(); - EXPECT_EQ(s.GetIgnoreBit(), false); - - for (int i = 0; i < 8; i++) { - s.SetHistorySize(i); - EXPECT_EQ(s.GetHistorySize(), i); - } - s.SetHistorySize(2); - s.ClearHistorySize(); - EXPECT_EQ(s.GetHistorySize(), 0); -} - -TEST(Shadow, Mapping) { - static int global; - int stack; - void *heap = malloc(0); - free(heap); - - CHECK(IsAppMem((uptr)&global)); - CHECK(IsAppMem((uptr)&stack)); - CHECK(IsAppMem((uptr)heap)); - - CHECK(IsShadowMem(MemToShadow((uptr)&global))); - CHECK(IsShadowMem(MemToShadow((uptr)&stack))); - CHECK(IsShadowMem(MemToShadow((uptr)heap))); -} - -TEST(Shadow, Celling) { - u64 aligned_data[4]; - char *data = (char*)aligned_data; - CHECK_EQ((uptr)data % kShadowSize, 0); - uptr s0 = MemToShadow((uptr)&data[0]); - CHECK_EQ(s0 % kShadowSize, 0); - for (unsigned i = 1; i < kShadowCell; i++) - CHECK_EQ(s0, MemToShadow((uptr)&data[i])); - for (unsigned i = kShadowCell; i < 2*kShadowCell; i++) - CHECK_EQ(s0 + kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i])); - for (unsigned i = 2*kShadowCell; i < 3*kShadowCell; i++) - CHECK_EQ(s0 + 2*kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i])); -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cpp new file mode 100644 index 00000000000..1d2023f6ad2 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cpp @@ -0,0 +1,77 @@ +//===-- tsan_shadow_test.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_platform.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Shadow, FastState) { + Shadow s(FastState(11, 22)); + EXPECT_EQ(s.tid(), (u64)11); + EXPECT_EQ(s.epoch(), (u64)22); + EXPECT_EQ(s.GetIgnoreBit(), false); + EXPECT_EQ(s.GetFreedAndReset(), false); + EXPECT_EQ(s.GetHistorySize(), 0); + EXPECT_EQ(s.addr0(), (u64)0); + EXPECT_EQ(s.size(), (u64)1); + EXPECT_EQ(s.IsWrite(), true); + + s.IncrementEpoch(); + EXPECT_EQ(s.epoch(), (u64)23); + s.IncrementEpoch(); + EXPECT_EQ(s.epoch(), (u64)24); + + s.SetIgnoreBit(); + EXPECT_EQ(s.GetIgnoreBit(), true); + s.ClearIgnoreBit(); + EXPECT_EQ(s.GetIgnoreBit(), false); + + for (int i = 0; i < 8; i++) { + s.SetHistorySize(i); + EXPECT_EQ(s.GetHistorySize(), i); + } + s.SetHistorySize(2); + s.ClearHistorySize(); + EXPECT_EQ(s.GetHistorySize(), 0); +} + +TEST(Shadow, Mapping) { + static int global; + int stack; + void *heap = malloc(0); + free(heap); + + CHECK(IsAppMem((uptr)&global)); + CHECK(IsAppMem((uptr)&stack)); + CHECK(IsAppMem((uptr)heap)); + + CHECK(IsShadowMem(MemToShadow((uptr)&global))); + CHECK(IsShadowMem(MemToShadow((uptr)&stack))); + CHECK(IsShadowMem(MemToShadow((uptr)heap))); +} + +TEST(Shadow, Celling) { + u64 aligned_data[4]; + char *data = (char*)aligned_data; + CHECK_EQ((uptr)data % kShadowSize, 0); + uptr s0 = MemToShadow((uptr)&data[0]); + CHECK_EQ(s0 % kShadowSize, 0); + for (unsigned i = 1; i < kShadowCell; i++) + CHECK_EQ(s0, MemToShadow((uptr)&data[i])); + for (unsigned i = kShadowCell; i < 2*kShadowCell; i++) + CHECK_EQ(s0 + kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i])); + for (unsigned i = 2*kShadowCell; i < 3*kShadowCell; i++) + CHECK_EQ(s0 + 2*kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i])); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc deleted file mode 100644 index d8b81d33123..00000000000 --- a/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc +++ /dev/null @@ -1,94 +0,0 @@ -//===-- tsan_stack_test.cc ------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_sync.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" -#include - -namespace __tsan { - -template -static void TestStackTrace(StackTraceTy *trace) { - ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0, 0); - uptr stack[128]; - thr.shadow_stack = &stack[0]; - thr.shadow_stack_pos = &stack[0]; - thr.shadow_stack_end = &stack[128]; - - ObtainCurrentStack(&thr, 0, trace); - EXPECT_EQ(0U, trace->size); - - ObtainCurrentStack(&thr, 42, trace); - EXPECT_EQ(1U, trace->size); - EXPECT_EQ(42U, trace->trace[0]); - - *thr.shadow_stack_pos++ = 100; - *thr.shadow_stack_pos++ = 101; - ObtainCurrentStack(&thr, 0, trace); - EXPECT_EQ(2U, trace->size); - EXPECT_EQ(100U, trace->trace[0]); - EXPECT_EQ(101U, trace->trace[1]); - - ObtainCurrentStack(&thr, 42, trace); - EXPECT_EQ(3U, trace->size); - EXPECT_EQ(100U, trace->trace[0]); - EXPECT_EQ(101U, trace->trace[1]); - EXPECT_EQ(42U, trace->trace[2]); -} - -template -static void TestTrim(StackTraceTy *trace) { - ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0, 0); - const uptr kShadowStackSize = 2 * kStackTraceMax; - uptr stack[kShadowStackSize]; - thr.shadow_stack = &stack[0]; - thr.shadow_stack_pos = &stack[0]; - thr.shadow_stack_end = &stack[kShadowStackSize]; - - for (uptr i = 0; i < kShadowStackSize; ++i) - *thr.shadow_stack_pos++ = 100 + i; - - ObtainCurrentStack(&thr, 0, trace); - EXPECT_EQ(kStackTraceMax, trace->size); - for (uptr i = 0; i < kStackTraceMax; i++) { - EXPECT_EQ(100 + kStackTraceMax + i, trace->trace[i]); - } - - ObtainCurrentStack(&thr, 42, trace); - EXPECT_EQ(kStackTraceMax, trace->size); - for (uptr i = 0; i < kStackTraceMax - 1; i++) { - EXPECT_EQ(101 + kStackTraceMax + i, trace->trace[i]); - } - EXPECT_EQ(42U, trace->trace[kStackTraceMax - 1]); -} - -TEST(StackTrace, BasicVarSize) { - VarSizeStackTrace trace; - TestStackTrace(&trace); -} - -TEST(StackTrace, BasicBuffered) { - BufferedStackTrace trace; - TestStackTrace(&trace); -} - -TEST(StackTrace, TrimVarSize) { - VarSizeStackTrace trace; - TestTrim(&trace); -} - -TEST(StackTrace, TrimBuffered) { - BufferedStackTrace trace; - TestTrim(&trace); -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cpp new file mode 100644 index 00000000000..f2ee1265c31 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cpp @@ -0,0 +1,94 @@ +//===-- tsan_stack_test.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_sync.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" +#include + +namespace __tsan { + +template +static void TestStackTrace(StackTraceTy *trace) { + ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0, 0); + uptr stack[128]; + thr.shadow_stack = &stack[0]; + thr.shadow_stack_pos = &stack[0]; + thr.shadow_stack_end = &stack[128]; + + ObtainCurrentStack(&thr, 0, trace); + EXPECT_EQ(0U, trace->size); + + ObtainCurrentStack(&thr, 42, trace); + EXPECT_EQ(1U, trace->size); + EXPECT_EQ(42U, trace->trace[0]); + + *thr.shadow_stack_pos++ = 100; + *thr.shadow_stack_pos++ = 101; + ObtainCurrentStack(&thr, 0, trace); + EXPECT_EQ(2U, trace->size); + EXPECT_EQ(100U, trace->trace[0]); + EXPECT_EQ(101U, trace->trace[1]); + + ObtainCurrentStack(&thr, 42, trace); + EXPECT_EQ(3U, trace->size); + EXPECT_EQ(100U, trace->trace[0]); + EXPECT_EQ(101U, trace->trace[1]); + EXPECT_EQ(42U, trace->trace[2]); +} + +template +static void TestTrim(StackTraceTy *trace) { + ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0, 0); + const uptr kShadowStackSize = 2 * kStackTraceMax; + uptr stack[kShadowStackSize]; + thr.shadow_stack = &stack[0]; + thr.shadow_stack_pos = &stack[0]; + thr.shadow_stack_end = &stack[kShadowStackSize]; + + for (uptr i = 0; i < kShadowStackSize; ++i) + *thr.shadow_stack_pos++ = 100 + i; + + ObtainCurrentStack(&thr, 0, trace); + EXPECT_EQ(kStackTraceMax, trace->size); + for (uptr i = 0; i < kStackTraceMax; i++) { + EXPECT_EQ(100 + kStackTraceMax + i, trace->trace[i]); + } + + ObtainCurrentStack(&thr, 42, trace); + EXPECT_EQ(kStackTraceMax, trace->size); + for (uptr i = 0; i < kStackTraceMax - 1; i++) { + EXPECT_EQ(101 + kStackTraceMax + i, trace->trace[i]); + } + EXPECT_EQ(42U, trace->trace[kStackTraceMax - 1]); +} + +TEST(StackTrace, BasicVarSize) { + VarSizeStackTrace trace; + TestStackTrace(&trace); +} + +TEST(StackTrace, BasicBuffered) { + BufferedStackTrace trace; + TestStackTrace(&trace); +} + +TEST(StackTrace, TrimVarSize) { + VarSizeStackTrace trace; + TestTrim(&trace); +} + +TEST(StackTrace, TrimBuffered) { + BufferedStackTrace trace; + TestTrim(&trace); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc deleted file mode 100644 index 7ea2826e8a5..00000000000 --- a/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc +++ /dev/null @@ -1,122 +0,0 @@ -//===-- tsan_sync_test.cc -------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_sync.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" - -namespace __tsan { - -TEST(MetaMap, Basic) { - ThreadState *thr = cur_thread(); - MetaMap *m = &ctx->metamap; - u64 block[1] = {}; // fake malloc block - m->AllocBlock(thr, 0, (uptr)&block[0], 1 * sizeof(u64)); - MBlock *mb = m->GetBlock((uptr)&block[0]); - EXPECT_NE(mb, (MBlock*)0); - EXPECT_EQ(mb->siz, 1 * sizeof(u64)); - EXPECT_EQ(mb->tid, thr->tid); - uptr sz = m->FreeBlock(thr->proc(), (uptr)&block[0]); - EXPECT_EQ(sz, 1 * sizeof(u64)); - mb = m->GetBlock((uptr)&block[0]); - EXPECT_EQ(mb, (MBlock*)0); -} - -TEST(MetaMap, FreeRange) { - ThreadState *thr = cur_thread(); - MetaMap *m = &ctx->metamap; - u64 block[4] = {}; // fake malloc block - m->AllocBlock(thr, 0, (uptr)&block[0], 1 * sizeof(u64)); - m->AllocBlock(thr, 0, (uptr)&block[1], 3 * sizeof(u64)); - MBlock *mb1 = m->GetBlock((uptr)&block[0]); - EXPECT_EQ(mb1->siz, 1 * sizeof(u64)); - MBlock *mb2 = m->GetBlock((uptr)&block[1]); - EXPECT_EQ(mb2->siz, 3 * sizeof(u64)); - m->FreeRange(thr->proc(), (uptr)&block[0], 4 * sizeof(u64)); - mb1 = m->GetBlock((uptr)&block[0]); - EXPECT_EQ(mb1, (MBlock*)0); - mb2 = m->GetBlock((uptr)&block[1]); - EXPECT_EQ(mb2, (MBlock*)0); -} - -TEST(MetaMap, Sync) { - ThreadState *thr = cur_thread(); - MetaMap *m = &ctx->metamap; - u64 block[4] = {}; // fake malloc block - m->AllocBlock(thr, 0, (uptr)&block[0], 4 * sizeof(u64)); - SyncVar *s1 = m->GetIfExistsAndLock((uptr)&block[0], true); - EXPECT_EQ(s1, (SyncVar*)0); - s1 = m->GetOrCreateAndLock(thr, 0, (uptr)&block[0], true); - EXPECT_NE(s1, (SyncVar*)0); - EXPECT_EQ(s1->addr, (uptr)&block[0]); - s1->mtx.Unlock(); - SyncVar *s2 = m->GetOrCreateAndLock(thr, 0, (uptr)&block[1], false); - EXPECT_NE(s2, (SyncVar*)0); - EXPECT_EQ(s2->addr, (uptr)&block[1]); - s2->mtx.ReadUnlock(); - m->FreeBlock(thr->proc(), (uptr)&block[0]); - s1 = m->GetIfExistsAndLock((uptr)&block[0], true); - EXPECT_EQ(s1, (SyncVar*)0); - s2 = m->GetIfExistsAndLock((uptr)&block[1], true); - EXPECT_EQ(s2, (SyncVar*)0); - m->OnProcIdle(thr->proc()); -} - -TEST(MetaMap, MoveMemory) { - ThreadState *thr = cur_thread(); - MetaMap *m = &ctx->metamap; - u64 block1[4] = {}; // fake malloc block - u64 block2[4] = {}; // fake malloc block - m->AllocBlock(thr, 0, (uptr)&block1[0], 3 * sizeof(u64)); - m->AllocBlock(thr, 0, (uptr)&block1[3], 1 * sizeof(u64)); - SyncVar *s1 = m->GetOrCreateAndLock(thr, 0, (uptr)&block1[0], true); - s1->mtx.Unlock(); - SyncVar *s2 = m->GetOrCreateAndLock(thr, 0, (uptr)&block1[1], true); - s2->mtx.Unlock(); - m->MoveMemory((uptr)&block1[0], (uptr)&block2[0], 4 * sizeof(u64)); - MBlock *mb1 = m->GetBlock((uptr)&block1[0]); - EXPECT_EQ(mb1, (MBlock*)0); - MBlock *mb2 = m->GetBlock((uptr)&block1[3]); - EXPECT_EQ(mb2, (MBlock*)0); - mb1 = m->GetBlock((uptr)&block2[0]); - EXPECT_NE(mb1, (MBlock*)0); - EXPECT_EQ(mb1->siz, 3 * sizeof(u64)); - mb2 = m->GetBlock((uptr)&block2[3]); - EXPECT_NE(mb2, (MBlock*)0); - EXPECT_EQ(mb2->siz, 1 * sizeof(u64)); - s1 = m->GetIfExistsAndLock((uptr)&block1[0], true); - EXPECT_EQ(s1, (SyncVar*)0); - s2 = m->GetIfExistsAndLock((uptr)&block1[1], true); - EXPECT_EQ(s2, (SyncVar*)0); - s1 = m->GetIfExistsAndLock((uptr)&block2[0], true); - EXPECT_NE(s1, (SyncVar*)0); - EXPECT_EQ(s1->addr, (uptr)&block2[0]); - s1->mtx.Unlock(); - s2 = m->GetIfExistsAndLock((uptr)&block2[1], true); - EXPECT_NE(s2, (SyncVar*)0); - EXPECT_EQ(s2->addr, (uptr)&block2[1]); - s2->mtx.Unlock(); - m->FreeRange(thr->proc(), (uptr)&block2[0], 4 * sizeof(u64)); -} - -TEST(MetaMap, ResetSync) { - ThreadState *thr = cur_thread(); - MetaMap *m = &ctx->metamap; - u64 block[1] = {}; // fake malloc block - m->AllocBlock(thr, 0, (uptr)&block[0], 1 * sizeof(u64)); - SyncVar *s = m->GetOrCreateAndLock(thr, 0, (uptr)&block[0], true); - s->Reset(thr->proc()); - s->mtx.Unlock(); - uptr sz = m->FreeBlock(thr->proc(), (uptr)&block[0]); - EXPECT_EQ(sz, 1 * sizeof(u64)); -} - -} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cpp new file mode 100644 index 00000000000..825cc0943e8 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cpp @@ -0,0 +1,122 @@ +//===-- tsan_sync_test.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_sync.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(MetaMap, Basic) { + ThreadState *thr = cur_thread(); + MetaMap *m = &ctx->metamap; + u64 block[1] = {}; // fake malloc block + m->AllocBlock(thr, 0, (uptr)&block[0], 1 * sizeof(u64)); + MBlock *mb = m->GetBlock((uptr)&block[0]); + EXPECT_NE(mb, (MBlock*)0); + EXPECT_EQ(mb->siz, 1 * sizeof(u64)); + EXPECT_EQ(mb->tid, thr->tid); + uptr sz = m->FreeBlock(thr->proc(), (uptr)&block[0]); + EXPECT_EQ(sz, 1 * sizeof(u64)); + mb = m->GetBlock((uptr)&block[0]); + EXPECT_EQ(mb, (MBlock*)0); +} + +TEST(MetaMap, FreeRange) { + ThreadState *thr = cur_thread(); + MetaMap *m = &ctx->metamap; + u64 block[4] = {}; // fake malloc block + m->AllocBlock(thr, 0, (uptr)&block[0], 1 * sizeof(u64)); + m->AllocBlock(thr, 0, (uptr)&block[1], 3 * sizeof(u64)); + MBlock *mb1 = m->GetBlock((uptr)&block[0]); + EXPECT_EQ(mb1->siz, 1 * sizeof(u64)); + MBlock *mb2 = m->GetBlock((uptr)&block[1]); + EXPECT_EQ(mb2->siz, 3 * sizeof(u64)); + m->FreeRange(thr->proc(), (uptr)&block[0], 4 * sizeof(u64)); + mb1 = m->GetBlock((uptr)&block[0]); + EXPECT_EQ(mb1, (MBlock*)0); + mb2 = m->GetBlock((uptr)&block[1]); + EXPECT_EQ(mb2, (MBlock*)0); +} + +TEST(MetaMap, Sync) { + ThreadState *thr = cur_thread(); + MetaMap *m = &ctx->metamap; + u64 block[4] = {}; // fake malloc block + m->AllocBlock(thr, 0, (uptr)&block[0], 4 * sizeof(u64)); + SyncVar *s1 = m->GetIfExistsAndLock((uptr)&block[0], true); + EXPECT_EQ(s1, (SyncVar*)0); + s1 = m->GetOrCreateAndLock(thr, 0, (uptr)&block[0], true); + EXPECT_NE(s1, (SyncVar*)0); + EXPECT_EQ(s1->addr, (uptr)&block[0]); + s1->mtx.Unlock(); + SyncVar *s2 = m->GetOrCreateAndLock(thr, 0, (uptr)&block[1], false); + EXPECT_NE(s2, (SyncVar*)0); + EXPECT_EQ(s2->addr, (uptr)&block[1]); + s2->mtx.ReadUnlock(); + m->FreeBlock(thr->proc(), (uptr)&block[0]); + s1 = m->GetIfExistsAndLock((uptr)&block[0], true); + EXPECT_EQ(s1, (SyncVar*)0); + s2 = m->GetIfExistsAndLock((uptr)&block[1], true); + EXPECT_EQ(s2, (SyncVar*)0); + m->OnProcIdle(thr->proc()); +} + +TEST(MetaMap, MoveMemory) { + ThreadState *thr = cur_thread(); + MetaMap *m = &ctx->metamap; + u64 block1[4] = {}; // fake malloc block + u64 block2[4] = {}; // fake malloc block + m->AllocBlock(thr, 0, (uptr)&block1[0], 3 * sizeof(u64)); + m->AllocBlock(thr, 0, (uptr)&block1[3], 1 * sizeof(u64)); + SyncVar *s1 = m->GetOrCreateAndLock(thr, 0, (uptr)&block1[0], true); + s1->mtx.Unlock(); + SyncVar *s2 = m->GetOrCreateAndLock(thr, 0, (uptr)&block1[1], true); + s2->mtx.Unlock(); + m->MoveMemory((uptr)&block1[0], (uptr)&block2[0], 4 * sizeof(u64)); + MBlock *mb1 = m->GetBlock((uptr)&block1[0]); + EXPECT_EQ(mb1, (MBlock*)0); + MBlock *mb2 = m->GetBlock((uptr)&block1[3]); + EXPECT_EQ(mb2, (MBlock*)0); + mb1 = m->GetBlock((uptr)&block2[0]); + EXPECT_NE(mb1, (MBlock*)0); + EXPECT_EQ(mb1->siz, 3 * sizeof(u64)); + mb2 = m->GetBlock((uptr)&block2[3]); + EXPECT_NE(mb2, (MBlock*)0); + EXPECT_EQ(mb2->siz, 1 * sizeof(u64)); + s1 = m->GetIfExistsAndLock((uptr)&block1[0], true); + EXPECT_EQ(s1, (SyncVar*)0); + s2 = m->GetIfExistsAndLock((uptr)&block1[1], true); + EXPECT_EQ(s2, (SyncVar*)0); + s1 = m->GetIfExistsAndLock((uptr)&block2[0], true); + EXPECT_NE(s1, (SyncVar*)0); + EXPECT_EQ(s1->addr, (uptr)&block2[0]); + s1->mtx.Unlock(); + s2 = m->GetIfExistsAndLock((uptr)&block2[1], true); + EXPECT_NE(s2, (SyncVar*)0); + EXPECT_EQ(s2->addr, (uptr)&block2[1]); + s2->mtx.Unlock(); + m->FreeRange(thr->proc(), (uptr)&block2[0], 4 * sizeof(u64)); +} + +TEST(MetaMap, ResetSync) { + ThreadState *thr = cur_thread(); + MetaMap *m = &ctx->metamap; + u64 block[1] = {}; // fake malloc block + m->AllocBlock(thr, 0, (uptr)&block[0], 1 * sizeof(u64)); + SyncVar *s = m->GetOrCreateAndLock(thr, 0, (uptr)&block[0], true); + s->Reset(thr->proc()); + s->mtx.Unlock(); + uptr sz = m->FreeBlock(thr->proc(), (uptr)&block[0]); + EXPECT_EQ(sz, 1 * sizeof(u64)); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cc b/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cc deleted file mode 100644 index a1487310f0a..00000000000 --- a/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cc +++ /dev/null @@ -1,24 +0,0 @@ -//===-- tsan_unit_test_main.cc --------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "gtest/gtest.h" - -namespace __sanitizer { -bool ReexecDisabled() { - return true; -} -} - -int main(int argc, char **argv) { - testing::GTEST_FLAG(death_test_style) = "threadsafe"; - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cpp new file mode 100644 index 00000000000..0e7b18a2ab3 --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cpp @@ -0,0 +1,24 @@ +//===-- tsan_unit_test_main.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +namespace __sanitizer { +bool ReexecDisabled() { + return true; +} +} + +int main(int argc, char **argv) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} -- cgit v1.2.3