[ASan] Add __asan_set_on_error_callback() interface function that allows user to set a callback to be called right when ASan detects an error

git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@161754 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/asan/asan_interface.h b/lib/asan/asan_interface.h
index c625a62..c1390ce 100644
--- a/lib/asan/asan_interface.h
+++ b/lib/asan/asan_interface.h
@@ -118,6 +118,13 @@
   void __asan_set_error_report_callback(void (*callback)(const char*))
       SANITIZER_INTERFACE_ATTRIBUTE;
 
+  // Sets the callback to be called right when ASan detects an error.
+  // This can be used to notice cases when ASan detects an error, but the
+  // program crashes before ASan report is printed.
+  // Passing 0 unsets the callback.
+  void __asan_set_on_error_callback(void (*callback)(void))
+      SANITIZER_INTERFACE_ATTRIBUTE;
+
   // Returns the estimated number of bytes that will be reserved by allocator
   // for request of "size" bytes. If ASan allocator can't allocate that much
   // memory, returns the maximal possible allocation size, otherwise returns
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc
index 883b84b..1cabbcd 100644
--- a/lib/asan/asan_report.cc
+++ b/lib/asan/asan_report.cc
@@ -21,7 +21,7 @@
 
 namespace __asan {
 
-// ---------------------- Error report callback ------------------- {{{1
+// -------------------- User-specified callbacks ----------------- {{{1
 static void (*error_report_callback)(const char*);
 static char *error_message_buffer = 0;
 static uptr error_message_buffer_pos = 0;
@@ -40,6 +40,8 @@
   }
 }
 
+static void (*on_error_callback)(void);
+
 // ---------------------- Helper functions ----------------------- {{{1
 
 static void PrintBytes(const char *before, uptr *a) {
@@ -219,6 +221,9 @@
       SleepForSeconds(Max(5, flags()->sleep_before_dying + 1));
       Die();
     }
+    if (on_error_callback) {
+      on_error_callback();
+    }
     AsanPrintf("===================================================="
                "=============\n");
     AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
@@ -409,3 +414,7 @@
     error_message_buffer_pos = 0;
   }
 }
+
+void NOINLINE __asan_set_on_error_callback(void (*callback)(void)) {
+  on_error_callback = callback;
+}
diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc
index 44d4c3c..e066d0b 100644
--- a/lib/asan/tests/asan_noinst_test.cc
+++ b/lib/asan/tests/asan_noinst_test.cc
@@ -530,6 +530,12 @@
   __asan_set_death_callback(NULL);
 }
 
+TEST(AddressSanitizerInterface, OnErrorCallbackTest) {
+  __asan_set_on_error_callback(MyDeathCallback);
+  EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback.*double-free");
+  __asan_set_on_error_callback(NULL);
+}
+
 static const char* kUseAfterPoisonErrorMessage = "use-after-poison";
 
 #define GOOD_ACCESS(ptr, offset)  \