ART: Add SigQuit Callback

Add callback being triggered when the runtime handles SigQuit.

Bug: 31684920
Test: m test-art-host-gtest-runtime_callbacks_test
Change-Id: I23e3b256c654b6078c79b3897439d893ea79d96e
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 5a5ed75b..0246649 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1552,6 +1552,12 @@
 
   thread_list_->DumpForSigQuit(os);
   BaseMutex::DumpAll(os);
+
+  // Inform anyone else who is interested in SigQuit.
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    callbacks_->SigQuit();
+  }
 }
 
 void Runtime::DumpLockHolders(std::ostream& os) {
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index ee9edda..cd38ead 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 
+#include "base/macros.h"
 #include "class_linker.h"
 #include "thread.h"
 
@@ -27,13 +28,19 @@
   thread_callbacks_.push_back(cb);
 }
 
-void RuntimeCallbacks::RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
-  auto it = std::find(thread_callbacks_.begin(), thread_callbacks_.end(), cb);
-  if (it != thread_callbacks_.end()) {
-    thread_callbacks_.erase(it);
+template <typename T>
+ALWAYS_INLINE
+static inline void Remove(T* cb, std::vector<T*>* data) {
+  auto it = std::find(data->begin(), data->end(), cb);
+  if (it != data->end()) {
+    data->erase(it);
   }
 }
 
+void RuntimeCallbacks::RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
+  Remove(cb, &thread_callbacks_);
+}
+
 void RuntimeCallbacks::ThreadStart(Thread* self) {
   for (ThreadLifecycleCallback* cb : thread_callbacks_) {
     cb->ThreadStart(self);
@@ -51,10 +58,7 @@
 }
 
 void RuntimeCallbacks::RemoveClassLoadCallback(ClassLoadCallback* cb) {
-  auto it = std::find(class_callbacks_.begin(), class_callbacks_.end(), cb);
-  if (it != class_callbacks_.end()) {
-    class_callbacks_.erase(it);
-  }
+  Remove(cb, &class_callbacks_);
 }
 
 void RuntimeCallbacks::ClassLoad(Handle<mirror::Class> klass) {
@@ -69,4 +73,18 @@
   }
 }
 
+void RuntimeCallbacks::AddRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb) {
+  sigquit_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb) {
+  Remove(cb, &sigquit_callbacks_);
+}
+
+void RuntimeCallbacks::SigQuit() {
+  for (RuntimeSigQuitCallback* cb : sigquit_callbacks_) {
+    cb->SigQuit();
+  }
+}
+
 }  // namespace art
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 5bdb44a..d700cf2d 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -48,6 +48,13 @@
 //       any state checking (is the listener enabled) in the listener itself. For an example, see
 //       Dbg.
 
+class RuntimeSigQuitCallback {
+ public:
+  virtual ~RuntimeSigQuitCallback() {}
+
+  virtual void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
 class RuntimeCallbacks {
  public:
   void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
@@ -63,11 +70,20 @@
   void ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void AddRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb)
+      REQUIRES(Locks::mutator_lock_);
+  void RemoveRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb)
+      REQUIRES(Locks::mutator_lock_);
+
+  void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   std::vector<ThreadLifecycleCallback*> thread_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
   std::vector<ClassLoadCallback*> class_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
+  std::vector<RuntimeSigQuitCallback*> sigquit_callbacks_
+      GUARDED_BY(Locks::mutator_lock_);
 };
 
 }  // namespace art
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index f05794d..c96bfd4 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -17,6 +17,9 @@
 #include "runtime_callbacks.h"
 
 #include "jni.h"
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <initializer_list>
 #include <memory>
@@ -290,4 +293,47 @@
   EXPECT_TRUE(expect2);
 }
 
+class RuntimeSigQuitCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest {
+ protected:
+  void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+    Runtime::Current()->GetRuntimeCallbacks()->AddRuntimeSigQuitCallback(&cb_);
+  }
+  void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+    Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimeSigQuitCallback(&cb_);
+  }
+
+  struct Callback : public RuntimeSigQuitCallback {
+    void SigQuit() OVERRIDE {
+      ++sigquit_count;
+    }
+
+    size_t sigquit_count = 0;
+  };
+
+  Callback cb_;
+};
+
+TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) {
+  // The runtime needs to be started for the signal handler.
+  Thread* self = Thread::Current();
+
+  self->TransitionFromSuspendedToRunnable();
+  bool started = runtime_->Start();
+  ASSERT_TRUE(started);
+
+  EXPECT_EQ(0u, cb_.sigquit_count);
+
+  kill(getpid(), SIGQUIT);
+
+  // Try a few times.
+  for (size_t i = 0; i != 30; ++i) {
+    if (cb_.sigquit_count == 0) {
+      sleep(1);
+    } else {
+      break;
+    }
+  }
+  EXPECT_EQ(1u, cb_.sigquit_count);
+}
+
 }  // namespace art