Skip to content

Commit 94ea366

Browse files
committed
tsan: fix trace tests on darwin
The trace tests crashed on darwin because of some thread initialization issues (thread initialization is somewhat different on darwin). Instead of starting real threads, create a new ThreadState in the main thread. This makes the tests more unit-testy and hopefully won't crash on darwin (there is almost no platform-specific code involved now). This will also help with future trace tests that will need more than 1 thread. Creating more than 1 real thread and dispatching test actions across multiple threads in the required deterministic order is painful. Depends on D110539. Reviewed By: melver Differential Revision: https://reviews.llvm.org/D110546
1 parent b72176b commit 94ea366

File tree

2 files changed

+165
-177
lines changed

2 files changed

+165
-177
lines changed

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -456,14 +456,12 @@ static void InitializeLongjmpXorKey() {
456456
extern "C" void __tsan_tls_initialization() {}
457457

458458
void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
459-
// Check that the thr object is in tls;
460459
const uptr thr_beg = (uptr)thr;
461460
const uptr thr_end = (uptr)thr + sizeof(*thr);
462-
CHECK_GE(thr_beg, tls_addr);
463-
CHECK_LE(thr_beg, tls_addr + tls_size);
464-
CHECK_GE(thr_end, tls_addr);
465-
CHECK_LE(thr_end, tls_addr + tls_size);
466-
// Since the thr object is huge, skip it.
461+
// ThreadState is normally allocated in TLS and is large,
462+
// so we skip it. But unit tests allocate ThreadState outside of TLS.
463+
if (thr_beg < tls_addr || thr_end >= tls_addr + tls_size)
464+
return;
467465
const uptr pc = StackTrace::GetNextInstructionPc(
468466
reinterpret_cast<uptr>(__tsan_tls_initialization));
469467
MemoryRangeImitateWrite(thr, pc, tls_addr, thr_beg - tls_addr);

compiler-rt/lib/tsan/tests/unit/tsan_trace_test.cpp

Lines changed: 161 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -22,102 +22,115 @@ using namespace v3;
2222

2323
// We need to run all trace tests in a new thread,
2424
// so that the thread trace is empty initially.
25-
static void run_in_thread(void *(*f)(void *), void *arg = nullptr) {
26-
pthread_t th;
27-
pthread_create(&th, nullptr, f, arg);
28-
pthread_join(th, nullptr);
29-
}
30-
31-
#if SANITIZER_MAC
32-
// These tests are currently failing on Mac.
33-
// See https://reviews.llvm.org/D107911 for more details.
34-
# define MAYBE_RestoreAccess DISABLED_RestoreAccess
35-
# define MAYBE_MemoryAccessSize DISABLED_MemoryAccessSize
36-
# define MAYBE_RestoreMutexLock DISABLED_RestoreMutexLock
37-
# define MAYBE_MultiPart DISABLED_MultiPart
38-
#else
39-
# define MAYBE_RestoreAccess RestoreAccess
40-
# define MAYBE_MemoryAccessSize MemoryAccessSize
41-
# define MAYBE_RestoreMutexLock RestoreMutexLock
42-
# define MAYBE_MultiPart MultiPart
43-
#endif
25+
template <uptr N>
26+
struct ThreadArray {
27+
ThreadArray() {
28+
for (auto *&thr : threads) {
29+
thr = static_cast<ThreadState *>(
30+
MmapOrDie(sizeof(ThreadState), "ThreadState"));
31+
Tid tid = ThreadCreate(cur_thread(), 0, 0, true);
32+
Processor *proc = ProcCreate();
33+
ProcWire(proc, thr);
34+
ThreadStart(thr, tid, 0, ThreadType::Regular);
35+
}
36+
}
4437

45-
TEST(Trace, MAYBE_RestoreAccess) {
46-
struct Thread {
47-
static void *Func(void *arg) {
48-
// A basic test with some function entry/exit events,
49-
// some mutex lock/unlock events and some other distracting
50-
// memory events.
51-
ThreadState *thr = cur_thread();
52-
TraceFunc(thr, 0x1000);
53-
TraceFunc(thr, 0x1001);
54-
TraceMutexLock(thr, v3::EventType::kLock, 0x4000, 0x5000, 0x6000);
55-
TraceMutexLock(thr, v3::EventType::kLock, 0x4001, 0x5001, 0x6001);
56-
TraceMutexUnlock(thr, 0x5000);
57-
TraceFunc(thr);
58-
CHECK(TryTraceMemoryAccess(thr, 0x2001, 0x3001, 8, kAccessRead));
59-
TraceMutexLock(thr, v3::EventType::kRLock, 0x4002, 0x5002, 0x6002);
60-
TraceFunc(thr, 0x1002);
61-
CHECK(TryTraceMemoryAccess(thr, 0x2000, 0x3000, 8, kAccessRead));
62-
// This is the access we want to find.
63-
// The previous one is equivalent, but RestoreStack must prefer
64-
// the last of the matchig accesses.
65-
CHECK(TryTraceMemoryAccess(thr, 0x2002, 0x3000, 8, kAccessRead));
66-
Lock lock1(&ctx->slot_mtx);
67-
ThreadRegistryLock lock2(&ctx->thread_registry);
68-
VarSizeStackTrace stk;
69-
MutexSet mset;
70-
uptr tag = kExternalTagNone;
71-
bool res =
72-
RestoreStack(thr->tid, v3::EventType::kAccessExt, thr->sid,
73-
thr->epoch, 0x3000, 8, kAccessRead, &stk, &mset, &tag);
74-
CHECK(res);
75-
CHECK_EQ(stk.size, 3);
76-
CHECK_EQ(stk.trace[0], 0x1000);
77-
CHECK_EQ(stk.trace[1], 0x1002);
78-
CHECK_EQ(stk.trace[2], 0x2002);
79-
CHECK_EQ(mset.Size(), 2);
80-
CHECK_EQ(mset.Get(0).addr, 0x5001);
81-
CHECK_EQ(mset.Get(0).stack_id, 0x6001);
82-
CHECK_EQ(mset.Get(0).write, true);
83-
CHECK_EQ(mset.Get(1).addr, 0x5002);
84-
CHECK_EQ(mset.Get(1).stack_id, 0x6002);
85-
CHECK_EQ(mset.Get(1).write, false);
86-
CHECK_EQ(tag, kExternalTagNone);
87-
return nullptr;
38+
~ThreadArray() {
39+
for (uptr i = 0; i < N; i++) {
40+
if (threads[i])
41+
Finish(i);
8842
}
89-
};
90-
run_in_thread(Thread::Func);
43+
}
44+
45+
void Finish(uptr i) {
46+
auto *thr = threads[i];
47+
threads[i] = nullptr;
48+
Processor *proc = thr->proc();
49+
ThreadFinish(thr);
50+
ProcUnwire(proc, thr);
51+
ProcDestroy(proc);
52+
UnmapOrDie(thr, sizeof(ThreadState));
53+
}
54+
55+
ThreadState *threads[N];
56+
ThreadState *operator[](uptr i) { return threads[i]; }
57+
ThreadState *operator->() { return threads[0]; }
58+
operator ThreadState *() { return threads[0]; }
59+
};
60+
61+
TEST(Trace, RestoreAccess) {
62+
// A basic test with some function entry/exit events,
63+
// some mutex lock/unlock events and some other distracting
64+
// memory events.
65+
ThreadArray<1> thr;
66+
TraceFunc(thr, 0x1000);
67+
TraceFunc(thr, 0x1001);
68+
TraceMutexLock(thr, v3::EventType::kLock, 0x4000, 0x5000, 0x6000);
69+
TraceMutexLock(thr, v3::EventType::kLock, 0x4001, 0x5001, 0x6001);
70+
TraceMutexUnlock(thr, 0x5000);
71+
TraceFunc(thr);
72+
CHECK(TryTraceMemoryAccess(thr, 0x2001, 0x3001, 8, kAccessRead));
73+
TraceMutexLock(thr, v3::EventType::kRLock, 0x4002, 0x5002, 0x6002);
74+
TraceFunc(thr, 0x1002);
75+
CHECK(TryTraceMemoryAccess(thr, 0x2000, 0x3000, 8, kAccessRead));
76+
// This is the access we want to find.
77+
// The previous one is equivalent, but RestoreStack must prefer
78+
// the last of the matchig accesses.
79+
CHECK(TryTraceMemoryAccess(thr, 0x2002, 0x3000, 8, kAccessRead));
80+
Lock lock1(&ctx->slot_mtx);
81+
ThreadRegistryLock lock2(&ctx->thread_registry);
82+
VarSizeStackTrace stk;
83+
MutexSet mset;
84+
uptr tag = kExternalTagNone;
85+
bool res =
86+
RestoreStack(thr->tid, v3::EventType::kAccessExt, thr->sid, thr->epoch,
87+
0x3000, 8, kAccessRead, &stk, &mset, &tag);
88+
CHECK(res);
89+
CHECK_EQ(stk.size, 3);
90+
CHECK_EQ(stk.trace[0], 0x1000);
91+
CHECK_EQ(stk.trace[1], 0x1002);
92+
CHECK_EQ(stk.trace[2], 0x2002);
93+
CHECK_EQ(mset.Size(), 2);
94+
CHECK_EQ(mset.Get(0).addr, 0x5001);
95+
CHECK_EQ(mset.Get(0).stack_id, 0x6001);
96+
CHECK_EQ(mset.Get(0).write, true);
97+
CHECK_EQ(mset.Get(1).addr, 0x5002);
98+
CHECK_EQ(mset.Get(1).stack_id, 0x6002);
99+
CHECK_EQ(mset.Get(1).write, false);
100+
CHECK_EQ(tag, kExternalTagNone);
91101
}
92102

93-
TEST(Trace, MAYBE_MemoryAccessSize) {
94-
struct Thread {
95-
struct Params {
96-
uptr access_size, offset, size;
97-
bool res;
98-
int type;
99-
};
100-
static void *Func(void *arg) {
101-
// Test tracing and matching of accesses of different sizes.
102-
const Params *params = static_cast<Params *>(arg);
103+
TEST(Trace, MemoryAccessSize) {
104+
// Test tracing and matching of accesses of different sizes.
105+
struct Params {
106+
uptr access_size, offset, size;
107+
bool res;
108+
};
109+
Params tests[] = {
110+
{1, 0, 1, true}, {4, 0, 2, true},
111+
{4, 2, 2, true}, {8, 3, 1, true},
112+
{2, 1, 1, true}, {1, 1, 1, false},
113+
{8, 5, 4, false}, {4, static_cast<uptr>(-1l), 4, false},
114+
};
115+
for (auto params : tests) {
116+
for (int type = 0; type < 3; type++) {
117+
ThreadArray<1> thr;
103118
Printf("access_size=%zu, offset=%zu, size=%zu, res=%d, type=%d\n",
104-
params->access_size, params->offset, params->size, params->res,
105-
params->type);
106-
ThreadState *thr = cur_thread();
119+
params.access_size, params.offset, params.size, params.res, type);
107120
TraceFunc(thr, 0x1000);
108-
switch (params->type) {
121+
switch (type) {
109122
case 0:
110123
// This should emit compressed event.
111-
CHECK(TryTraceMemoryAccess(thr, 0x2000, 0x3000, params->access_size,
124+
CHECK(TryTraceMemoryAccess(thr, 0x2000, 0x3000, params.access_size,
112125
kAccessRead));
113126
break;
114127
case 1:
115128
// This should emit full event.
116-
CHECK(TryTraceMemoryAccess(thr, 0x2000000, 0x3000,
117-
params->access_size, kAccessRead));
129+
CHECK(TryTraceMemoryAccess(thr, 0x2000000, 0x3000, params.access_size,
130+
kAccessRead));
118131
break;
119132
case 2:
120-
TraceMemoryAccessRange(thr, 0x2000000, 0x3000, params->access_size,
133+
TraceMemoryAccessRange(thr, 0x2000000, 0x3000, params.access_size,
121134
kAccessRead);
122135
break;
123136
}
@@ -127,105 +140,82 @@ TEST(Trace, MAYBE_MemoryAccessSize) {
127140
MutexSet mset;
128141
uptr tag = kExternalTagNone;
129142
bool res = RestoreStack(thr->tid, v3::EventType::kAccessExt, thr->sid,
130-
thr->epoch, 0x3000 + params->offset, params->size,
143+
thr->epoch, 0x3000 + params.offset, params.size,
131144
kAccessRead, &stk, &mset, &tag);
132-
CHECK_EQ(res, params->res);
133-
if (params->res) {
145+
CHECK_EQ(res, params.res);
146+
if (params.res) {
134147
CHECK_EQ(stk.size, 2);
135148
CHECK_EQ(stk.trace[0], 0x1000);
136-
CHECK_EQ(stk.trace[1], params->type ? 0x2000000 : 0x2000);
149+
CHECK_EQ(stk.trace[1], type ? 0x2000000 : 0x2000);
137150
}
138-
return nullptr;
139151
}
140-
};
141-
Thread::Params tests[] = {
142-
{1, 0, 1, true, 0}, {4, 0, 2, true, 0},
143-
{4, 2, 2, true, 0}, {8, 3, 1, true, 0},
144-
{2, 1, 1, true, 0}, {1, 1, 1, false, 0},
145-
{8, 5, 4, false, 0}, {4, static_cast<uptr>(-1l), 4, false, 0},
146-
};
147-
for (auto params : tests) {
148-
for (params.type = 0; params.type < 3; params.type++)
149-
run_in_thread(Thread::Func, &params);
150152
}
151153
}
152154

153-
TEST(Trace, MAYBE_RestoreMutexLock) {
154-
struct Thread {
155-
static void *Func(void *arg) {
156-
// Check of restoration of a mutex lock event.
157-
ThreadState *thr = cur_thread();
158-
TraceFunc(thr, 0x1000);
159-
TraceMutexLock(thr, v3::EventType::kLock, 0x4000, 0x5000, 0x6000);
160-
TraceMutexLock(thr, v3::EventType::kRLock, 0x4001, 0x5001, 0x6001);
161-
TraceMutexLock(thr, v3::EventType::kRLock, 0x4002, 0x5001, 0x6002);
162-
Lock lock1(&ctx->slot_mtx);
163-
ThreadRegistryLock lock2(&ctx->thread_registry);
164-
VarSizeStackTrace stk;
165-
MutexSet mset;
166-
uptr tag = kExternalTagNone;
167-
bool res = RestoreStack(thr->tid, v3::EventType::kLock, thr->sid,
168-
thr->epoch, 0x5001, 0, 0, &stk, &mset, &tag);
169-
CHECK(res);
170-
CHECK_EQ(stk.size, 2);
171-
CHECK_EQ(stk.trace[0], 0x1000);
172-
CHECK_EQ(stk.trace[1], 0x4002);
173-
CHECK_EQ(mset.Size(), 2);
174-
CHECK_EQ(mset.Get(0).addr, 0x5000);
175-
CHECK_EQ(mset.Get(0).stack_id, 0x6000);
176-
CHECK_EQ(mset.Get(0).write, true);
177-
CHECK_EQ(mset.Get(1).addr, 0x5001);
178-
CHECK_EQ(mset.Get(1).stack_id, 0x6001);
179-
CHECK_EQ(mset.Get(1).write, false);
180-
return nullptr;
181-
}
182-
};
183-
run_in_thread(Thread::Func);
155+
TEST(Trace, RestoreMutexLock) {
156+
// Check of restoration of a mutex lock event.
157+
ThreadArray<1> thr;
158+
TraceFunc(thr, 0x1000);
159+
TraceMutexLock(thr, v3::EventType::kLock, 0x4000, 0x5000, 0x6000);
160+
TraceMutexLock(thr, v3::EventType::kRLock, 0x4001, 0x5001, 0x6001);
161+
TraceMutexLock(thr, v3::EventType::kRLock, 0x4002, 0x5001, 0x6002);
162+
Lock lock1(&ctx->slot_mtx);
163+
ThreadRegistryLock lock2(&ctx->thread_registry);
164+
VarSizeStackTrace stk;
165+
MutexSet mset;
166+
uptr tag = kExternalTagNone;
167+
bool res = RestoreStack(thr->tid, v3::EventType::kLock, thr->sid, thr->epoch,
168+
0x5001, 0, 0, &stk, &mset, &tag);
169+
CHECK(res);
170+
CHECK_EQ(stk.size, 2);
171+
CHECK_EQ(stk.trace[0], 0x1000);
172+
CHECK_EQ(stk.trace[1], 0x4002);
173+
CHECK_EQ(mset.Size(), 2);
174+
CHECK_EQ(mset.Get(0).addr, 0x5000);
175+
CHECK_EQ(mset.Get(0).stack_id, 0x6000);
176+
CHECK_EQ(mset.Get(0).write, true);
177+
CHECK_EQ(mset.Get(1).addr, 0x5001);
178+
CHECK_EQ(mset.Get(1).stack_id, 0x6001);
179+
CHECK_EQ(mset.Get(1).write, false);
184180
}
185181

186-
TEST(Trace, MAYBE_MultiPart) {
187-
struct Thread {
188-
static void *Func(void *arg) {
189-
// Check replay of a trace with multiple parts.
190-
ThreadState *thr = cur_thread();
191-
TraceFunc(thr, 0x1000);
192-
TraceFunc(thr, 0x2000);
193-
TraceMutexLock(thr, v3::EventType::kLock, 0x4000, 0x5000, 0x6000);
194-
const uptr kEvents = 3 * sizeof(TracePart) / sizeof(v3::Event);
195-
for (uptr i = 0; i < kEvents; i++) {
196-
TraceFunc(thr, 0x3000);
197-
TraceMutexLock(thr, v3::EventType::kLock, 0x4002, 0x5002, 0x6002);
198-
TraceMutexUnlock(thr, 0x5002);
199-
TraceFunc(thr);
200-
}
201-
TraceFunc(thr, 0x4000);
202-
TraceMutexLock(thr, v3::EventType::kRLock, 0x4001, 0x5001, 0x6001);
203-
CHECK(TryTraceMemoryAccess(thr, 0x2002, 0x3000, 8, kAccessRead));
204-
Lock lock1(&ctx->slot_mtx);
205-
ThreadRegistryLock lock2(&ctx->thread_registry);
206-
VarSizeStackTrace stk;
207-
MutexSet mset;
208-
uptr tag = kExternalTagNone;
209-
bool res =
210-
RestoreStack(thr->tid, v3::EventType::kAccessExt, thr->sid,
211-
thr->epoch, 0x3000, 8, kAccessRead, &stk, &mset, &tag);
212-
CHECK(res);
213-
CHECK_EQ(stk.size, 4);
214-
CHECK_EQ(stk.trace[0], 0x1000);
215-
CHECK_EQ(stk.trace[1], 0x2000);
216-
CHECK_EQ(stk.trace[2], 0x4000);
217-
CHECK_EQ(stk.trace[3], 0x2002);
218-
CHECK_EQ(mset.Size(), 2);
219-
CHECK_EQ(mset.Get(0).addr, 0x5000);
220-
CHECK_EQ(mset.Get(0).stack_id, 0x6000);
221-
CHECK_EQ(mset.Get(0).write, true);
222-
CHECK_EQ(mset.Get(1).addr, 0x5001);
223-
CHECK_EQ(mset.Get(1).stack_id, 0x6001);
224-
CHECK_EQ(mset.Get(1).write, false);
225-
return nullptr;
226-
}
227-
};
228-
run_in_thread(Thread::Func);
182+
TEST(Trace, MultiPart) {
183+
// Check replay of a trace with multiple parts.
184+
ThreadArray<1> thr;
185+
TraceFunc(thr, 0x1000);
186+
TraceFunc(thr, 0x2000);
187+
TraceMutexLock(thr, v3::EventType::kLock, 0x4000, 0x5000, 0x6000);
188+
const uptr kEvents = 3 * sizeof(TracePart) / sizeof(v3::Event);
189+
for (uptr i = 0; i < kEvents; i++) {
190+
TraceFunc(thr, 0x3000);
191+
TraceMutexLock(thr, v3::EventType::kLock, 0x4002, 0x5002, 0x6002);
192+
TraceMutexUnlock(thr, 0x5002);
193+
TraceFunc(thr);
194+
}
195+
TraceFunc(thr, 0x4000);
196+
TraceMutexLock(thr, v3::EventType::kRLock, 0x4001, 0x5001, 0x6001);
197+
CHECK(TryTraceMemoryAccess(thr, 0x2002, 0x3000, 8, kAccessRead));
198+
Lock lock1(&ctx->slot_mtx);
199+
ThreadRegistryLock lock2(&ctx->thread_registry);
200+
VarSizeStackTrace stk;
201+
MutexSet mset;
202+
uptr tag = kExternalTagNone;
203+
bool res =
204+
RestoreStack(thr->tid, v3::EventType::kAccessExt, thr->sid, thr->epoch,
205+
0x3000, 8, kAccessRead, &stk, &mset, &tag);
206+
CHECK(res);
207+
CHECK_EQ(stk.size, 4);
208+
CHECK_EQ(stk.trace[0], 0x1000);
209+
CHECK_EQ(stk.trace[1], 0x2000);
210+
CHECK_EQ(stk.trace[2], 0x4000);
211+
CHECK_EQ(stk.trace[3], 0x2002);
212+
CHECK_EQ(mset.Size(), 2);
213+
CHECK_EQ(mset.Get(0).addr, 0x5000);
214+
CHECK_EQ(mset.Get(0).stack_id, 0x6000);
215+
CHECK_EQ(mset.Get(0).write, true);
216+
CHECK_EQ(mset.Get(1).addr, 0x5001);
217+
CHECK_EQ(mset.Get(1).stack_id, 0x6001);
218+
CHECK_EQ(mset.Get(1).write, false);
229219
}
230220

231221
} // namespace __tsan

0 commit comments

Comments
 (0)