From 6d315cbcc336eb4d1c3e05e1456acd90978df6f1 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 18 Dec 2012 06:57:34 +0000 Subject: [PATCH] tsan: describe "file descriptor" location llvm-svn: 170417 --- compiler-rt/lib/tsan/lit_tests/fd_location.cc | 33 ++++ compiler-rt/lib/tsan/rtl/tsan_fd.cc | 155 ++++++++++-------- compiler-rt/lib/tsan/rtl/tsan_fd.h | 1 + compiler-rt/lib/tsan/rtl/tsan_report.cc | 7 +- compiler-rt/lib/tsan/rtl/tsan_report.h | 4 +- compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc | 24 +++ 6 files changed, 158 insertions(+), 66 deletions(-) create mode 100644 compiler-rt/lib/tsan/lit_tests/fd_location.cc diff --git a/compiler-rt/lib/tsan/lit_tests/fd_location.cc b/compiler-rt/lib/tsan/lit_tests/fd_location.cc new file mode 100644 index 000000000000..35f9aabb0377 --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/fd_location.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +int fds[2]; + +void *Thread1(void *x) { + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + close(fds[0]); + close(fds[1]); + return NULL; +} + +int main() { + pipe(fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is file descriptor {{[0-9]+}} created by main thread at: +// CHECK: #0 pipe +// CHECK: #1 main + diff --git a/compiler-rt/lib/tsan/rtl/tsan_fd.cc b/compiler-rt/lib/tsan/rtl/tsan_fd.cc index cb91c4131667..4431097e168c 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_fd.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_fd.cc @@ -21,154 +21,181 @@ const int kTableSizeL1 = 1024; const int kTableSizeL2 = 1024; const int kTableSize = kTableSizeL1 * kTableSizeL2; -struct FdDesc { +struct FdSync { atomic_uint64_t rc; }; +struct FdDesc { + FdSync *sync; + int creation_tid; + u32 creation_stack; +}; + struct FdContext { atomic_uintptr_t tab[kTableSizeL1]; // Addresses used for synchronization. - FdDesc globdesc; - FdDesc filedesc; - FdDesc sockdesc; + FdSync globsync; + FdSync filesync; + FdSync socksync; u64 connectsync; }; static FdContext fdctx; -static FdDesc *allocdesc() { - FdDesc *pd = (FdDesc*)internal_alloc(MBlockFD, sizeof(FdDesc)); - atomic_store(&pd->rc, 1, memory_order_relaxed); - return pd; +static FdSync *allocsync() { + FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync)); + atomic_store(&s->rc, 1, memory_order_relaxed); + return s; } -static FdDesc *ref(FdDesc *pd) { - if (pd && atomic_load(&pd->rc, memory_order_relaxed) != (u64)-1) - atomic_fetch_add(&pd->rc, 1, memory_order_relaxed); - return pd; +static FdSync *ref(FdSync *s) { + if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) + atomic_fetch_add(&s->rc, 1, memory_order_relaxed); + return s; } -static void unref(ThreadState *thr, uptr pc, FdDesc *pd) { - if (pd && atomic_load(&pd->rc, memory_order_relaxed) != (u64)-1) { - if (atomic_fetch_sub(&pd->rc, 1, memory_order_acq_rel) == 1) { - CHECK_NE(pd, &fdctx.globdesc); - CHECK_NE(pd, &fdctx.filedesc); - CHECK_NE(pd, &fdctx.sockdesc); - SyncVar *s = CTX()->synctab.GetAndRemove(thr, pc, (uptr)pd); - if (s) - DestroyAndFree(s); - internal_free(pd); +static void unref(ThreadState *thr, uptr pc, FdSync *s) { + if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) { + if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) { + CHECK_NE(s, &fdctx.globsync); + CHECK_NE(s, &fdctx.filesync); + CHECK_NE(s, &fdctx.socksync); + SyncVar *v = CTX()->synctab.GetAndRemove(thr, pc, (uptr)s); + if (v) + DestroyAndFree(v); + internal_free(s); } } } -static FdDesc **fdaddr(ThreadState *thr, uptr pc, int fd) { +static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) { CHECK_LT(fd, kTableSize); atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2]; uptr l1 = atomic_load(pl1, memory_order_consume); if (l1 == 0) { - uptr size = kTableSizeL2 * sizeof(uptr); + uptr size = kTableSizeL2 * sizeof(FdDesc); void *p = internal_alloc(MBlockFD, size); internal_memset(p, 0, size); - MemoryResetRange(thr, (uptr)&fdaddr, (uptr)p, size); + MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size); if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel)) l1 = (uptr)p; else internal_free(p); } - return &((FdDesc**)l1)[fd % kTableSizeL2]; // NOLINT + return &((FdDesc*)l1)[fd % kTableSizeL2]; // NOLINT } // pd must be already ref'ed. -static void init(ThreadState *thr, uptr pc, int fd, FdDesc *d) { - FdDesc **pd = fdaddr(thr, pc, fd); +static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) { + FdDesc *d = fddesc(thr, pc, fd); // As a matter of fact, we don't intercept all close calls. // See e.g. libc __res_iclose(). - if (*pd) - unref(thr, pc, *pd); - *pd = d; + if (d->sync) + unref(thr, pc, d->sync); + d->sync = s; + d->creation_tid = thr->tid; + d->creation_stack = CurrentStackId(thr, pc); // To catch races between fd usage and open. - MemoryRangeImitateWrite(thr, pc, (uptr)pd, 8); + MemoryRangeImitateWrite(thr, pc, (uptr)d, 8); } void FdInit() { - atomic_store(&fdctx.globdesc.rc, (u64)-1, memory_order_relaxed); - atomic_store(&fdctx.filedesc.rc, (u64)-1, memory_order_relaxed); - atomic_store(&fdctx.sockdesc.rc, (u64)-1, memory_order_relaxed); + atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed); + atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed); + atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed); +} + +bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) { + for (int l1 = 0; l1 < kTableSizeL1; l1++) { + FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed); + if (tab == 0) + break; + if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) { + int l2 = (addr - (uptr)tab) / sizeof(FdDesc); + FdDesc *d = &tab[l2]; + *fd = l1 * kTableSizeL1 + l2; + *tid = d->creation_tid; + *stack = d->creation_stack; + return true; + } + } + return false; } void FdAcquire(ThreadState *thr, uptr pc, int fd) { - FdDesc **pd = fdaddr(thr, pc, fd); - FdDesc *d = *pd; - DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, d); - MemoryRead8Byte(thr, pc, (uptr)pd); - if (d) - Acquire(thr, pc, (uptr)d); + FdDesc *d = fddesc(thr, pc, fd); + FdSync *s = d->sync; + DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s); + MemoryRead8Byte(thr, pc, (uptr)d); + if (s) + Acquire(thr, pc, (uptr)s); } void FdRelease(ThreadState *thr, uptr pc, int fd) { - FdDesc **pd = fdaddr(thr, pc, fd); - FdDesc *d = *pd; - DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, d); - if (d) - Release(thr, pc, (uptr)d); - MemoryRead8Byte(thr, pc, (uptr)pd); + FdDesc *d = fddesc(thr, pc, fd); + FdSync *s = d->sync; + DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); + if (s) + Release(thr, pc, (uptr)s); + MemoryRead8Byte(thr, pc, (uptr)d); } void FdClose(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); - FdDesc **pd = fdaddr(thr, pc, fd); + FdDesc *d = fddesc(thr, pc, fd); // To catch races between fd usage and close. - MemoryWrite8Byte(thr, pc, (uptr)pd); + MemoryWrite8Byte(thr, pc, (uptr)d); // We need to clear it, because if we do not intercept any call out there // that creates fd, we will hit false postives. - MemoryResetRange(thr, pc, (uptr)pd, 8); - unref(thr, pc, *pd); - *pd = 0; + MemoryResetRange(thr, pc, (uptr)d, 8); + unref(thr, pc, d->sync); + d->sync = 0; + d->creation_tid = 0; + d->creation_stack = 0; } void FdFileCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd); - init(thr, pc, fd, &fdctx.filedesc); + init(thr, pc, fd, &fdctx.filesync); } void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) { DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); // Ignore the case when user dups not yet connected socket. - FdDesc **opd = fdaddr(thr, pc, oldfd); - MemoryRead8Byte(thr, pc, (uptr)opd); + FdDesc *od = fddesc(thr, pc, oldfd); + MemoryRead8Byte(thr, pc, (uptr)od); FdClose(thr, pc, newfd); - init(thr, pc, newfd, ref(*opd)); + init(thr, pc, newfd, ref(od->sync)); } void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) { DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd); - FdDesc *d = allocdesc(); - init(thr, pc, rfd, d); - init(thr, pc, wfd, ref(d)); + FdSync *s = allocsync(); + init(thr, pc, rfd, s); + init(thr, pc, wfd, ref(s)); } void FdEventCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd); - init(thr, pc, fd, allocdesc()); + init(thr, pc, fd, allocsync()); } void FdPollCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd); - init(thr, pc, fd, allocdesc()); + init(thr, pc, fd, allocsync()); } void FdSocketCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd); // It can be a UDP socket. - init(thr, pc, fd, &fdctx.sockdesc); + init(thr, pc, fd, &fdctx.socksync); } void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd); // Synchronize connect->accept. Acquire(thr, pc, (uptr)&fdctx.connectsync); - init(thr, pc, newfd, &fdctx.sockdesc); + init(thr, pc, newfd, &fdctx.socksync); } void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) { @@ -179,7 +206,7 @@ void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) { void FdSocketConnect(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd); - init(thr, pc, fd, &fdctx.sockdesc); + init(thr, pc, fd, &fdctx.socksync); } uptr File2addr(char *path) { diff --git a/compiler-rt/lib/tsan/rtl/tsan_fd.h b/compiler-rt/lib/tsan/rtl/tsan_fd.h index 6f509cd2e901..9947f1486276 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_fd.h +++ b/compiler-rt/lib/tsan/rtl/tsan_fd.h @@ -51,6 +51,7 @@ void FdSocketCreate(ThreadState *thr, uptr pc, int fd); void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd); void FdSocketConnecting(ThreadState *thr, uptr pc, int fd); void FdSocketConnect(ThreadState *thr, uptr pc, int fd); +bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack); uptr File2addr(char *path); uptr Dir2addr(char *path); diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.cc b/compiler-rt/lib/tsan/rtl/tsan_report.cc index d70a1424c225..b79ab9125542 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_report.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_report.cc @@ -102,6 +102,7 @@ static void PrintMop(const ReportMop *mop, bool first) { } static void PrintLocation(const ReportLocation *loc) { + char thrbuf[kThreadBufSize]; if (loc->type == ReportLocationGlobal) { Printf(" Location is global '%s' of size %zu at %zx %s:%d (%s+%p)\n\n", loc->name, loc->size, loc->addr, loc->file, loc->line, @@ -112,7 +113,11 @@ static void PrintLocation(const ReportLocation *loc) { loc->size, loc->addr, thread_name(thrbuf, loc->tid)); PrintStack(loc->stack); } else if (loc->type == ReportLocationStack) { - Printf(" Location is stack of thread T%d:\n\n", loc->tid); + Printf(" Location is stack of %s\n\n", thread_name(thrbuf, loc->tid)); + } else if (loc->type == ReportLocationFD) { + Printf(" Location is file descriptor %d created by %s at:\n", + loc->fd, thread_name(thrbuf, loc->tid)); + PrintStack(loc->stack); } } diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.h b/compiler-rt/lib/tsan/rtl/tsan_report.h index 67545b39ac86..34223b34f760 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_report.h +++ b/compiler-rt/lib/tsan/rtl/tsan_report.h @@ -57,7 +57,8 @@ struct ReportMop { enum ReportLocationType { ReportLocationGlobal, ReportLocationHeap, - ReportLocationStack + ReportLocationStack, + ReportLocationFD }; struct ReportLocation { @@ -67,6 +68,7 @@ struct ReportLocation { char *module; uptr offset; int tid; + int fd; char *name; char *file; int line; diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc index b1556d892fb5..23e4ae5bf392 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc @@ -23,6 +23,7 @@ #include "tsan_sync.h" #include "tsan_mman.h" #include "tsan_flags.h" +#include "tsan_fd.h" namespace __tsan { @@ -227,6 +228,29 @@ void ScopedReport::AddLocation(uptr addr, uptr size) { if (addr == 0) return; #ifndef TSAN_GO + int fd = -1; + int creat_tid = -1; + u32 creat_stack = 0; + if (FdLocation(addr, &fd, &creat_tid, &creat_stack) + || FdLocation(AlternativeAddress(addr), &fd, &creat_tid, &creat_stack)) { + void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation)); + ReportLocation *loc = new(mem) ReportLocation(); + rep_->locs.PushBack(loc); + loc->type = ReportLocationFD; + loc->fd = fd; + loc->tid = creat_tid; + uptr ssz = 0; + const uptr *stack = StackDepotGet(creat_stack, &ssz); + if (stack) { + StackTrace trace; + trace.Init(stack, ssz); + loc->stack = SymbolizeStack(trace); + } + ThreadContext *tctx = FindThread(creat_tid); + if (tctx) + AddThread(tctx); + return; + } if (allocator()->PointerIsMine((void*)addr)) { MBlock *b = user_mblock(0, (void*)addr); ThreadContext *tctx = FindThread(b->alloc_tid);