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
);
21 SyncVar::SyncVar() : mtx(MutexTypeSyncVar
) { Reset(); }
23 void SyncVar::Init(ThreadState
*thr
, uptr pc
, uptr addr
, bool save_stack
) {
27 if (save_stack
&& !SANITIZER_GO
) // Go does not use them
28 creation_stack_id
= CurrentStackId(thr
, pc
);
29 if (common_flags()->detect_deadlocks
)
30 DDMutexInit(thr
, pc
, this);
33 void SyncVar::Reset() {
34 CHECK(!ctx
->resetting
);
35 creation_stack_id
= kInvalidStackID
;
36 owner_tid
= kInvalidTid
;
39 atomic_store_relaxed(&flags
, 0);
45 : block_alloc_("heap block allocator"), sync_alloc_("sync allocator") {}
47 void MetaMap::AllocBlock(ThreadState
*thr
, uptr pc
, uptr p
, uptr sz
) {
48 u32 idx
= block_alloc_
.Alloc(&thr
->proc()->block_cache
);
49 MBlock
*b
= block_alloc_
.Map(idx
);
53 b
->stk
= CurrentStackId(thr
, pc
);
54 u32
*meta
= MemToMeta(p
);
56 *meta
= idx
| kFlagBlock
;
59 uptr
MetaMap::FreeBlock(Processor
*proc
, uptr p
, bool reset
) {
60 MBlock
* b
= GetBlock(p
);
63 uptr sz
= RoundUpTo(b
->siz
, kMetaShadowCell
);
64 FreeRange(proc
, p
, sz
, reset
);
68 bool MetaMap::FreeRange(Processor
*proc
, uptr p
, uptr sz
, bool reset
) {
69 bool has_something
= false;
70 u32
*meta
= MemToMeta(p
);
71 u32
*end
= MemToMeta(p
+ sz
);
74 for (; meta
< end
; meta
++) {
77 // Note: don't write to meta in this case -- the block can be huge.
83 if (idx
& kFlagBlock
) {
84 block_alloc_
.Free(&proc
->block_cache
, idx
& ~kFlagMask
);
86 } else if (idx
& kFlagSync
) {
87 DCHECK(idx
& kFlagSync
);
88 SyncVar
*s
= sync_alloc_
.Map(idx
& ~kFlagMask
);
92 sync_alloc_
.Free(&proc
->sync_cache
, idx
& ~kFlagMask
);
102 // ResetRange removes all meta objects from the range.
103 // It is called for large mmap-ed regions. The function is best-effort wrt
104 // freeing of meta objects, because we don't want to page in the whole range
105 // which can be huge. The function probes pages one-by-one until it finds a page
106 // without meta objects, at this point it stops freeing meta objects. Because
107 // thread stacks grow top-down, we do the same starting from end as well.
108 void MetaMap::ResetRange(Processor
*proc
, uptr p
, uptr sz
, bool reset
) {
110 // UnmapOrDie/MmapFixedNoReserve does not work on Windows,
111 // so we do the optimization only for C/C++.
112 FreeRange(proc
, p
, sz
, reset
);
115 const uptr kMetaRatio
= kMetaShadowCell
/ kMetaShadowSize
;
116 const uptr kPageSize
= GetPageSizeCached() * kMetaRatio
;
117 if (sz
<= 4 * kPageSize
) {
118 // If the range is small, just do the normal free procedure.
119 FreeRange(proc
, p
, sz
, reset
);
122 // First, round both ends of the range to page size.
123 uptr diff
= RoundUp(p
, kPageSize
) - p
;
125 FreeRange(proc
, p
, diff
, reset
);
129 diff
= p
+ sz
- RoundDown(p
+ sz
, kPageSize
);
131 FreeRange(proc
, p
+ sz
- diff
, diff
, reset
);
134 // Now we must have a non-empty page-aligned range.
136 CHECK_EQ(p
, RoundUp(p
, kPageSize
));
137 CHECK_EQ(sz
, RoundUp(sz
, kPageSize
));
140 // Probe start of the range.
141 for (uptr checked
= 0; sz
> 0; checked
+= kPageSize
) {
142 bool has_something
= FreeRange(proc
, p
, kPageSize
, reset
);
145 if (!has_something
&& checked
> (128 << 10))
148 // Probe end of the range.
149 for (uptr checked
= 0; sz
> 0; checked
+= kPageSize
) {
150 bool has_something
= FreeRange(proc
, p
+ sz
- kPageSize
, kPageSize
, reset
);
152 // Stacks grow down, so sync object are most likely at the end of the region
153 // (if it is a stack). The very end of the stack is TLS and tsan increases
154 // TLS by at least 256K, so check at least 512K.
155 if (!has_something
&& checked
> (512 << 10))
158 // Finally, page out the whole range (including the parts that we've just
159 // freed). Note: we can't simply madvise, because we need to leave a zeroed
160 // range (otherwise __tsan_java_move can crash if it encounters a left-over
161 // meta objects in java heap).
162 uptr metap
= (uptr
)MemToMeta(p0
);
163 uptr metasz
= sz0
/ kMetaRatio
;
164 UnmapOrDie((void*)metap
, metasz
);
165 if (!MmapFixedSuperNoReserve(metap
, metasz
))
169 void MetaMap::ResetClocks() {
170 // This can be called from the background thread
171 // which does not have proc/cache.
172 // The cache is too large for stack.
173 static InternalAllocatorCache cache
;
174 internal_memset(&cache
, 0, sizeof(cache
));
175 internal_allocator()->InitCache(&cache
);
176 sync_alloc_
.ForEach([&](SyncVar
*s
) {
178 InternalFree(s
->clock
, &cache
);
182 InternalFree(s
->read_clock
, &cache
);
183 s
->read_clock
= nullptr;
185 s
->last_lock
.Reset();
187 internal_allocator()->DestroyCache(&cache
);
190 MBlock
* MetaMap::GetBlock(uptr p
) {
191 u32
*meta
= MemToMeta(p
);
196 if (idx
& kFlagBlock
)
197 return block_alloc_
.Map(idx
& ~kFlagMask
);
198 DCHECK(idx
& kFlagSync
);
199 SyncVar
* s
= sync_alloc_
.Map(idx
& ~kFlagMask
);
204 SyncVar
*MetaMap::GetSync(ThreadState
*thr
, uptr pc
, uptr addr
, bool create
,
206 DCHECK(!create
|| thr
->slot_locked
);
207 u32
*meta
= MemToMeta(addr
);
210 SyncVar
*mys
= nullptr;
212 for (u32 idx
= idx0
; idx
&& !(idx
& kFlagBlock
);) {
213 DCHECK(idx
& kFlagSync
);
214 SyncVar
* s
= sync_alloc_
.Map(idx
& ~kFlagMask
);
215 if (LIKELY(s
->addr
== addr
)) {
216 if (UNLIKELY(myidx
!= 0)) {
218 sync_alloc_
.Free(&thr
->proc()->sync_cache
, myidx
);
226 if (UNLIKELY(*meta
!= idx0
)) {
231 if (LIKELY(myidx
== 0)) {
232 myidx
= sync_alloc_
.Alloc(&thr
->proc()->sync_cache
);
233 mys
= sync_alloc_
.Map(myidx
);
234 mys
->Init(thr
, pc
, addr
, save_stack
);
237 if (atomic_compare_exchange_strong((atomic_uint32_t
*)meta
, &idx0
,
238 myidx
| kFlagSync
, memory_order_release
)) {
244 void MetaMap::MoveMemory(uptr src
, uptr dst
, uptr sz
) {
245 // src and dst can overlap,
246 // there are no concurrent accesses to the regions (e.g. stop-the-world).
249 uptr diff
= dst
- src
;
250 u32
*src_meta
= MemToMeta(src
);
251 u32
*dst_meta
= MemToMeta(dst
);
252 u32
*src_meta_end
= MemToMeta(src
+ sz
);
255 src_meta
= MemToMeta(src
+ sz
) - 1;
256 dst_meta
= MemToMeta(dst
+ sz
) - 1;
257 src_meta_end
= MemToMeta(src
) - 1;
260 for (; src_meta
!= src_meta_end
; src_meta
+= inc
, dst_meta
+= inc
) {
261 CHECK_EQ(*dst_meta
, 0);
265 // Patch the addresses in sync objects.
267 if (idx
& kFlagBlock
)
269 CHECK(idx
& kFlagSync
);
270 SyncVar
*s
= sync_alloc_
.Map(idx
& ~kFlagMask
);
277 void MetaMap::OnProcIdle(Processor
*proc
) {
278 block_alloc_
.FlushCache(&proc
->block_cache
);
279 sync_alloc_
.FlushCache(&proc
->sync_cache
);
282 MetaMap::MemoryStats
MetaMap::GetMemoryStats() const {
284 stats
.mem_block
= block_alloc_
.AllocatedMemory();
285 stats
.sync_obj
= sync_alloc_
.AllocatedMemory();
289 } // namespace __tsan