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