tsan: better reporting of thread leaks
1. do not report running threads as leaks
2. aggregate leaked threads by creation stack

llvm-svn: 177647
diff --git a/compiler-rt/lib/tsan/lit_tests/thread_leak3.c b/compiler-rt/lib/tsan/lit_tests/thread_leak3.c
index a39c93c..3577164 100644
--- a/compiler-rt/lib/tsan/lit_tests/thread_leak3.c
+++ b/compiler-rt/lib/tsan/lit_tests/thread_leak3.c
@@ -1,5 +1,6 @@
 // RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
 #include <pthread.h>
+#include <unistd.h>
 
 void *Thread(void *x) {
   return 0;
@@ -8,6 +9,7 @@
 int main() {
   pthread_t t;
   pthread_create(&t, 0, Thread, 0);
+  sleep(1);
   return 0;
 }
 
diff --git a/compiler-rt/lib/tsan/lit_tests/thread_leak4.c b/compiler-rt/lib/tsan/lit_tests/thread_leak4.c
new file mode 100644
index 0000000..3909c0a
--- /dev/null
+++ b/compiler-rt/lib/tsan/lit_tests/thread_leak4.c
@@ -0,0 +1,16 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+void *Thread(void *x) {
+  sleep(10);
+  return 0;
+}
+
+int main() {
+  pthread_t t;
+  pthread_create(&t, 0, Thread, 0);
+  return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
diff --git a/compiler-rt/lib/tsan/lit_tests/thread_leak5.c b/compiler-rt/lib/tsan/lit_tests/thread_leak5.c
new file mode 100644
index 0000000..fc72b14
--- /dev/null
+++ b/compiler-rt/lib/tsan/lit_tests/thread_leak5.c
@@ -0,0 +1,19 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+void *Thread(void *x) {
+  return 0;
+}
+
+int main() {
+  for (int i = 0; i < 5; i++) {
+    pthread_t t;
+    pthread_create(&t, 0, Thread, 0);
+  }
+  sleep(1);
+  return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: thread leak
+// CHECK:   And 4 more similar thread leaks
diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.cc b/compiler-rt/lib/tsan/rtl/tsan_report.cc
index 30e5488..bfa2660 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_report.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_report.cc
@@ -22,7 +22,8 @@
     , locs(MBlockReportLoc)
     , mutexes(MBlockReportMutex)
     , threads(MBlockReportThread)
-    , sleep() {
+    , sleep()
+    , count() {
 }
 
 ReportMop::ReportMop()
@@ -201,6 +202,9 @@
   for (uptr i = 0; i < rep->threads.Size(); i++)
     PrintThread(rep->threads[i]);
 
+  if (rep->typ == ReportTypeThreadLeak && rep->count > 1)
+    Printf("  And %d more similar thread leaks.\n\n", rep->count - 1);
+
   if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep)))
     ReportErrorSummary(rep_typ_str, ent->file, ent->line, ent->func);
 
diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.h b/compiler-rt/lib/tsan/rtl/tsan_report.h
index eacb10c..b2ce0dd 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_report.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_report.h
@@ -102,6 +102,7 @@
   Vector<ReportMutex*> mutexes;
   Vector<ReportThread*> threads;
   ReportStack *sleep;
+  int count;
 
   ReportDesc();
   ~ReportDesc();
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
index c216a80..3af0402 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
@@ -554,6 +554,7 @@
   void AddMutex(const SyncVar *s);
   void AddLocation(uptr addr, uptr size);
   void AddSleep(u32 stack_id);
+  void SetCount(int count);
 
   const ReportDesc *GetReport() const;
 
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc
index 50c5311..fd61f4a 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc
@@ -365,6 +365,10 @@
 }
 #endif
 
+void ScopedReport::SetCount(int count) {
+  rep_->count = count;
+}
+
 const ReportDesc *ScopedReport::GetReport() const {
   return rep_;
 }
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc
index e93a7a3..35e9787 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc
@@ -143,26 +143,44 @@
   thr = 0;
 }
 
-static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *unused) {
+#ifndef TSAN_GO
+struct ThreadLeak {
+  ThreadContext *tctx;
+  int count;
+};
+
+static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) {
+  Vector<ThreadLeak> &leaks = *(Vector<ThreadLeak>*)arg;
   ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
-  if (tctx->detached)
+  if (tctx->detached || tctx->status != ThreadStatusFinished)
     return;
-  if (tctx->status != ThreadStatusCreated
-      && tctx->status != ThreadStatusRunning
-      && tctx->status != ThreadStatusFinished)
-    return;
-  ScopedReport rep(ReportTypeThreadLeak);
-  rep.AddThread(tctx);
-  OutputReport(CTX(), rep);
+  for (uptr i = 0; i < leaks.Size(); i++) {
+    if (leaks[i].tctx->creation_stack_id == tctx->creation_stack_id) {
+      leaks[i].count++;
+      return;
+    }
+  }
+  ThreadLeak leak = {tctx, 1};
+  leaks.PushBack(leak);
 }
+#endif
 
 void ThreadFinalize(ThreadState *thr) {
   CHECK_GT(thr->in_rtl, 0);
+#ifndef TSAN_GO
   if (!flags()->report_thread_leaks)
     return;
   ThreadRegistryLock l(CTX()->thread_registry);
+  Vector<ThreadLeak> leaks(MBlockScopedBuf);
   CTX()->thread_registry->RunCallbackForEachThreadLocked(
-      MaybeReportThreadLeak, 0);
+      MaybeReportThreadLeak, &leaks);
+  for (uptr i = 0; i < leaks.Size(); i++) {
+    ScopedReport rep(ReportTypeThreadLeak);
+    rep.AddThread(leaks[i].tctx);
+    rep.SetCount(leaks[i].count);
+    OutputReport(CTX(), rep);
+  }
+#endif
 }
 
 int ThreadCount(ThreadState *thr) {