tsan: describe "file descriptor" location
authorDmitry Vyukov <dvyukov@google.com>
Tue, 18 Dec 2012 06:57:34 +0000 (06:57 +0000)
committerDmitry Vyukov <dvyukov@google.com>
Tue, 18 Dec 2012 06:57:34 +0000 (06:57 +0000)
llvm-svn: 170417

compiler-rt/lib/tsan/lit_tests/fd_location.cc [new file with mode: 0644]
compiler-rt/lib/tsan/rtl/tsan_fd.cc
compiler-rt/lib/tsan/rtl/tsan_fd.h
compiler-rt/lib/tsan/rtl/tsan_report.cc
compiler-rt/lib/tsan/rtl/tsan_report.h
compiler-rt/lib/tsan/rtl/tsan_rtl_report.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 (file)
index 0000000..35f9aab
--- /dev/null
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+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
+
index cb91c41..4431097 100644 (file)
@@ -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) {
index 6f509cd..9947f14 100644 (file)
@@ -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);
index d70a142..b79ab91 100644 (file)
@@ -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);
   }
 }
 
index 67545b3..34223b3 100644 (file)
@@ -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;
index b1556d8..23e4ae5 100644 (file)
@@ -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);