| //===-- tsan_test_util.h ----------------------------------------*- C++ -*-===// |
| // |
| // 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. |
| //===----------------------------------------------------------------------===// |
| #ifndef TSAN_TEST_UTIL_H |
| #define TSAN_TEST_UTIL_H |
| |
| void TestMutexBeforeInit(); |
| |
| // A location of memory on which a race may be detected. |
| class MemLoc { |
| public: |
| explicit MemLoc(int offset_from_aligned = 0); |
| explicit MemLoc(void *const real_addr) : loc_(real_addr) { } |
| ~MemLoc(); |
| void *loc() const { return loc_; } |
| private: |
| void *const loc_; |
| MemLoc(const MemLoc&); |
| void operator = (const MemLoc&); |
| }; |
| |
| class UserMutex { |
| public: |
| enum Type { |
| Normal, |
| RW, |
| #ifndef __APPLE__ |
| Spin |
| #else |
| Spin = Normal |
| #endif |
| }; |
| |
| explicit UserMutex(Type type = Normal); |
| ~UserMutex(); |
| |
| void Init(); |
| void StaticInit(); // Emulates static initialization (tsan invisible). |
| void Destroy(); |
| void Lock(); |
| bool TryLock(); |
| void Unlock(); |
| void ReadLock(); |
| bool TryReadLock(); |
| void ReadUnlock(); |
| |
| private: |
| // Placeholder for pthread_mutex_t, CRITICAL_SECTION or whatever. |
| void *mtx_[128]; |
| bool alive_; |
| const Type type_; |
| |
| UserMutex(const UserMutex &); |
| void operator=(const UserMutex &); |
| }; |
| |
| // A thread is started in CTOR and joined in DTOR. |
| class ScopedThread { |
| public: |
| explicit ScopedThread(bool detached = false, bool main = false); |
| ~ScopedThread(); |
| void Detach(); |
| |
| void Access(void *addr, bool is_write, int size, bool expect_race); |
| void Read(const MemLoc &ml, int size, bool expect_race = false) { |
| Access(ml.loc(), false, size, expect_race); |
| } |
| void Write(const MemLoc &ml, int size, bool expect_race = false) { |
| Access(ml.loc(), true, size, expect_race); |
| } |
| void Read1(const MemLoc &ml, bool expect_race = false) { |
| Read(ml, 1, expect_race); } |
| void Read2(const MemLoc &ml, bool expect_race = false) { |
| Read(ml, 2, expect_race); } |
| void Read4(const MemLoc &ml, bool expect_race = false) { |
| Read(ml, 4, expect_race); } |
| void Read8(const MemLoc &ml, bool expect_race = false) { |
| Read(ml, 8, expect_race); } |
| void Write1(const MemLoc &ml, bool expect_race = false) { |
| Write(ml, 1, expect_race); } |
| void Write2(const MemLoc &ml, bool expect_race = false) { |
| Write(ml, 2, expect_race); } |
| void Write4(const MemLoc &ml, bool expect_race = false) { |
| Write(ml, 4, expect_race); } |
| void Write8(const MemLoc &ml, bool expect_race = false) { |
| Write(ml, 8, expect_race); } |
| |
| void VptrUpdate(const MemLoc &vptr, const MemLoc &new_val, |
| bool expect_race = false); |
| |
| void Call(void(*pc)()); |
| void Return(); |
| |
| void Create(const UserMutex &m); |
| void Destroy(const UserMutex &m); |
| void Lock(const UserMutex &m); |
| bool TryLock(const UserMutex &m); |
| void Unlock(const UserMutex &m); |
| void ReadLock(const UserMutex &m); |
| bool TryReadLock(const UserMutex &m); |
| void ReadUnlock(const UserMutex &m); |
| |
| void Memcpy(void *dst, const void *src, int size, bool expect_race = false); |
| void Memset(void *dst, int val, int size, bool expect_race = false); |
| |
| private: |
| struct Impl; |
| Impl *impl_; |
| ScopedThread(const ScopedThread&); // Not implemented. |
| void operator = (const ScopedThread&); // Not implemented. |
| }; |
| |
| class MainThread : public ScopedThread { |
| public: |
| MainThread() |
| : ScopedThread(false, true) { |
| } |
| }; |
| |
| #endif // #ifndef TSAN_TEST_UTIL_H |