Skip to content

Commit 76e9612

Browse files
committed
tsan: add fiber support This patch adds functions for managing fibers: __tsan_get_current_fiber() __tsan_create_fiber() __tsan_destroy_fiber() __tsan_switch_to_fiber() __tsan_set_fiber_name() See the added tests for use examples. Author: yuri (Yuri Per) Reviewed in: https://reviews.llvm.org/D54889 [The previous commit of this change was reverted, this is a resubmit with a squashed fix for check_analyze.sh and COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED] llvm-svn: 353947
1 parent f81f7f3 commit 76e9612

14 files changed

+466
-10
lines changed

compiler-rt/include/sanitizer/tsan_interface.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,24 @@ void __tsan_external_assign_tag(void *addr, void *tag);
136136
void __tsan_external_read(void *addr, void *caller_pc, void *tag);
137137
void __tsan_external_write(void *addr, void *caller_pc, void *tag);
138138

139+
// Fiber switching API.
140+
// - TSAN context for fiber can be created by __tsan_create_fiber
141+
// and freed by __tsan_destroy_fiber.
142+
// - TSAN context of current fiber or thread can be obtained
143+
// by calling __tsan_get_current_fiber.
144+
// - __tsan_switch_to_fiber should be called immediatly before switch
145+
// to fiber, such as call of swapcontext.
146+
// - Fiber name can be set by __tsan_set_fiber_name.
147+
void *__tsan_get_current_fiber(void);
148+
void *__tsan_create_fiber(unsigned flags);
149+
void __tsan_destroy_fiber(void *fiber);
150+
void __tsan_switch_to_fiber(void *fiber, unsigned flags);
151+
void __tsan_set_fiber_name(void *fiber, const char *name);
152+
153+
// Flags for __tsan_switch_to_fiber:
154+
// Do not establish a happens-before relation between fibers
155+
const unsigned __tsan_switch_to_fiber_no_sync = 1 << 0;
156+
139157
#ifdef __cplusplus
140158
} // extern "C"
141159
#endif

compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ enum ThreadStatus {
3131
enum class ThreadType {
3232
Regular, // Normal thread
3333
Worker, // macOS Grand Central Dispatch (GCD) worker thread
34+
Fiber, // Fiber
3435
};
3536

3637
// Generic thread context. Specific sanitizer tools may inherit from it.

compiler-rt/lib/tsan/check_analyze.sh

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,12 @@ check() {
3535
}
3636

3737
for f in write1 write2 write4 write8; do
38-
check $f rsp 1
39-
check $f push 1
40-
check $f pop 8
41-
done
42-
43-
for f in read1; do
4438
check $f rsp 1
4539
check $f push 2
4640
check $f pop 16
4741
done
4842

49-
for f in read2 read4 read8; do
43+
for f in read1 read2 read4 read8; do
5044
check $f rsp 1
5145
check $f push 3
5246
check $f pop 24

compiler-rt/lib/tsan/rtl/tsan_interceptors.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ const int SIG_SETMASK = 2;
153153
#endif
154154

155155
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED \
156-
(!cur_thread()->is_inited)
156+
(cur_thread_init(), !cur_thread()->is_inited)
157157

158158
namespace __tsan {
159159
struct SignalDesc {
@@ -554,6 +554,7 @@ static void LongJmp(ThreadState *thr, uptr *env) {
554554

555555
// FIXME: put everything below into a common extern "C" block?
556556
extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) {
557+
cur_thread_init();
557558
SetJmp(cur_thread(), sp, mangled_sp);
558559
}
559560

@@ -942,6 +943,7 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
942943
void *param = p->param;
943944
int tid = 0;
944945
{
946+
cur_thread_init();
945947
ThreadState *thr = cur_thread();
946948
// Thread-local state is not initialized yet.
947949
ScopedIgnoreInterceptors ignore;
@@ -1053,6 +1055,9 @@ TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
10531055
TSAN_INTERCEPTOR(void, pthread_exit, void *retval) {
10541056
{
10551057
SCOPED_INTERCEPTOR_RAW(pthread_exit, retval);
1058+
#if !SANITIZER_MAC && !SANITIZER_ANDROID
1059+
CHECK_EQ(thr, &cur_thread_placeholder);
1060+
#endif
10561061
}
10571062
REAL(pthread_exit)(retval);
10581063
}
@@ -1981,6 +1986,7 @@ static bool is_sync_signal(ThreadSignalContext *sctx, int sig) {
19811986
void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
19821987
__sanitizer_siginfo *info,
19831988
void *ctx) {
1989+
cur_thread_init();
19841990
ThreadState *thr = cur_thread();
19851991
ThreadSignalContext *sctx = SigCtx(thr);
19861992
if (sig < 0 || sig >= kSigCount) {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ LibIgnore *libignore();
2323

2424
#if !SANITIZER_GO
2525
INLINE bool in_symbolizer() {
26+
cur_thread_init();
2627
return UNLIKELY(cur_thread()->in_symbolizer);
2728
}
2829
#endif
2930

3031
} // namespace __tsan
3132

3233
#define SCOPED_INTERCEPTOR_RAW(func, ...) \
34+
cur_thread_init(); \
3335
ThreadState *thr = cur_thread(); \
3436
const uptr caller_pc = GET_CALLER_PC(); \
3537
ScopedInterceptor si(thr, #func, caller_pc); \

compiler-rt/lib/tsan/rtl/tsan_interface.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ typedef u32 uint32_t;
2424
typedef u64 uint64_t;
2525

2626
void __tsan_init() {
27+
cur_thread_init();
2728
Initialize(cur_thread());
2829
}
2930

@@ -123,6 +124,33 @@ void __sanitizer_unaligned_store64(uu64 *addr, u64 v) {
123124
__tsan_unaligned_write8(addr);
124125
*addr = v;
125126
}
127+
128+
#if !SANITIZER_MAC && !SANITIZER_ANDROID
129+
SANITIZER_INTERFACE_ATTRIBUTE
130+
void *__tsan_get_current_fiber() {
131+
return cur_thread();
132+
}
133+
134+
SANITIZER_INTERFACE_ATTRIBUTE
135+
void *__tsan_create_fiber(unsigned flags) {
136+
return FiberCreate(cur_thread(), CALLERPC, flags);
137+
}
138+
139+
SANITIZER_INTERFACE_ATTRIBUTE
140+
void __tsan_destroy_fiber(void *fiber) {
141+
FiberDestroy(cur_thread(), CALLERPC, static_cast<ThreadState *>(fiber));
142+
}
143+
144+
SANITIZER_INTERFACE_ATTRIBUTE
145+
void __tsan_switch_to_fiber(void *fiber, unsigned flags) {
146+
FiberSwitch(cur_thread(), CALLERPC, static_cast<ThreadState *>(fiber), flags);
147+
}
148+
149+
SANITIZER_INTERFACE_ATTRIBUTE
150+
void __tsan_set_fiber_name(void *fiber, const char *name) {
151+
ThreadSetName(static_cast<ThreadState *>(fiber), name);
152+
}
153+
#endif // !SANITIZER_MAC && !SANITIZER_ANDROID
126154
} // extern "C"
127155

128156
void __tsan_acquire(void *addr) {

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,9 @@ struct ThreadState {
384384
// taken by epoch between synchs.
385385
// This way we can save one load from tls.
386386
u64 fast_synch_epoch;
387+
// Technically `current` should be a separate THREADLOCAL variable;
388+
// but it is placed here in order to share cache line with previous fields.
389+
ThreadState* current;
387390
// This is a slow path flag. On fast path, fast_state.GetIgnoreBit() is read.
388391
// We do not distinguish beteween ignoring reads and writes
389392
// for better performance.
@@ -462,11 +465,20 @@ struct ThreadState {
462465
#if SANITIZER_MAC || SANITIZER_ANDROID
463466
ThreadState *cur_thread();
464467
void cur_thread_finalize();
468+
INLINE void cur_thread_init() { }
465469
#else
466470
__attribute__((tls_model("initial-exec")))
467471
extern THREADLOCAL char cur_thread_placeholder[];
468472
INLINE ThreadState *cur_thread() {
469-
return reinterpret_cast<ThreadState *>(&cur_thread_placeholder);
473+
return reinterpret_cast<ThreadState *>(cur_thread_placeholder)->current;
474+
}
475+
INLINE void cur_thread_init() {
476+
ThreadState *thr = reinterpret_cast<ThreadState *>(cur_thread_placeholder);
477+
if (UNLIKELY(!thr->current))
478+
thr->current = thr;
479+
}
480+
INLINE void set_cur_thread(ThreadState *thr) {
481+
reinterpret_cast<ThreadState *>(cur_thread_placeholder)->current = thr;
470482
}
471483
INLINE void cur_thread_finalize() { }
472484
#endif // SANITIZER_MAC || SANITIZER_ANDROID
@@ -868,6 +880,16 @@ uptr ALWAYS_INLINE HeapEnd() {
868880
}
869881
#endif
870882

883+
ThreadState *FiberCreate(ThreadState *thr, uptr pc, unsigned flags);
884+
void FiberDestroy(ThreadState *thr, uptr pc, ThreadState *fiber);
885+
void FiberSwitch(ThreadState *thr, uptr pc, ThreadState *fiber, unsigned flags);
886+
887+
// These need to match __tsan_switch_to_fiber_* flags defined in
888+
// tsan_interface.h. See documentation there as well.
889+
enum FiberSwitchFlags {
890+
FiberSwitchFlagNoSync = 1 << 0, // __tsan_switch_to_fiber_no_sync
891+
};
892+
871893
} // namespace __tsan
872894

873895
#endif // TSAN_RTL_H

compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@ void ThreadStart(ThreadState *thr, int tid, tid_t os_id,
246246
uptr tls_addr = 0;
247247
uptr tls_size = 0;
248248
#if !SANITIZER_GO
249-
GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size);
249+
if (thread_type != ThreadType::Fiber)
250+
GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size);
250251

251252
if (tid) {
252253
if (stk_addr && stk_size)
@@ -404,4 +405,40 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
404405
}
405406
}
406407

408+
#if !SANITIZER_MAC && !SANITIZER_ANDROID && !SANITIZER_GO
409+
void FiberSwitchImpl(ThreadState *from, ThreadState *to) {
410+
Processor *proc = from->proc();
411+
ProcUnwire(proc, from);
412+
ProcWire(proc, to);
413+
set_cur_thread(to);
414+
}
415+
416+
ThreadState *FiberCreate(ThreadState *thr, uptr pc, unsigned flags) {
417+
void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadState));
418+
ThreadState *fiber = static_cast<ThreadState *>(mem);
419+
internal_memset(fiber, 0, sizeof(*fiber));
420+
int tid = ThreadCreate(thr, pc, 0, true);
421+
FiberSwitchImpl(thr, fiber);
422+
ThreadStart(fiber, tid, 0, ThreadType::Fiber);
423+
FiberSwitchImpl(fiber, thr);
424+
return fiber;
425+
}
426+
427+
void FiberDestroy(ThreadState *thr, uptr pc, ThreadState *fiber) {
428+
FiberSwitchImpl(thr, fiber);
429+
ThreadFinish(fiber);
430+
FiberSwitchImpl(fiber, thr);
431+
internal_free(fiber);
432+
}
433+
434+
void FiberSwitch(ThreadState *thr, uptr pc,
435+
ThreadState *fiber, unsigned flags) {
436+
if (!(flags & FiberSwitchFlagNoSync))
437+
Release(thr, pc, (uptr)fiber);
438+
FiberSwitchImpl(thr, fiber);
439+
if (!(flags & FiberSwitchFlagNoSync))
440+
Acquire(fiber, pc, (uptr)fiber);
441+
}
442+
#endif
443+
407444
} // namespace __tsan

compiler-rt/test/tsan/fiber_asm.cc

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
2+
// REQUIRES: x86_64-target-arch
3+
// UNSUPPORTED: darwin
4+
#include "test.h"
5+
6+
struct ucontext {
7+
void *sp;
8+
void *fiber;
9+
};
10+
11+
extern "C" {
12+
void ucontext_do_switch(void **save, void **load);
13+
void ucontext_trampoline();
14+
}
15+
16+
__asm__(".global ucontext_do_switch\n"
17+
"ucontext_do_switch:\n\t"
18+
"pushq %rbp\n\t"
19+
"pushq %r15\n\t"
20+
"pushq %r14\n\t"
21+
"pushq %r13\n\t"
22+
"pushq %r12\n\t"
23+
"pushq %rbx\n\t"
24+
"movq %rsp, (%rdi)\n\t"
25+
"movq (%rsi), %rsp\n\t"
26+
"popq %rbx\n\t"
27+
"popq %r12\n\t"
28+
"popq %r13\n\t"
29+
"popq %r14\n\t"
30+
"popq %r15\n\t"
31+
"popq %rbp\n\t"
32+
"retq");
33+
34+
__asm__(".global ucontext_trampoline\n"
35+
"ucontext_trampoline:\n\t"
36+
".cfi_startproc\n\t"
37+
".cfi_undefined rip\n\t"
38+
"movq %r12, %rdi\n\t"
39+
"jmpq *%rbx\n\t"
40+
".cfi_endproc");
41+
42+
void ucontext_init(ucontext *context, void *stack, unsigned stack_sz,
43+
void (*func)(void*), void *arg) {
44+
void **sp = reinterpret_cast<void **>(static_cast<char *>(stack) + stack_sz);
45+
*(--sp) = 0;
46+
*(--sp) = reinterpret_cast<void *>(ucontext_trampoline);
47+
*(--sp) = 0; // rbp
48+
*(--sp) = 0; // r15
49+
*(--sp) = 0; // r14
50+
*(--sp) = 0; // r13
51+
*(--sp) = arg; // r12
52+
*(--sp) = reinterpret_cast<void *>(func); // rbx
53+
context->sp = sp;
54+
context->fiber = __tsan_create_fiber(0);
55+
}
56+
57+
void ucontext_free(ucontext *context) {
58+
__tsan_destroy_fiber(context->fiber);
59+
}
60+
61+
__attribute__((no_sanitize_thread))
62+
void ucontext_switch(ucontext *save, ucontext *load) {
63+
save->fiber = __tsan_get_current_fiber();
64+
__tsan_switch_to_fiber(load->fiber, 0);
65+
ucontext_do_switch(&save->sp, &load->sp);
66+
}
67+
68+
char stack[64 * 1024] __attribute__((aligned(16)));
69+
70+
ucontext uc, orig_uc;
71+
72+
void func(void *arg) {
73+
__asm__ __volatile__(".cfi_undefined rip");
74+
ucontext_switch(&uc, &orig_uc);
75+
}
76+
77+
int main() {
78+
ucontext_init(&uc, stack, sizeof(stack), func, 0);
79+
ucontext_switch(&orig_uc, &uc);
80+
ucontext_free(&uc);
81+
fprintf(stderr, "PASS\n");
82+
return 0;
83+
}
84+
85+
// CHECK-NOT: WARNING: ThreadSanitizer:
86+
// CHECK: PASS
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
2+
// UNSUPPORTED: darwin
3+
#include "test.h"
4+
#include <ucontext.h>
5+
6+
char stack[64 * 1024] __attribute__((aligned(16)));
7+
8+
ucontext_t uc, orig_uc1, orig_uc2;
9+
void *fiber, *orig_fiber1, *orig_fiber2;
10+
11+
int var;
12+
13+
void *Thread(void *x) {
14+
orig_fiber2 = __tsan_get_current_fiber();
15+
swapcontext(&orig_uc2, &orig_uc1);
16+
return 0;
17+
}
18+
19+
void func() {
20+
pthread_t t;
21+
pthread_create(&t, 0, Thread, 0);
22+
pthread_join(t, 0);
23+
__tsan_switch_to_fiber(orig_fiber1, 0);
24+
swapcontext(&uc, &orig_uc1);
25+
}
26+
27+
int main() {
28+
orig_fiber1 = __tsan_get_current_fiber();
29+
fiber = __tsan_create_fiber(0);
30+
getcontext(&uc);
31+
uc.uc_stack.ss_sp = stack;
32+
uc.uc_stack.ss_size = sizeof(stack);
33+
uc.uc_link = 0;
34+
makecontext(&uc, func, 0);
35+
var = 1;
36+
__tsan_switch_to_fiber(fiber, 0);
37+
swapcontext(&orig_uc1, &uc);
38+
var = 2;
39+
__tsan_switch_to_fiber(orig_fiber2, 0);
40+
swapcontext(&orig_uc1, &orig_uc2);
41+
var = 3;
42+
__tsan_destroy_fiber(fiber);
43+
fprintf(stderr, "PASS\n");
44+
return 0;
45+
}
46+
47+
// CHECK-NOT: WARNING: ThreadSanitizer:
48+
// CHECK: PASS

0 commit comments

Comments
 (0)