[ASan] Add print_full_thread_history runtime option (on by default) that prints all full thread creation paths for threads involved in ASan error report

git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@163200 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
index 8abba56..dcec1ec 100644
--- a/lib/asan/asan_allocator.cc
+++ b/lib/asan/asan_allocator.cc
@@ -609,14 +609,14 @@
                alloc_thread->tid());
 
     PrintStack(&alloc_stack);
-    t->summary()->Announce();
-    free_thread->Announce();
-    alloc_thread->Announce();
+    DescribeThread(t->summary());
+    DescribeThread(free_thread);
+    DescribeThread(alloc_thread);
   } else {
     Printf("allocated by thread T%d here:\n", alloc_thread->tid());
     PrintStack(&alloc_stack);
-    t->summary()->Announce();
-    alloc_thread->Announce();
+    DescribeThread(t->summary());
+    DescribeThread(alloc_thread);
   }
 }
 
diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h
index ce30fcb..de7475b 100644
--- a/lib/asan/asan_flags.h
+++ b/lib/asan/asan_flags.h
@@ -89,6 +89,10 @@
   bool allow_reexec;
   // Strips this prefix from file paths in error reports.
   const char *strip_path_prefix;
+  // If set, prints not only thread creation stacks for threads in error report,
+  // but also thread creation stacks for threads that created those threads,
+  // etc. up to main thread.
+  bool print_full_thread_history;
 };
 
 Flags *flags();
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc
index 71ca400..2a00c4e 100644
--- a/lib/asan/asan_report.cc
+++ b/lib/asan/asan_report.cc
@@ -184,7 +184,7 @@
   Printf("HINT: this may be a false positive if your program uses "
              "some custom stack unwind mechanism\n"
              "      (longjmp and C++ exceptions *are* supported)\n");
-  t->summary()->Announce();
+  DescribeThread(t->summary());
   return true;
 }
 
@@ -201,6 +201,26 @@
   DescribeHeapAddress(addr, access_size);
 }
 
+// ------------------- Thread description -------------------- {{{1
+
+void DescribeThread(AsanThreadSummary *summary) {
+  CHECK(summary);
+  // No need to announce the main thread.
+  if (summary->tid() == 0 || summary->announced()) {
+    return;
+  }
+  summary->set_announced(true);
+  Printf("Thread T%d created by T%d here:\n",
+         summary->tid(), summary->parent_tid());
+  PrintStack(summary->stack());
+  // Recursively described parent thread if needed.
+  if (flags()->print_full_thread_history) {
+    AsanThreadSummary *parent_summary =
+        asanThreadRegistry().FindByTid(summary->parent_tid());
+    DescribeThread(parent_summary);
+  }
+}
+
 // -------------------- Different kinds of reports ----------------- {{{1
 
 // Use ScopedInErrorReport to run common actions just before and
@@ -238,7 +258,7 @@
     // Make sure the current thread is announced.
     AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
     if (curr_thread) {
-      curr_thread->summary()->Announce();
+      DescribeThread(curr_thread->summary());
     }
     // Print memory stats.
     __asan_print_accumulated_stats();
diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h
index 0845690..dc3bf9b 100644
--- a/lib/asan/asan_report.h
+++ b/lib/asan/asan_report.h
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "asan_internal.h"
+#include "asan_thread.h"
 #include "sanitizer/asan_interface.h"
 
 namespace __asan {
@@ -27,6 +28,8 @@
 // Determines memory type on its own.
 void DescribeAddress(uptr addr, uptr access_size);
 
+void DescribeThread(AsanThreadSummary *summary);
+
 // Different kinds of error reports.
 void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr);
 void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack);
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index 2970fd5..d2a51df 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -102,6 +102,7 @@
   ParseFlag(str, &f->disable_core, "disable_core");
   ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix");
   ParseFlag(str, &f->allow_reexec, "allow_reexec");
+  ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history");
 }
 
 extern "C" {
@@ -139,6 +140,7 @@
   f->disable_core = (__WORDSIZE == 64);
   f->strip_path_prefix = "";
   f->allow_reexec = true;
+  f->print_full_thread_history = true;
 
   // Override from user-specified string.
   ParseFlagsFromString(f, __asan_default_options());
diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h
index 8abc1d1..4d4c439 100644
--- a/lib/asan/asan_thread.h
+++ b/lib/asan/asan_thread.h
@@ -40,16 +40,12 @@
     }
     thread_ = 0;
   }
-  void Announce() {
-    if (tid_ == 0) return;  // no need to announce the main thread.
-    if (!announced_) {
-      announced_ = true;
-      Printf("Thread T%d created by T%d here:\n", tid_, parent_tid_);
-      PrintStack(&stack_);
-    }
-  }
   u32 tid() { return tid_; }
   void set_tid(u32 tid) { tid_ = tid; }
+  u32 parent_tid() { return parent_tid_; }
+  bool announced() { return announced_; }
+  void set_announced(bool announced) { announced_ = announced; }
+  StackTrace *stack() { return &stack_; }
   AsanThread *thread() { return thread_; }
   void set_thread(AsanThread *thread) { thread_ = thread; }
   static void TSDDtor(void *tsd);
diff --git a/lib/asan/lit_tests/deep_thread_stack.cc b/lib/asan/lit_tests/deep_thread_stack.cc
new file mode 100644
index 0000000..810f4e4
--- /dev/null
+++ b/lib/asan/lit_tests/deep_thread_stack.cc
@@ -0,0 +1,61 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <pthread.h>
+
+int *x;
+
+void *AllocThread(void *arg) {
+  x = new int;
+  *x = 42;
+  return NULL;
+}
+
+void *FreeThread(void *arg) {
+  delete x;
+  return NULL;
+}
+
+void *AccessThread(void *arg) {
+  *x = 43;  // BOOM
+  return NULL;
+}
+
+typedef void* (*callback_type)(void* arg);
+
+void *RunnerThread(void *function) {
+  pthread_t thread;
+  pthread_create(&thread, NULL, (callback_type)function, NULL);
+  pthread_join(thread, NULL);
+  return NULL;
+}
+
+void RunThread(callback_type function) {
+  pthread_t runner;
+  pthread_create(&runner, NULL, RunnerThread, (void*)function);
+  pthread_join(runner, NULL);
+}
+
+int main(int argc, char *argv[]) {
+  RunThread(AllocThread);
+  RunThread(FreeThread);
+  RunThread(AccessThread);
+  return (x != 0);
+}
+
+// CHECK: AddressSanitizer heap-use-after-free
+// CHECK: WRITE of size 4 at 0x{{.*}} thread T[[ACCESS_THREAD:[0-9]+]]
+// CHECK: freed by thread T[[FREE_THREAD:[0-9]+]] here:
+// CHECK: previously allocated by thread T[[ALLOC_THREAD:[0-9]+]] here:
+// CHECK: Thread T[[ACCESS_THREAD]] created by T[[ACCESS_RUNNER:[0-9]+]] here:
+// CHECK: Thread T[[ACCESS_RUNNER]] created by T0 here:
+// CHECK: Thread T[[FREE_THREAD]] created by T[[FREE_RUNNER:[0-9]+]] here:
+// CHECK: Thread T[[FREE_RUNNER]] created by T0 here:
+// CHECK: Thread T[[ALLOC_THREAD]] created by T[[ALLOC_RUNNER:[0-9]+]] here:
+// CHECK: Thread T[[ALLOC_RUNNER]] created by T0 here: