tsan: implement new version of standalong deadlock detector
intercept pthread_cond (it is required to properly track state of mutexes)
detect cycles in mutex graph

llvm-svn: 202975
diff --git a/compiler-rt/lib/tsan/dd/dd_rtl.cc b/compiler-rt/lib/tsan/dd/dd_rtl.cc
index 0845654..840a25a 100644
--- a/compiler-rt/lib/tsan/dd/dd_rtl.cc
+++ b/compiler-rt/lib/tsan/dd/dd_rtl.cc
@@ -18,32 +18,11 @@
 
 static Context *ctx;
 
-void Initialize() {
-  static u64 ctx_mem[sizeof(Context) / sizeof(u64) + 1];
-  ctx = new(ctx_mem) Context();
-
-  InitializeInterceptors();
-  //common_flags()->allow_addr2line = true;
-  common_flags()->symbolize = true;
-  ctx->dd = DDetector::Create();
-}
-
-void ThreadInit(Thread *thr) {
-  thr->dd_pt = ctx->dd->CreatePhysicalThread();
-  thr->dd_lt = ctx->dd->CreateLogicalThread(0);
-}
-
-void ThreadDestroy(Thread *thr) {
-  ctx->dd->DestroyPhysicalThread(thr->dd_pt);
-  ctx->dd->DestroyLogicalThread(thr->dd_lt);
-}
-
-static u32 CurrentStackTrace(Thread *thr) {
+static u32 CurrentStackTrace(Thread *thr, uptr skip) {
   StackTrace trace;
   thr->ignore_interceptors = true;
   trace.Unwind(1000, 0, 0, 0, 0, 0, false);
   thr->ignore_interceptors = false;
-  const uptr skip = 4;
   if (trace.size <= skip)
     return 0;
   return StackDepotPut(trace.trace + skip, trace.size - skip);
@@ -58,45 +37,96 @@
 }
 
 static void ReportDeadlock(Thread *thr, DDReport *rep) {
+  if (rep == 0)
+    return;
   Printf("==============================\n");
-  Printf("DEADLOCK\n");
-  PrintStackTrace(thr, CurrentStackTrace(thr));
+  Printf("WARNING: lock-order-inversion (potential deadlock)\n");
   for (int i = 0; i < rep->n; i++) {
-    Printf("Mutex %llu created at:\n", rep->loop[i].mtx_ctx0);
+    Printf("Thread %d locks mutex %llu under mutex %llu:\n",
+      rep->loop[i].thr_ctx, rep->loop[i].mtx_ctx1, rep->loop[i].mtx_ctx0);
     PrintStackTrace(thr, rep->loop[i].stk);
   }
   Printf("==============================\n");
 }
 
-void MutexLock(Thread *thr, uptr m, bool writelock, bool trylock) {
+Callback::Callback(Thread *thr)
+    : thr(thr) {
+  lt = thr->dd_lt;
+  pt = thr->dd_pt;
+}
+
+u32 Callback::Unwind() {
+  return CurrentStackTrace(thr, 3);
+}
+
+void Initialize() {
+  static u64 ctx_mem[sizeof(Context) / sizeof(u64) + 1];
+  ctx = new(ctx_mem) Context();
+
+  InitializeInterceptors();
+  ParseCommonFlagsFromString(flags(), GetEnv("DSAN_OPTIONS"));
+  //common_flags()->allow_addr2line = true;
+  common_flags()->symbolize = true;
+  ctx->dd = DDetector::Create();
+}
+
+void ThreadInit(Thread *thr) {
+  static atomic_uintptr_t id_gen;
+  uptr id = atomic_fetch_add(&id_gen, 1, memory_order_relaxed);
+  thr->dd_pt = ctx->dd->CreatePhysicalThread();
+  thr->dd_lt = ctx->dd->CreateLogicalThread(id);
+}
+
+void ThreadDestroy(Thread *thr) {
+  ctx->dd->DestroyPhysicalThread(thr->dd_pt);
+  ctx->dd->DestroyLogicalThread(thr->dd_lt);
+}
+
+void MutexBeforeLock(Thread *thr, uptr m, bool writelock) {
   if (thr->ignore_interceptors)
     return;
-  DDReport *rep = 0;
+  Callback cb(thr);
   {
     MutexHashMap::Handle h(&ctx->mutex_map, m);
     if (h.created())
-      ctx->dd->MutexInit(&h->dd, CurrentStackTrace(thr), m);
-    rep = ctx->dd->MutexLock(thr->dd_pt, thr->dd_lt, &h->dd,
-                             writelock, trylock);
+      ctx->dd->MutexInit(&cb, &h->dd);
+    ctx->dd->MutexBeforeLock(&cb, &h->dd, writelock);
   }
-  if (rep)
-    ReportDeadlock(thr, rep);
+  ReportDeadlock(thr, ctx->dd->GetReport(&cb));
 }
 
-void MutexUnlock(Thread *thr, uptr m, bool writelock) {
+void MutexAfterLock(Thread *thr, uptr m, bool writelock, bool trylock) {
   if (thr->ignore_interceptors)
     return;
-  MutexHashMap::Handle h(&ctx->mutex_map, m);
-  ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &h->dd, writelock);
+  Callback cb(thr);
+  {
+    MutexHashMap::Handle h(&ctx->mutex_map, m);
+    if (h.created())
+      ctx->dd->MutexInit(&cb, &h->dd);
+    ctx->dd->MutexAfterLock(&cb, &h->dd, writelock, trylock);
+  }
+  ReportDeadlock(thr, ctx->dd->GetReport(&cb));
+}
+
+void MutexBeforeUnlock(Thread *thr, uptr m, bool writelock) {
+  if (thr->ignore_interceptors)
+    return;
+  Callback cb(thr);
+  {
+    MutexHashMap::Handle h(&ctx->mutex_map, m);
+    ctx->dd->MutexBeforeUnlock(&cb, &h->dd, writelock);
+  }
+  ReportDeadlock(thr, ctx->dd->GetReport(&cb));
 }
 
 void MutexDestroy(Thread *thr, uptr m) {
   if (thr->ignore_interceptors)
     return;
+  Callback cb(thr);
   MutexHashMap::Handle h(&ctx->mutex_map, m, true);
   if (!h.exists())
     return;
-  ctx->dd->MutexDestroy(thr->dd_pt, thr->dd_lt, &h->dd);
+  ctx->dd->MutexDestroy(&cb, &h->dd);
 }
 
 }  // namespace __dsan