13cab2bb3Spatrick //===-- tsan_sync.cpp -----------------------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of ThreadSanitizer (TSan), a race detector.
103cab2bb3Spatrick //
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick #include "sanitizer_common/sanitizer_placement_new.h"
133cab2bb3Spatrick #include "tsan_sync.h"
143cab2bb3Spatrick #include "tsan_rtl.h"
153cab2bb3Spatrick #include "tsan_mman.h"
163cab2bb3Spatrick
173cab2bb3Spatrick namespace __tsan {
183cab2bb3Spatrick
193cab2bb3Spatrick void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s);
203cab2bb3Spatrick
SyncVar()21*810390e3Srobert SyncVar::SyncVar() : mtx(MutexTypeSyncVar) { Reset(); }
223cab2bb3Spatrick
Init(ThreadState * thr,uptr pc,uptr addr,bool save_stack)23*810390e3Srobert void SyncVar::Init(ThreadState *thr, uptr pc, uptr addr, bool save_stack) {
24*810390e3Srobert Reset();
253cab2bb3Spatrick this->addr = addr;
26*810390e3Srobert next = 0;
27*810390e3Srobert if (save_stack && !SANITIZER_GO) // Go does not use them
283cab2bb3Spatrick creation_stack_id = CurrentStackId(thr, pc);
293cab2bb3Spatrick if (common_flags()->detect_deadlocks)
303cab2bb3Spatrick DDMutexInit(thr, pc, this);
313cab2bb3Spatrick }
323cab2bb3Spatrick
Reset()33*810390e3Srobert void SyncVar::Reset() {
34*810390e3Srobert CHECK(!ctx->resetting);
35*810390e3Srobert creation_stack_id = kInvalidStackID;
363cab2bb3Spatrick owner_tid = kInvalidTid;
37*810390e3Srobert last_lock.Reset();
383cab2bb3Spatrick recursion = 0;
393cab2bb3Spatrick atomic_store_relaxed(&flags, 0);
40*810390e3Srobert Free(clock);
41*810390e3Srobert Free(read_clock);
423cab2bb3Spatrick }
433cab2bb3Spatrick
MetaMap()443cab2bb3Spatrick MetaMap::MetaMap()
45*810390e3Srobert : block_alloc_("heap block allocator"), sync_alloc_("sync allocator") {}
463cab2bb3Spatrick
AllocBlock(ThreadState * thr,uptr pc,uptr p,uptr sz)473cab2bb3Spatrick void MetaMap::AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz) {
483cab2bb3Spatrick u32 idx = block_alloc_.Alloc(&thr->proc()->block_cache);
493cab2bb3Spatrick MBlock *b = block_alloc_.Map(idx);
503cab2bb3Spatrick b->siz = sz;
513cab2bb3Spatrick b->tag = 0;
523cab2bb3Spatrick b->tid = thr->tid;
533cab2bb3Spatrick b->stk = CurrentStackId(thr, pc);
543cab2bb3Spatrick u32 *meta = MemToMeta(p);
553cab2bb3Spatrick DCHECK_EQ(*meta, 0);
563cab2bb3Spatrick *meta = idx | kFlagBlock;
573cab2bb3Spatrick }
583cab2bb3Spatrick
FreeBlock(Processor * proc,uptr p,bool reset)59*810390e3Srobert uptr MetaMap::FreeBlock(Processor *proc, uptr p, bool reset) {
603cab2bb3Spatrick MBlock* b = GetBlock(p);
613cab2bb3Spatrick if (b == 0)
623cab2bb3Spatrick return 0;
633cab2bb3Spatrick uptr sz = RoundUpTo(b->siz, kMetaShadowCell);
64*810390e3Srobert FreeRange(proc, p, sz, reset);
653cab2bb3Spatrick return sz;
663cab2bb3Spatrick }
673cab2bb3Spatrick
FreeRange(Processor * proc,uptr p,uptr sz,bool reset)68*810390e3Srobert bool MetaMap::FreeRange(Processor *proc, uptr p, uptr sz, bool reset) {
693cab2bb3Spatrick bool has_something = false;
703cab2bb3Spatrick u32 *meta = MemToMeta(p);
713cab2bb3Spatrick u32 *end = MemToMeta(p + sz);
723cab2bb3Spatrick if (end == meta)
733cab2bb3Spatrick end++;
743cab2bb3Spatrick for (; meta < end; meta++) {
753cab2bb3Spatrick u32 idx = *meta;
763cab2bb3Spatrick if (idx == 0) {
773cab2bb3Spatrick // Note: don't write to meta in this case -- the block can be huge.
783cab2bb3Spatrick continue;
793cab2bb3Spatrick }
803cab2bb3Spatrick *meta = 0;
813cab2bb3Spatrick has_something = true;
823cab2bb3Spatrick while (idx != 0) {
833cab2bb3Spatrick if (idx & kFlagBlock) {
843cab2bb3Spatrick block_alloc_.Free(&proc->block_cache, idx & ~kFlagMask);
853cab2bb3Spatrick break;
863cab2bb3Spatrick } else if (idx & kFlagSync) {
873cab2bb3Spatrick DCHECK(idx & kFlagSync);
883cab2bb3Spatrick SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
893cab2bb3Spatrick u32 next = s->next;
90*810390e3Srobert if (reset)
91*810390e3Srobert s->Reset();
923cab2bb3Spatrick sync_alloc_.Free(&proc->sync_cache, idx & ~kFlagMask);
933cab2bb3Spatrick idx = next;
943cab2bb3Spatrick } else {
953cab2bb3Spatrick CHECK(0);
963cab2bb3Spatrick }
973cab2bb3Spatrick }
983cab2bb3Spatrick }
993cab2bb3Spatrick return has_something;
1003cab2bb3Spatrick }
1013cab2bb3Spatrick
1023cab2bb3Spatrick // ResetRange removes all meta objects from the range.
1033cab2bb3Spatrick // It is called for large mmap-ed regions. The function is best-effort wrt
1043cab2bb3Spatrick // freeing of meta objects, because we don't want to page in the whole range
1053cab2bb3Spatrick // which can be huge. The function probes pages one-by-one until it finds a page
1063cab2bb3Spatrick // without meta objects, at this point it stops freeing meta objects. Because
1073cab2bb3Spatrick // thread stacks grow top-down, we do the same starting from end as well.
ResetRange(Processor * proc,uptr p,uptr sz,bool reset)108*810390e3Srobert void MetaMap::ResetRange(Processor *proc, uptr p, uptr sz, bool reset) {
1093cab2bb3Spatrick if (SANITIZER_GO) {
1103cab2bb3Spatrick // UnmapOrDie/MmapFixedNoReserve does not work on Windows,
1113cab2bb3Spatrick // so we do the optimization only for C/C++.
112*810390e3Srobert FreeRange(proc, p, sz, reset);
1133cab2bb3Spatrick return;
1143cab2bb3Spatrick }
1153cab2bb3Spatrick const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
1163cab2bb3Spatrick const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
1173cab2bb3Spatrick if (sz <= 4 * kPageSize) {
1183cab2bb3Spatrick // If the range is small, just do the normal free procedure.
119*810390e3Srobert FreeRange(proc, p, sz, reset);
1203cab2bb3Spatrick return;
1213cab2bb3Spatrick }
1223cab2bb3Spatrick // First, round both ends of the range to page size.
1233cab2bb3Spatrick uptr diff = RoundUp(p, kPageSize) - p;
1243cab2bb3Spatrick if (diff != 0) {
125*810390e3Srobert FreeRange(proc, p, diff, reset);
1263cab2bb3Spatrick p += diff;
1273cab2bb3Spatrick sz -= diff;
1283cab2bb3Spatrick }
1293cab2bb3Spatrick diff = p + sz - RoundDown(p + sz, kPageSize);
1303cab2bb3Spatrick if (diff != 0) {
131*810390e3Srobert FreeRange(proc, p + sz - diff, diff, reset);
1323cab2bb3Spatrick sz -= diff;
1333cab2bb3Spatrick }
1343cab2bb3Spatrick // Now we must have a non-empty page-aligned range.
1353cab2bb3Spatrick CHECK_GT(sz, 0);
1363cab2bb3Spatrick CHECK_EQ(p, RoundUp(p, kPageSize));
1373cab2bb3Spatrick CHECK_EQ(sz, RoundUp(sz, kPageSize));
1383cab2bb3Spatrick const uptr p0 = p;
1393cab2bb3Spatrick const uptr sz0 = sz;
1403cab2bb3Spatrick // Probe start of the range.
1413cab2bb3Spatrick for (uptr checked = 0; sz > 0; checked += kPageSize) {
142*810390e3Srobert bool has_something = FreeRange(proc, p, kPageSize, reset);
1433cab2bb3Spatrick p += kPageSize;
1443cab2bb3Spatrick sz -= kPageSize;
1453cab2bb3Spatrick if (!has_something && checked > (128 << 10))
1463cab2bb3Spatrick break;
1473cab2bb3Spatrick }
1483cab2bb3Spatrick // Probe end of the range.
1493cab2bb3Spatrick for (uptr checked = 0; sz > 0; checked += kPageSize) {
150*810390e3Srobert bool has_something = FreeRange(proc, p + sz - kPageSize, kPageSize, reset);
1513cab2bb3Spatrick sz -= kPageSize;
1523cab2bb3Spatrick // Stacks grow down, so sync object are most likely at the end of the region
1533cab2bb3Spatrick // (if it is a stack). The very end of the stack is TLS and tsan increases
1543cab2bb3Spatrick // TLS by at least 256K, so check at least 512K.
1553cab2bb3Spatrick if (!has_something && checked > (512 << 10))
1563cab2bb3Spatrick break;
1573cab2bb3Spatrick }
1583cab2bb3Spatrick // Finally, page out the whole range (including the parts that we've just
1593cab2bb3Spatrick // freed). Note: we can't simply madvise, because we need to leave a zeroed
1603cab2bb3Spatrick // range (otherwise __tsan_java_move can crash if it encounters a left-over
1613cab2bb3Spatrick // meta objects in java heap).
1623cab2bb3Spatrick uptr metap = (uptr)MemToMeta(p0);
1633cab2bb3Spatrick uptr metasz = sz0 / kMetaRatio;
1643cab2bb3Spatrick UnmapOrDie((void*)metap, metasz);
165d89ec533Spatrick if (!MmapFixedSuperNoReserve(metap, metasz))
1663cab2bb3Spatrick Die();
1673cab2bb3Spatrick }
1683cab2bb3Spatrick
ResetClocks()169*810390e3Srobert void MetaMap::ResetClocks() {
170*810390e3Srobert // This can be called from the background thread
171*810390e3Srobert // which does not have proc/cache.
172*810390e3Srobert // The cache is too large for stack.
173*810390e3Srobert static InternalAllocatorCache cache;
174*810390e3Srobert internal_memset(&cache, 0, sizeof(cache));
175*810390e3Srobert internal_allocator()->InitCache(&cache);
176*810390e3Srobert sync_alloc_.ForEach([&](SyncVar *s) {
177*810390e3Srobert if (s->clock) {
178*810390e3Srobert InternalFree(s->clock, &cache);
179*810390e3Srobert s->clock = nullptr;
180*810390e3Srobert }
181*810390e3Srobert if (s->read_clock) {
182*810390e3Srobert InternalFree(s->read_clock, &cache);
183*810390e3Srobert s->read_clock = nullptr;
184*810390e3Srobert }
185*810390e3Srobert s->last_lock.Reset();
186*810390e3Srobert });
187*810390e3Srobert internal_allocator()->DestroyCache(&cache);
188*810390e3Srobert }
189*810390e3Srobert
GetBlock(uptr p)1903cab2bb3Spatrick MBlock* MetaMap::GetBlock(uptr p) {
1913cab2bb3Spatrick u32 *meta = MemToMeta(p);
1923cab2bb3Spatrick u32 idx = *meta;
1933cab2bb3Spatrick for (;;) {
1943cab2bb3Spatrick if (idx == 0)
1953cab2bb3Spatrick return 0;
1963cab2bb3Spatrick if (idx & kFlagBlock)
1973cab2bb3Spatrick return block_alloc_.Map(idx & ~kFlagMask);
1983cab2bb3Spatrick DCHECK(idx & kFlagSync);
1993cab2bb3Spatrick SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
2003cab2bb3Spatrick idx = s->next;
2013cab2bb3Spatrick }
2023cab2bb3Spatrick }
2033cab2bb3Spatrick
GetSync(ThreadState * thr,uptr pc,uptr addr,bool create,bool save_stack)204*810390e3Srobert SyncVar *MetaMap::GetSync(ThreadState *thr, uptr pc, uptr addr, bool create,
205*810390e3Srobert bool save_stack) {
206*810390e3Srobert DCHECK(!create || thr->slot_locked);
2073cab2bb3Spatrick u32 *meta = MemToMeta(addr);
2083cab2bb3Spatrick u32 idx0 = *meta;
2093cab2bb3Spatrick u32 myidx = 0;
210*810390e3Srobert SyncVar *mys = nullptr;
2113cab2bb3Spatrick for (;;) {
212*810390e3Srobert for (u32 idx = idx0; idx && !(idx & kFlagBlock);) {
2133cab2bb3Spatrick DCHECK(idx & kFlagSync);
2143cab2bb3Spatrick SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
215*810390e3Srobert if (LIKELY(s->addr == addr)) {
216*810390e3Srobert if (UNLIKELY(myidx != 0)) {
217*810390e3Srobert mys->Reset();
2183cab2bb3Spatrick sync_alloc_.Free(&thr->proc()->sync_cache, myidx);
2193cab2bb3Spatrick }
2203cab2bb3Spatrick return s;
2213cab2bb3Spatrick }
2223cab2bb3Spatrick idx = s->next;
2233cab2bb3Spatrick }
2243cab2bb3Spatrick if (!create)
225*810390e3Srobert return nullptr;
226*810390e3Srobert if (UNLIKELY(*meta != idx0)) {
2273cab2bb3Spatrick idx0 = *meta;
2283cab2bb3Spatrick continue;
2293cab2bb3Spatrick }
2303cab2bb3Spatrick
231*810390e3Srobert if (LIKELY(myidx == 0)) {
2323cab2bb3Spatrick myidx = sync_alloc_.Alloc(&thr->proc()->sync_cache);
2333cab2bb3Spatrick mys = sync_alloc_.Map(myidx);
234*810390e3Srobert mys->Init(thr, pc, addr, save_stack);
2353cab2bb3Spatrick }
2363cab2bb3Spatrick mys->next = idx0;
2373cab2bb3Spatrick if (atomic_compare_exchange_strong((atomic_uint32_t*)meta, &idx0,
2383cab2bb3Spatrick myidx | kFlagSync, memory_order_release)) {
2393cab2bb3Spatrick return mys;
2403cab2bb3Spatrick }
2413cab2bb3Spatrick }
2423cab2bb3Spatrick }
2433cab2bb3Spatrick
MoveMemory(uptr src,uptr dst,uptr sz)2443cab2bb3Spatrick void MetaMap::MoveMemory(uptr src, uptr dst, uptr sz) {
2453cab2bb3Spatrick // src and dst can overlap,
2463cab2bb3Spatrick // there are no concurrent accesses to the regions (e.g. stop-the-world).
2473cab2bb3Spatrick CHECK_NE(src, dst);
2483cab2bb3Spatrick CHECK_NE(sz, 0);
2493cab2bb3Spatrick uptr diff = dst - src;
2503cab2bb3Spatrick u32 *src_meta = MemToMeta(src);
2513cab2bb3Spatrick u32 *dst_meta = MemToMeta(dst);
2523cab2bb3Spatrick u32 *src_meta_end = MemToMeta(src + sz);
2533cab2bb3Spatrick uptr inc = 1;
2543cab2bb3Spatrick if (dst > src) {
2553cab2bb3Spatrick src_meta = MemToMeta(src + sz) - 1;
2563cab2bb3Spatrick dst_meta = MemToMeta(dst + sz) - 1;
2573cab2bb3Spatrick src_meta_end = MemToMeta(src) - 1;
2583cab2bb3Spatrick inc = -1;
2593cab2bb3Spatrick }
2603cab2bb3Spatrick for (; src_meta != src_meta_end; src_meta += inc, dst_meta += inc) {
2613cab2bb3Spatrick CHECK_EQ(*dst_meta, 0);
2623cab2bb3Spatrick u32 idx = *src_meta;
2633cab2bb3Spatrick *src_meta = 0;
2643cab2bb3Spatrick *dst_meta = idx;
2653cab2bb3Spatrick // Patch the addresses in sync objects.
2663cab2bb3Spatrick while (idx != 0) {
2673cab2bb3Spatrick if (idx & kFlagBlock)
2683cab2bb3Spatrick break;
2693cab2bb3Spatrick CHECK(idx & kFlagSync);
2703cab2bb3Spatrick SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
2713cab2bb3Spatrick s->addr += diff;
2723cab2bb3Spatrick idx = s->next;
2733cab2bb3Spatrick }
2743cab2bb3Spatrick }
2753cab2bb3Spatrick }
2763cab2bb3Spatrick
OnProcIdle(Processor * proc)2773cab2bb3Spatrick void MetaMap::OnProcIdle(Processor *proc) {
2783cab2bb3Spatrick block_alloc_.FlushCache(&proc->block_cache);
2793cab2bb3Spatrick sync_alloc_.FlushCache(&proc->sync_cache);
2803cab2bb3Spatrick }
2813cab2bb3Spatrick
GetMemoryStats() const282*810390e3Srobert MetaMap::MemoryStats MetaMap::GetMemoryStats() const {
283*810390e3Srobert MemoryStats stats;
284*810390e3Srobert stats.mem_block = block_alloc_.AllocatedMemory();
285*810390e3Srobert stats.sync_obj = sync_alloc_.AllocatedMemory();
286*810390e3Srobert return stats;
287*810390e3Srobert }
288*810390e3Srobert
2893cab2bb3Spatrick } // namespace __tsan
290