1 //===-- tsan_sync.cpp -----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is a part of ThreadSanitizer (TSan), a race detector.
11 //===----------------------------------------------------------------------===//
12 #include "sanitizer_common/sanitizer_placement_new.h"
13 #include "tsan_sync.h"
15 #include "tsan_mman.h"
19 void DDMutexInit(ThreadState
*thr
, uptr pc
, SyncVar
*s
);
22 : mtx(MutexTypeSyncVar
, StatMtxSyncVar
) {
26 void SyncVar::Init(ThreadState
*thr
, uptr pc
, uptr addr
, u64 uid
) {
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);
38 void SyncVar::Reset(Processor
*proc
) {
40 creation_stack_id
= 0;
41 owner_tid
= kInvalidTid
;
44 atomic_store_relaxed(&flags
, 0);
47 CHECK_EQ(clock
.size(), 0);
48 CHECK_EQ(read_clock
.size(), 0);
50 clock
.Reset(&proc
->clock_cache
);
51 read_clock
.Reset(&proc
->clock_cache
);
56 : block_alloc_("heap block allocator")
57 , sync_alloc_("sync allocator") {
58 atomic_store(&uid_gen_
, 0, memory_order_relaxed
);
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
);
67 b
->stk
= CurrentStackId(thr
, pc
);
68 u32
*meta
= MemToMeta(p
);
70 *meta
= idx
| kFlagBlock
;
73 uptr
MetaMap::FreeBlock(Processor
*proc
, uptr p
) {
74 MBlock
* b
= GetBlock(p
);
77 uptr sz
= RoundUpTo(b
->siz
, kMetaShadowCell
);
78 FreeRange(proc
, p
, sz
);
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
);
88 for (; meta
< end
; meta
++) {
91 // Note: don't write to meta in this case -- the block can be huge.
97 if (idx
& kFlagBlock
) {
98 block_alloc_
.Free(&proc
->block_cache
, idx
& ~kFlagMask
);
100 } else if (idx
& kFlagSync
) {
101 DCHECK(idx
& kFlagSync
);
102 SyncVar
*s
= sync_alloc_
.Map(idx
& ~kFlagMask
);
105 sync_alloc_
.Free(&proc
->sync_cache
, idx
& ~kFlagMask
);
112 return has_something
;
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
) {
123 // UnmapOrDie/MmapFixedNoReserve does not work on Windows,
124 // so we do the optimization only for C/C++.
125 FreeRange(proc
, p
, sz
);
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
);
135 // First, round both ends of the range to page size.
136 uptr diff
= RoundUp(p
, kPageSize
) - p
;
138 FreeRange(proc
, p
, diff
);
142 diff
= p
+ sz
- RoundDown(p
+ sz
, kPageSize
);
144 FreeRange(proc
, p
+ sz
- diff
, diff
);
147 // Now we must have a non-empty page-aligned range.
149 CHECK_EQ(p
, RoundUp(p
, kPageSize
));
150 CHECK_EQ(sz
, RoundUp(sz
, kPageSize
));
153 // Probe start of the range.
154 for (uptr checked
= 0; sz
> 0; checked
+= kPageSize
) {
155 bool has_something
= FreeRange(proc
, p
, kPageSize
);
158 if (!has_something
&& checked
> (128 << 10))
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
);
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))
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
))
182 MBlock
* MetaMap::GetBlock(uptr p
) {
183 u32
*meta
= MemToMeta(p
);
188 if (idx
& kFlagBlock
)
189 return block_alloc_
.Map(idx
& ~kFlagMask
);
190 DCHECK(idx
& kFlagSync
);
191 SyncVar
* s
= sync_alloc_
.Map(idx
& ~kFlagMask
);
196 SyncVar
* MetaMap::GetOrCreateAndLock(ThreadState
*thr
, uptr pc
,
197 uptr addr
, bool write_lock
) {
198 return GetAndLock(thr
, pc
, addr
, write_lock
, true);
201 SyncVar
* MetaMap::GetIfExistsAndLock(uptr addr
, bool write_lock
) {
202 return GetAndLock(0, 0, addr
, write_lock
, false);
205 SyncVar
* MetaMap::GetAndLock(ThreadState
*thr
, uptr pc
,
206 uptr addr
, bool write_lock
, bool create
) {
207 u32
*meta
= MemToMeta(addr
);
216 if (idx
& kFlagBlock
)
218 DCHECK(idx
& kFlagSync
);
219 SyncVar
* s
= sync_alloc_
.Map(idx
& ~kFlagMask
);
220 if (s
->addr
== addr
) {
222 mys
->Reset(thr
->proc());
223 sync_alloc_
.Free(&thr
->proc()->sync_cache
, myidx
);
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
);
247 if (atomic_compare_exchange_strong((atomic_uint32_t
*)meta
, &idx0
,
248 myidx
| kFlagSync
, memory_order_release
)) {
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).
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
);
269 src_meta
= MemToMeta(src
+ sz
) - 1;
270 dst_meta
= MemToMeta(dst
+ sz
) - 1;
271 src_meta_end
= MemToMeta(src
) - 1;
274 for (; src_meta
!= src_meta_end
; src_meta
+= inc
, dst_meta
+= inc
) {
275 CHECK_EQ(*dst_meta
, 0);
279 // Patch the addresses in sync objects.
281 if (idx
& kFlagBlock
)
283 CHECK(idx
& kFlagSync
);
284 SyncVar
*s
= sync_alloc_
.Map(idx
& ~kFlagMask
);
291 void MetaMap::OnProcIdle(Processor
*proc
) {
292 block_alloc_
.FlushCache(&proc
->block_cache
);
293 sync_alloc_
.FlushCache(&proc
->sync_cache
);
296 } // namespace __tsan