Skip to content

Commit b332134

Browse files
committed
tsan: new runtime (v3)
This change switches tsan to the new runtime which features: - 2x smaller shadow memory (2x of app memory) - faster fully vectorized race detection - small fixed-size vector clocks (512b) - fast vectorized vector clock operations - unlimited number of alive threads/goroutimes Depends on D112602. Reviewed By: melver Differential Revision: https://reviews.llvm.org/D112603
1 parent 921e89c commit b332134

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2303
-2136
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ class MUTEX ThreadRegistry {
104104
return threads_.empty() ? nullptr : threads_[tid];
105105
}
106106

107+
u32 NumThreadsLocked() const { return threads_.size(); }
108+
107109
u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg);
108110

109111
typedef void (*ThreadCallback)(ThreadContextBase *tctx, void *arg);

compiler-rt/lib/tsan/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ if(COMPILER_RT_TSAN_DEBUG_OUTPUT)
1616
endif()
1717

1818
# Add the actual runtime library.
19-
option(TSAN_USE_OLD_RUNTIME "Use the old tsan runtime (temporal option for emergencies)." OFF)
19+
option(TSAN_USE_OLD_RUNTIME "Use the old tsan runtime (temporal option for emergencies)." ON)
2020
if (TSAN_USE_OLD_RUNTIME)
2121
add_subdirectory(rtl-old)
2222
else()

compiler-rt/lib/tsan/check_analyze.sh

+9-3
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,27 @@ check() {
3434
fi
3535
}
3636

37+
# All hot functions must contain no PUSH/POP
38+
# and no CALLs (everything is tail-called).
3739
for f in write1 write2 write4 write8; do
3840
check $f rsp 1
39-
check $f push 2
41+
check $f push 0
42+
check $f pop 0
43+
check $f call 0
4044
done
4145

4246
for f in read1 read2 read4 read8; do
4347
check $f rsp 1
44-
check $f push 3
48+
check $f push 0
49+
check $f pop 0
50+
check $f call 0
4551
done
4652

4753
for f in func_entry func_exit; do
4854
check $f rsp 0
4955
check $f push 0
5056
check $f pop 0
51-
check $f call 1 # TraceSwitch()
57+
check $f call 0
5258
done
5359

5460
echo LGTM

compiler-rt/lib/tsan/go/build.bat

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type ^
1414
..\rtl\tsan_suppressions.cpp ^
1515
..\rtl\tsan_sync.cpp ^
1616
..\rtl\tsan_stack_trace.cpp ^
17+
..\rtl\tsan_vector_clock.cpp ^
1718
..\..\sanitizer_common\sanitizer_allocator.cpp ^
1819
..\..\sanitizer_common\sanitizer_common.cpp ^
1920
..\..\sanitizer_common\sanitizer_flags.cpp ^

compiler-rt/lib/tsan/go/buildgo.sh

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ SRCS="
1919
../rtl/tsan_stack_trace.cpp
2020
../rtl/tsan_suppressions.cpp
2121
../rtl/tsan_sync.cpp
22+
../rtl/tsan_vector_clock.cpp
2223
../../sanitizer_common/sanitizer_allocator.cpp
2324
../../sanitizer_common/sanitizer_common.cpp
2425
../../sanitizer_common/sanitizer_common_libcdep.cpp

compiler-rt/lib/tsan/go/tsan_go.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ void __tsan_malloc(ThreadState *thr, uptr pc, uptr p, uptr sz) {
214214
}
215215

216216
void __tsan_free(uptr p, uptr sz) {
217-
ctx->metamap.FreeRange(get_cur_proc(), p, sz);
217+
ctx->metamap.FreeRange(get_cur_proc(), p, sz, false);
218218
}
219219

220220
void __tsan_go_start(ThreadState *parent, ThreadState **pthr, void *pc) {

compiler-rt/lib/tsan/rtl/CMakeLists.txt

-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ set(TSAN_HEADERS
102102
tsan_symbolize.h
103103
tsan_sync.h
104104
tsan_trace.h
105-
tsan_update_shadow_word.inc
106105
tsan_vector_clock.h
107106
)
108107

compiler-rt/lib/tsan/rtl/tsan_defs.h

+19-4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ enum class Epoch : u16 {};
6363
constexpr uptr kEpochBits = 14;
6464
constexpr Epoch kEpochZero = static_cast<Epoch>(0);
6565
constexpr Epoch kEpochOver = static_cast<Epoch>(1 << kEpochBits);
66+
constexpr Epoch kEpochLast = static_cast<Epoch>((1 << kEpochBits) - 1);
67+
68+
inline Epoch EpochInc(Epoch epoch) {
69+
return static_cast<Epoch>(static_cast<u16>(epoch) + 1);
70+
}
71+
72+
inline bool EpochOverflow(Epoch epoch) { return epoch == kEpochOver; }
6673

6774
const int kClkBits = 42;
6875
const unsigned kMaxTidReuse = (1 << (64 - kClkBits)) - 1;
@@ -107,7 +114,7 @@ const uptr kShadowCnt = 4;
107114
const uptr kShadowCell = 8;
108115

109116
// Single shadow value.
110-
typedef u64 RawShadow;
117+
enum class RawShadow : u32 {};
111118
const uptr kShadowSize = sizeof(RawShadow);
112119

113120
// Shadow memory is kShadowMultiplier times larger than user memory.
@@ -184,10 +191,13 @@ MD5Hash md5_hash(const void *data, uptr size);
184191
struct Processor;
185192
struct ThreadState;
186193
class ThreadContext;
194+
struct TidSlot;
187195
struct Context;
188196
struct ReportStack;
189197
class ReportDesc;
190198
class RegionAlloc;
199+
struct Trace;
200+
struct TracePart;
191201

192202
typedef uptr AccessType;
193203

@@ -198,6 +208,8 @@ enum : AccessType {
198208
kAccessVptr = 1 << 2, // read or write of an object virtual table pointer
199209
kAccessFree = 1 << 3, // synthetic memory access during memory freeing
200210
kAccessExternalPC = 1 << 4, // access PC can have kExternalPCBit set
211+
kAccessCheckOnly = 1 << 5, // check for races, but don't store
212+
kAccessNoRodata = 1 << 6, // don't check for .rodata marker
201213
};
202214

203215
// Descriptor of user's memory block.
@@ -219,16 +231,19 @@ enum ExternalTag : uptr {
219231
// as 16-bit values, see tsan_defs.h.
220232
};
221233

222-
enum MutexType {
223-
MutexTypeTrace = MutexLastCommon,
224-
MutexTypeReport,
234+
enum {
235+
MutexTypeReport = MutexLastCommon,
225236
MutexTypeSyncVar,
226237
MutexTypeAnnotations,
227238
MutexTypeAtExit,
228239
MutexTypeFired,
229240
MutexTypeRacy,
230241
MutexTypeGlobalProc,
231242
MutexTypeInternalAlloc,
243+
MutexTypeTrace,
244+
MutexTypeSlot,
245+
MutexTypeSlots,
246+
MutexTypeMultiSlot,
232247
};
233248

234249
} // namespace __tsan

compiler-rt/lib/tsan/rtl/tsan_dense_alloc.h

+9
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ class DenseSlabAlloc {
104104
return atomic_load_relaxed(&fillpos_) * kL2Size * sizeof(T);
105105
}
106106

107+
template <typename Func>
108+
void ForEach(Func func) {
109+
SpinMutexLock lock(&mtx_);
110+
uptr fillpos = atomic_load_relaxed(&fillpos_);
111+
for (uptr l1 = 0; l1 < fillpos; l1++) {
112+
for (IndexT l2 = l1 == 0 ? 1 : 0; l2 < kL2Size; l2++) func(&map_[l1][l2]);
113+
}
114+
}
115+
107116
private:
108117
T *map_[kL1Size];
109118
SpinMutex mtx_;

compiler-rt/lib/tsan/rtl/tsan_flags.cpp

-6
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,6 @@ void InitializeFlags(Flags *f, const char *env, const char *env_option_name) {
110110

111111
if (common_flags()->help) parser.PrintFlagDescriptions();
112112

113-
if (f->history_size < 0 || f->history_size > 7) {
114-
Printf("ThreadSanitizer: incorrect value for history_size"
115-
" (must be [0..7])\n");
116-
Die();
117-
}
118-
119113
if (f->io_sync < 0 || f->io_sync > 2) {
120114
Printf("ThreadSanitizer: incorrect value for io_sync"
121115
" (must be [0..2])\n");

compiler-rt/lib/tsan/rtl/tsan_flags.inc

+3-7
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,10 @@ TSAN_FLAG(bool, stop_on_start, false,
5959
"Stops on start until __tsan_resume() is called (for debugging).")
6060
TSAN_FLAG(bool, running_on_valgrind, false,
6161
"Controls whether RunningOnValgrind() returns true or false.")
62-
// There are a lot of goroutines in Go, so we use smaller history.
6362
TSAN_FLAG(
64-
int, history_size, SANITIZER_GO ? 1 : 3,
65-
"Per-thread history size, controls how many previous memory accesses "
66-
"are remembered per thread. Possible values are [0..7]. "
67-
"history_size=0 amounts to 32K memory accesses. Each next value doubles "
68-
"the amount of memory accesses, up to history_size=7 that amounts to "
69-
"4M memory accesses. The default value is 2 (128K memory accesses).")
63+
uptr, history_size, 0,
64+
"Per-thread history size,"
65+
" controls how many extra previous memory accesses are remembered per thread.")
7066
TSAN_FLAG(int, io_sync, 1,
7167
"Controls level of synchronization implied by IO operations. "
7268
"0 - no synchronization "

compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1981,6 +1981,7 @@ static void ReportErrnoSpoiling(ThreadState *thr, uptr pc) {
19811981
static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
19821982
int sig, __sanitizer_siginfo *info,
19831983
void *uctx) {
1984+
CHECK(thr->slot);
19841985
__sanitizer_sigaction *sigactions = interceptor_ctx()->sigactions;
19851986
if (acquire)
19861987
Acquire(thr, 0, (uptr)&sigactions[sig]);
@@ -2268,7 +2269,7 @@ struct dl_iterate_phdr_data {
22682269
};
22692270

22702271
static bool IsAppNotRodata(uptr addr) {
2271-
return IsAppMem(addr) && *MemToShadow(addr) != kShadowRodata;
2272+
return IsAppMem(addr) && *MemToShadow(addr) != Shadow::kRodata;
22722273
}
22732274

22742275
static int dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size,

compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp

+46-41
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,9 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, morder mo) {
235235
T v = NoTsanAtomicLoad(a, mo);
236236
SyncVar *s = ctx->metamap.GetSyncIfExists((uptr)a);
237237
if (s) {
238-
ReadLock l(&s->mtx);
239-
AcquireImpl(thr, pc, &s->clock);
238+
SlotLocker locker(thr);
239+
ReadLock lock(&s->mtx);
240+
thr->clock.Acquire(s->clock);
240241
// Re-read under sync mutex because we need a consistent snapshot
241242
// of the value and the clock we acquire.
242243
v = NoTsanAtomicLoad(a, mo);
@@ -270,33 +271,36 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
270271
NoTsanAtomicStore(a, v, mo);
271272
return;
272273
}
273-
__sync_synchronize();
274-
SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
275-
Lock l(&s->mtx);
276-
thr->fast_state.IncrementEpoch();
277-
// Can't increment epoch w/o writing to the trace as well.
278-
TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
279-
ReleaseStoreImpl(thr, pc, &s->clock);
280-
NoTsanAtomicStore(a, v, mo);
274+
SlotLocker locker(thr);
275+
{
276+
auto s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
277+
Lock lock(&s->mtx);
278+
thr->clock.ReleaseStore(&s->clock);
279+
NoTsanAtomicStore(a, v, mo);
280+
}
281+
IncrementEpoch(thr);
281282
}
282283

283284
template <typename T, T (*F)(volatile T *v, T op)>
284285
static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) {
285286
MemoryAccess(thr, pc, (uptr)a, AccessSize<T>(), kAccessWrite | kAccessAtomic);
286287
if (LIKELY(mo == mo_relaxed))
287288
return F(a, v);
288-
SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
289-
Lock l(&s->mtx);
290-
thr->fast_state.IncrementEpoch();
291-
// Can't increment epoch w/o writing to the trace as well.
292-
TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
293-
if (IsAcqRelOrder(mo))
294-
AcquireReleaseImpl(thr, pc, &s->clock);
295-
else if (IsReleaseOrder(mo))
296-
ReleaseImpl(thr, pc, &s->clock);
297-
else if (IsAcquireOrder(mo))
298-
AcquireImpl(thr, pc, &s->clock);
299-
return F(a, v);
289+
SlotLocker locker(thr);
290+
{
291+
auto s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
292+
RWLock lock(&s->mtx, IsReleaseOrder(mo));
293+
if (IsAcqRelOrder(mo))
294+
thr->clock.ReleaseAcquire(&s->clock);
295+
else if (IsReleaseOrder(mo))
296+
thr->clock.Release(&s->clock);
297+
else if (IsAcquireOrder(mo))
298+
thr->clock.Acquire(s->clock);
299+
v = F(a, v);
300+
}
301+
if (IsReleaseOrder(mo))
302+
IncrementEpoch(thr);
303+
return v;
300304
}
301305

302306
template<typename T>
@@ -416,27 +420,28 @@ static bool AtomicCAS(ThreadState *thr, uptr pc, volatile T *a, T *c, T v,
416420
*c = pr;
417421
return false;
418422
}
419-
423+
SlotLocker locker(thr);
420424
bool release = IsReleaseOrder(mo);
421-
SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
422-
RWLock l(&s->mtx, release);
423-
T cc = *c;
424-
T pr = func_cas(a, cc, v);
425-
bool success = pr == cc;
426-
if (!success) {
427-
*c = pr;
428-
mo = fmo;
425+
bool success;
426+
{
427+
auto s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
428+
RWLock lock(&s->mtx, release);
429+
T cc = *c;
430+
T pr = func_cas(a, cc, v);
431+
success = pr == cc;
432+
if (!success) {
433+
*c = pr;
434+
mo = fmo;
435+
}
436+
if (success && IsAcqRelOrder(mo))
437+
thr->clock.ReleaseAcquire(&s->clock);
438+
else if (success && IsReleaseOrder(mo))
439+
thr->clock.Release(&s->clock);
440+
else if (IsAcquireOrder(mo))
441+
thr->clock.Acquire(s->clock);
429442
}
430-
thr->fast_state.IncrementEpoch();
431-
// Can't increment epoch w/o writing to the trace as well.
432-
TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
433-
434-
if (success && IsAcqRelOrder(mo))
435-
AcquireReleaseImpl(thr, pc, &s->clock);
436-
else if (success && IsReleaseOrder(mo))
437-
ReleaseImpl(thr, pc, &s->clock);
438-
else if (IsAcquireOrder(mo))
439-
AcquireImpl(thr, pc, &s->clock);
443+
if (success && release)
444+
IncrementEpoch(thr);
440445
return success;
441446
}
442447

compiler-rt/lib/tsan/rtl/tsan_interface_java.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ void __tsan_java_free(jptr ptr, jptr size) {
106106
DCHECK_GE(ptr, jctx->heap_begin);
107107
DCHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
108108

109-
ctx->metamap.FreeRange(thr->proc(), ptr, size);
109+
ctx->metamap.FreeRange(thr->proc(), ptr, size, false);
110110
}
111111

112112
void __tsan_java_move(jptr src, jptr dst, jptr size) {
@@ -133,7 +133,7 @@ void __tsan_java_move(jptr src, jptr dst, jptr size) {
133133
// support that anymore as it contains addresses of accesses.
134134
RawShadow *d = MemToShadow(dst);
135135
RawShadow *dend = MemToShadow(dst + size);
136-
internal_memset(d, 0, (dend - d) * sizeof(*d));
136+
ShadowSet(d, dend, Shadow::kEmpty);
137137
}
138138

139139
jptr __tsan_java_find(jptr *from_ptr, jptr to) {

0 commit comments

Comments
 (0)