1 //===-- tsan_sync.cc ------------------------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is a part of ThreadSanitizer (TSan), a race detector.
11 //
12 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_placement_new.h"
14 #include "tsan_sync.h"
15 #include "tsan_rtl.h"
16 #include "tsan_mman.h"
17
18 namespace __tsan {
19
20 void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s);
21
SyncVar()22 SyncVar::SyncVar()
23 : mtx(MutexTypeSyncVar, StatMtxSyncVar) {
24 Reset();
25 }
26
Init(ThreadState * thr,uptr pc,uptr addr,u64 uid)27 void SyncVar::Init(ThreadState *thr, uptr pc, uptr addr, u64 uid) {
28 this->addr = addr;
29 this->uid = uid;
30 this->next = 0;
31
32 creation_stack_id = 0;
33 if (kCppMode) // Go does not use them
34 creation_stack_id = CurrentStackId(thr, pc);
35 if (flags()->detect_deadlocks)
36 DDMutexInit(thr, pc, this);
37 }
38
Reset()39 void SyncVar::Reset() {
40 uid = 0;
41 creation_stack_id = 0;
42 owner_tid = kInvalidTid;
43 last_lock = 0;
44 recursion = 0;
45 is_rw = 0;
46 is_recursive = 0;
47 is_broken = 0;
48 is_linker_init = 0;
49
50 clock.Zero();
51 read_clock.Reset();
52 }
53
MetaMap()54 MetaMap::MetaMap() {
55 atomic_store(&uid_gen_, 0, memory_order_relaxed);
56 }
57
AllocBlock(ThreadState * thr,uptr pc,uptr p,uptr sz)58 void MetaMap::AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz) {
59 u32 idx = block_alloc_.Alloc(&thr->block_cache);
60 MBlock *b = block_alloc_.Map(idx);
61 b->siz = sz;
62 b->tid = thr->tid;
63 b->stk = CurrentStackId(thr, pc);
64 u32 *meta = MemToMeta(p);
65 DCHECK_EQ(*meta, 0);
66 *meta = idx | kFlagBlock;
67 }
68
FreeBlock(ThreadState * thr,uptr pc,uptr p)69 uptr MetaMap::FreeBlock(ThreadState *thr, uptr pc, uptr p) {
70 MBlock* b = GetBlock(p);
71 if (b == 0)
72 return 0;
73 uptr sz = RoundUpTo(b->siz, kMetaShadowCell);
74 FreeRange(thr, pc, p, sz);
75 return sz;
76 }
77
FreeRange(ThreadState * thr,uptr pc,uptr p,uptr sz)78 void MetaMap::FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz) {
79 u32 *meta = MemToMeta(p);
80 u32 *end = MemToMeta(p + sz);
81 if (end == meta)
82 end++;
83 for (; meta < end; meta++) {
84 u32 idx = *meta;
85 *meta = 0;
86 for (;;) {
87 if (idx == 0)
88 break;
89 if (idx & kFlagBlock) {
90 block_alloc_.Free(&thr->block_cache, idx & ~kFlagMask);
91 break;
92 } else if (idx & kFlagSync) {
93 DCHECK(idx & kFlagSync);
94 SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
95 u32 next = s->next;
96 s->Reset();
97 sync_alloc_.Free(&thr->sync_cache, idx & ~kFlagMask);
98 idx = next;
99 } else {
100 CHECK(0);
101 }
102 }
103 }
104 }
105
GetBlock(uptr p)106 MBlock* MetaMap::GetBlock(uptr p) {
107 u32 *meta = MemToMeta(p);
108 u32 idx = *meta;
109 for (;;) {
110 if (idx == 0)
111 return 0;
112 if (idx & kFlagBlock)
113 return block_alloc_.Map(idx & ~kFlagMask);
114 DCHECK(idx & kFlagSync);
115 SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
116 idx = s->next;
117 }
118 }
119
GetOrCreateAndLock(ThreadState * thr,uptr pc,uptr addr,bool write_lock)120 SyncVar* MetaMap::GetOrCreateAndLock(ThreadState *thr, uptr pc,
121 uptr addr, bool write_lock) {
122 return GetAndLock(thr, pc, addr, write_lock, true);
123 }
124
GetIfExistsAndLock(uptr addr)125 SyncVar* MetaMap::GetIfExistsAndLock(uptr addr) {
126 return GetAndLock(0, 0, addr, true, false);
127 }
128
GetAndLock(ThreadState * thr,uptr pc,uptr addr,bool write_lock,bool create)129 SyncVar* MetaMap::GetAndLock(ThreadState *thr, uptr pc,
130 uptr addr, bool write_lock, bool create) {
131 u32 *meta = MemToMeta(addr);
132 u32 idx0 = *meta;
133 u32 myidx = 0;
134 SyncVar *mys = 0;
135 for (;;) {
136 u32 idx = idx0;
137 for (;;) {
138 if (idx == 0)
139 break;
140 if (idx & kFlagBlock)
141 break;
142 DCHECK(idx & kFlagSync);
143 SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
144 if (s->addr == addr) {
145 if (myidx != 0) {
146 mys->Reset();
147 sync_alloc_.Free(&thr->sync_cache, myidx);
148 }
149 if (write_lock)
150 s->mtx.Lock();
151 else
152 s->mtx.ReadLock();
153 return s;
154 }
155 idx = s->next;
156 }
157 if (!create)
158 return 0;
159 if (*meta != idx0) {
160 idx0 = *meta;
161 continue;
162 }
163
164 if (myidx == 0) {
165 const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
166 myidx = sync_alloc_.Alloc(&thr->sync_cache);
167 mys = sync_alloc_.Map(myidx);
168 mys->Init(thr, pc, addr, uid);
169 }
170 mys->next = idx0;
171 if (atomic_compare_exchange_strong((atomic_uint32_t*)meta, &idx0,
172 myidx | kFlagSync, memory_order_release)) {
173 if (write_lock)
174 mys->mtx.Lock();
175 else
176 mys->mtx.ReadLock();
177 return mys;
178 }
179 }
180 }
181
MoveMemory(uptr src,uptr dst,uptr sz)182 void MetaMap::MoveMemory(uptr src, uptr dst, uptr sz) {
183 // src and dst can overlap,
184 // there are no concurrent accesses to the regions (e.g. stop-the-world).
185 CHECK_NE(src, dst);
186 CHECK_NE(sz, 0);
187 uptr diff = dst - src;
188 u32 *src_meta = MemToMeta(src);
189 u32 *dst_meta = MemToMeta(dst);
190 u32 *src_meta_end = MemToMeta(src + sz);
191 uptr inc = 1;
192 if (dst > src) {
193 src_meta = MemToMeta(src + sz) - 1;
194 dst_meta = MemToMeta(dst + sz) - 1;
195 src_meta_end = MemToMeta(src) - 1;
196 inc = -1;
197 }
198 for (; src_meta != src_meta_end; src_meta += inc, dst_meta += inc) {
199 CHECK_EQ(*dst_meta, 0);
200 u32 idx = *src_meta;
201 *src_meta = 0;
202 *dst_meta = idx;
203 // Patch the addresses in sync objects.
204 while (idx != 0) {
205 if (idx & kFlagBlock)
206 break;
207 CHECK(idx & kFlagSync);
208 SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
209 s->addr += diff;
210 idx = s->next;
211 }
212 }
213 }
214
OnThreadIdle(ThreadState * thr)215 void MetaMap::OnThreadIdle(ThreadState *thr) {
216 block_alloc_.FlushCache(&thr->block_cache);
217 sync_alloc_.FlushCache(&thr->sync_cache);
218 }
219
220 } // namespace __tsan
221