2018-02-19 Sebastian Perta <sebastian.perta@renesas.com>
[official-gcc.git] / libsanitizer / tsan / tsan_sync.cc
blob0f840de1f11bd8566cfabec1cb58e587f0d82797
1 //===-- tsan_sync.cc ------------------------------------------------------===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // This file is a part of ThreadSanitizer (TSan), a race detector.
9 //
10 //===----------------------------------------------------------------------===//
11 #include "sanitizer_common/sanitizer_placement_new.h"
12 #include "tsan_sync.h"
13 #include "tsan_rtl.h"
14 #include "tsan_mman.h"
16 namespace __tsan {
18 void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s);
20 SyncVar::SyncVar()
21 : mtx(MutexTypeSyncVar, StatMtxSyncVar) {
22 Reset(0);
25 void SyncVar::Init(ThreadState *thr, uptr pc, uptr addr, u64 uid) {
26 this->addr = addr;
27 this->uid = uid;
28 this->next = 0;
30 creation_stack_id = 0;
31 if (!SANITIZER_GO) // Go does not use them
32 creation_stack_id = CurrentStackId(thr, pc);
33 if (common_flags()->detect_deadlocks)
34 DDMutexInit(thr, pc, this);
37 void SyncVar::Reset(Processor *proc) {
38 uid = 0;
39 creation_stack_id = 0;
40 owner_tid = kInvalidTid;
41 last_lock = 0;
42 recursion = 0;
43 atomic_store_relaxed(&flags, 0);
45 if (proc == 0) {
46 CHECK_EQ(clock.size(), 0);
47 CHECK_EQ(read_clock.size(), 0);
48 } else {
49 clock.Reset(&proc->clock_cache);
50 read_clock.Reset(&proc->clock_cache);
54 MetaMap::MetaMap()
55 : block_alloc_("heap block allocator")
56 , sync_alloc_("sync allocator") {
57 atomic_store(&uid_gen_, 0, memory_order_relaxed);
60 void MetaMap::AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz) {
61 u32 idx = block_alloc_.Alloc(&thr->proc()->block_cache);
62 MBlock *b = block_alloc_.Map(idx);
63 b->siz = sz;
64 b->tag = 0;
65 b->tid = thr->tid;
66 b->stk = CurrentStackId(thr, pc);
67 u32 *meta = MemToMeta(p);
68 DCHECK_EQ(*meta, 0);
69 *meta = idx | kFlagBlock;
72 uptr MetaMap::FreeBlock(Processor *proc, uptr p) {
73 MBlock* b = GetBlock(p);
74 if (b == 0)
75 return 0;
76 uptr sz = RoundUpTo(b->siz, kMetaShadowCell);
77 FreeRange(proc, p, sz);
78 return sz;
81 bool MetaMap::FreeRange(Processor *proc, uptr p, uptr sz) {
82 bool has_something = false;
83 u32 *meta = MemToMeta(p);
84 u32 *end = MemToMeta(p + sz);
85 if (end == meta)
86 end++;
87 for (; meta < end; meta++) {
88 u32 idx = *meta;
89 if (idx == 0) {
90 // Note: don't write to meta in this case -- the block can be huge.
91 continue;
93 *meta = 0;
94 has_something = true;
95 while (idx != 0) {
96 if (idx & kFlagBlock) {
97 block_alloc_.Free(&proc->block_cache, idx & ~kFlagMask);
98 break;
99 } else if (idx & kFlagSync) {
100 DCHECK(idx & kFlagSync);
101 SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
102 u32 next = s->next;
103 s->Reset(proc);
104 sync_alloc_.Free(&proc->sync_cache, idx & ~kFlagMask);
105 idx = next;
106 } else {
107 CHECK(0);
111 return has_something;
114 // ResetRange removes all meta objects from the range.
115 // It is called for large mmap-ed regions. The function is best-effort wrt
116 // freeing of meta objects, because we don't want to page in the whole range
117 // which can be huge. The function probes pages one-by-one until it finds a page
118 // without meta objects, at this point it stops freeing meta objects. Because
119 // thread stacks grow top-down, we do the same starting from end as well.
120 void MetaMap::ResetRange(Processor *proc, uptr p, uptr sz) {
121 if (SANITIZER_GO) {
122 // UnmapOrDie/MmapFixedNoReserve does not work on Windows,
123 // so we do the optimization only for C/C++.
124 FreeRange(proc, p, sz);
125 return;
127 const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
128 const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
129 if (sz <= 4 * kPageSize) {
130 // If the range is small, just do the normal free procedure.
131 FreeRange(proc, p, sz);
132 return;
134 // First, round both ends of the range to page size.
135 uptr diff = RoundUp(p, kPageSize) - p;
136 if (diff != 0) {
137 FreeRange(proc, p, diff);
138 p += diff;
139 sz -= diff;
141 diff = p + sz - RoundDown(p + sz, kPageSize);
142 if (diff != 0) {
143 FreeRange(proc, p + sz - diff, diff);
144 sz -= diff;
146 // Now we must have a non-empty page-aligned range.
147 CHECK_GT(sz, 0);
148 CHECK_EQ(p, RoundUp(p, kPageSize));
149 CHECK_EQ(sz, RoundUp(sz, kPageSize));
150 const uptr p0 = p;
151 const uptr sz0 = sz;
152 // Probe start of the range.
153 for (uptr checked = 0; sz > 0; checked += kPageSize) {
154 bool has_something = FreeRange(proc, p, kPageSize);
155 p += kPageSize;
156 sz -= kPageSize;
157 if (!has_something && checked > (128 << 10))
158 break;
160 // Probe end of the range.
161 for (uptr checked = 0; sz > 0; checked += kPageSize) {
162 bool has_something = FreeRange(proc, p + sz - kPageSize, kPageSize);
163 sz -= kPageSize;
164 // Stacks grow down, so sync object are most likely at the end of the region
165 // (if it is a stack). The very end of the stack is TLS and tsan increases
166 // TLS by at least 256K, so check at least 512K.
167 if (!has_something && checked > (512 << 10))
168 break;
170 // Finally, page out the whole range (including the parts that we've just
171 // freed). Note: we can't simply madvise, because we need to leave a zeroed
172 // range (otherwise __tsan_java_move can crash if it encounters a left-over
173 // meta objects in java heap).
174 uptr metap = (uptr)MemToMeta(p0);
175 uptr metasz = sz0 / kMetaRatio;
176 UnmapOrDie((void*)metap, metasz);
177 MmapFixedNoReserve(metap, metasz);
180 MBlock* MetaMap::GetBlock(uptr p) {
181 u32 *meta = MemToMeta(p);
182 u32 idx = *meta;
183 for (;;) {
184 if (idx == 0)
185 return 0;
186 if (idx & kFlagBlock)
187 return block_alloc_.Map(idx & ~kFlagMask);
188 DCHECK(idx & kFlagSync);
189 SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
190 idx = s->next;
194 SyncVar* MetaMap::GetOrCreateAndLock(ThreadState *thr, uptr pc,
195 uptr addr, bool write_lock) {
196 return GetAndLock(thr, pc, addr, write_lock, true);
199 SyncVar* MetaMap::GetIfExistsAndLock(uptr addr, bool write_lock) {
200 return GetAndLock(0, 0, addr, write_lock, false);
203 SyncVar* MetaMap::GetAndLock(ThreadState *thr, uptr pc,
204 uptr addr, bool write_lock, bool create) {
205 u32 *meta = MemToMeta(addr);
206 u32 idx0 = *meta;
207 u32 myidx = 0;
208 SyncVar *mys = 0;
209 for (;;) {
210 u32 idx = idx0;
211 for (;;) {
212 if (idx == 0)
213 break;
214 if (idx & kFlagBlock)
215 break;
216 DCHECK(idx & kFlagSync);
217 SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
218 if (s->addr == addr) {
219 if (myidx != 0) {
220 mys->Reset(thr->proc());
221 sync_alloc_.Free(&thr->proc()->sync_cache, myidx);
223 if (write_lock)
224 s->mtx.Lock();
225 else
226 s->mtx.ReadLock();
227 return s;
229 idx = s->next;
231 if (!create)
232 return 0;
233 if (*meta != idx0) {
234 idx0 = *meta;
235 continue;
238 if (myidx == 0) {
239 const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
240 myidx = sync_alloc_.Alloc(&thr->proc()->sync_cache);
241 mys = sync_alloc_.Map(myidx);
242 mys->Init(thr, pc, addr, uid);
244 mys->next = idx0;
245 if (atomic_compare_exchange_strong((atomic_uint32_t*)meta, &idx0,
246 myidx | kFlagSync, memory_order_release)) {
247 if (write_lock)
248 mys->mtx.Lock();
249 else
250 mys->mtx.ReadLock();
251 return mys;
256 void MetaMap::MoveMemory(uptr src, uptr dst, uptr sz) {
257 // src and dst can overlap,
258 // there are no concurrent accesses to the regions (e.g. stop-the-world).
259 CHECK_NE(src, dst);
260 CHECK_NE(sz, 0);
261 uptr diff = dst - src;
262 u32 *src_meta = MemToMeta(src);
263 u32 *dst_meta = MemToMeta(dst);
264 u32 *src_meta_end = MemToMeta(src + sz);
265 uptr inc = 1;
266 if (dst > src) {
267 src_meta = MemToMeta(src + sz) - 1;
268 dst_meta = MemToMeta(dst + sz) - 1;
269 src_meta_end = MemToMeta(src) - 1;
270 inc = -1;
272 for (; src_meta != src_meta_end; src_meta += inc, dst_meta += inc) {
273 CHECK_EQ(*dst_meta, 0);
274 u32 idx = *src_meta;
275 *src_meta = 0;
276 *dst_meta = idx;
277 // Patch the addresses in sync objects.
278 while (idx != 0) {
279 if (idx & kFlagBlock)
280 break;
281 CHECK(idx & kFlagSync);
282 SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
283 s->addr += diff;
284 idx = s->next;
289 void MetaMap::OnProcIdle(Processor *proc) {
290 block_alloc_.FlushCache(&proc->block_cache);
291 sync_alloc_.FlushCache(&proc->sync_cache);
294 } // namespace __tsan