1 | //===-- tsan_test_util.h ----------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of ThreadSanitizer (TSan), a race detector. |
10 | // |
11 | // Test utils. |
12 | //===----------------------------------------------------------------------===// |
13 | #ifndef TSAN_TEST_UTIL_H |
14 | #define TSAN_TEST_UTIL_H |
15 | |
16 | #include "gtest/gtest.h" |
17 | |
18 | class ThreadSanitizer : public ::testing::Test { |
19 | protected: |
20 | void TearDown() override; |
21 | }; |
22 | |
23 | void TestMutexBeforeInit(); |
24 | |
25 | // A location of memory on which a race may be detected. |
26 | class MemLoc { |
27 | public: |
28 | explicit MemLoc(int offset_from_aligned = 0); |
29 | explicit MemLoc(void *const real_addr) : loc_(real_addr) { } |
30 | ~MemLoc(); |
31 | void *loc() const { return loc_; } |
32 | private: |
33 | void *const loc_; |
34 | MemLoc(const MemLoc&); |
35 | void operator = (const MemLoc&); |
36 | }; |
37 | |
38 | class UserMutex { |
39 | public: |
40 | enum Type { |
41 | Normal, |
42 | RW, |
43 | #ifndef __APPLE__ |
44 | Spin |
45 | #else |
46 | Spin = Normal |
47 | #endif |
48 | }; |
49 | |
50 | explicit UserMutex(Type type = Normal); |
51 | ~UserMutex(); |
52 | |
53 | void Init(); |
54 | void StaticInit(); // Emulates static initialization (tsan invisible). |
55 | void Destroy(); |
56 | void Lock(); |
57 | bool TryLock(); |
58 | void Unlock(); |
59 | void ReadLock(); |
60 | bool TryReadLock(); |
61 | void ReadUnlock(); |
62 | |
63 | private: |
64 | // Placeholder for pthread_mutex_t, CRITICAL_SECTION or whatever. |
65 | void *mtx_[128]; |
66 | bool alive_; |
67 | const Type type_; |
68 | |
69 | UserMutex(const UserMutex &); |
70 | void operator=(const UserMutex &); |
71 | }; |
72 | |
73 | // A thread is started in CTOR and joined in DTOR. |
74 | class ScopedThread { |
75 | public: |
76 | explicit ScopedThread(bool detached = false, bool main = false); |
77 | ~ScopedThread(); |
78 | void Detach(); |
79 | |
80 | void Access(void *addr, bool is_write, int size, bool expect_race); |
81 | void Read(const MemLoc &ml, int size, bool expect_race = false) { |
82 | Access(addr: ml.loc(), is_write: false, size, expect_race); |
83 | } |
84 | void Write(const MemLoc &ml, int size, bool expect_race = false) { |
85 | Access(addr: ml.loc(), is_write: true, size, expect_race); |
86 | } |
87 | void Read1(const MemLoc &ml, bool expect_race = false) { |
88 | Read(ml, size: 1, expect_race); } |
89 | void Read2(const MemLoc &ml, bool expect_race = false) { |
90 | Read(ml, size: 2, expect_race); } |
91 | void Read4(const MemLoc &ml, bool expect_race = false) { |
92 | Read(ml, size: 4, expect_race); } |
93 | void Read8(const MemLoc &ml, bool expect_race = false) { |
94 | Read(ml, size: 8, expect_race); } |
95 | void Write1(const MemLoc &ml, bool expect_race = false) { |
96 | Write(ml, size: 1, expect_race); } |
97 | void Write2(const MemLoc &ml, bool expect_race = false) { |
98 | Write(ml, size: 2, expect_race); } |
99 | void Write4(const MemLoc &ml, bool expect_race = false) { |
100 | Write(ml, size: 4, expect_race); } |
101 | void Write8(const MemLoc &ml, bool expect_race = false) { |
102 | Write(ml, size: 8, expect_race); } |
103 | |
104 | void VptrUpdate(const MemLoc &vptr, const MemLoc &new_val, |
105 | bool expect_race = false); |
106 | |
107 | void Call(void(*pc)()); |
108 | void Return(); |
109 | |
110 | void Create(const UserMutex &m); |
111 | void Destroy(const UserMutex &m); |
112 | void Lock(const UserMutex &m); |
113 | bool TryLock(const UserMutex &m); |
114 | void Unlock(const UserMutex &m); |
115 | void ReadLock(const UserMutex &m); |
116 | bool TryReadLock(const UserMutex &m); |
117 | void ReadUnlock(const UserMutex &m); |
118 | |
119 | void Memcpy(void *dst, const void *src, int size, bool expect_race = false); |
120 | void Memset(void *dst, int val, int size, bool expect_race = false); |
121 | |
122 | private: |
123 | struct Impl; |
124 | Impl *impl_; |
125 | ScopedThread(const ScopedThread&); // Not implemented. |
126 | void operator = (const ScopedThread&); // Not implemented. |
127 | }; |
128 | |
129 | class MainThread : public ScopedThread { |
130 | public: |
131 | MainThread() |
132 | : ScopedThread(false, true) { |
133 | } |
134 | }; |
135 | |
136 | #endif // #ifndef TSAN_TEST_UTIL_H |
137 | |