OSDN Git Service

Revert "Revert "ART: Start RuntimeCallbacks""
authorAndreas Gampe <agampe@google.com>
Thu, 19 Jan 2017 17:49:03 +0000 (17:49 +0000)
committerAndreas Gampe <agampe@google.com>
Thu, 19 Jan 2017 23:12:06 +0000 (15:12 -0800)
This reverts commit bf6331a45f730e1f1044af2ce43bceda660ae6fc.

Switch to using the mutator lock.

Bug: 31684920
Test: m test-art-host-gtest-runtime_callbacks_test
Test: art/tools/run-jdwp-tests.sh --mode=host --debug
Change-Id: I6ce0f71de2aa9f90cd23cfca7723a793b560b16b

runtime/Android.bp
runtime/debugger.cc
runtime/debugger.h
runtime/runtime.cc
runtime/runtime.h
runtime/runtime_callbacks.cc [new file with mode: 0644]
runtime/runtime_callbacks.h [new file with mode: 0644]
runtime/runtime_callbacks_test.cc [new file with mode: 0644]
runtime/thread.cc
runtime/thread.h

index b38eaac..81f174e 100644 (file)
@@ -184,6 +184,7 @@ cc_defaults {
         "reference_table.cc",
         "reflection.cc",
         "runtime.cc",
+        "runtime_callbacks.cc",
         "runtime_options.cc",
         "signal_catcher.cc",
         "stack.cc",
@@ -563,6 +564,7 @@ art_cc_test {
         "parsed_options_test.cc",
         "prebuilt_tools_test.cc",
         "reference_table_test.cc",
+        "runtime_callbacks_test.cc",
         "thread_pool_test.cc",
         "transaction_test.cc",
         "type_lookup_table_test.cc",
index 006476b..6da7e3a 100644 (file)
@@ -320,6 +320,8 @@ size_t Dbg::field_write_event_ref_count_ = 0;
 size_t Dbg::exception_catch_event_ref_count_ = 0;
 uint32_t Dbg::instrumentation_events_ = 0;
 
+Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_;
+
 // Breakpoints.
 static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
 
@@ -5135,4 +5137,12 @@ void Dbg::VisitRoots(RootVisitor* visitor) {
   }
 }
 
+void Dbg::DbgThreadLifecycleCallback::ThreadStart(Thread* self) {
+  Dbg::PostThreadStart(self);
+}
+
+void Dbg::DbgThreadLifecycleCallback::ThreadDeath(Thread* self) {
+  Dbg::PostThreadDeath(self);
+}
+
 }  // namespace art
index 3b4a5e1..0135990 100644 (file)
@@ -502,10 +502,6 @@ class Dbg {
       REQUIRES_SHARED(Locks::mutator_lock_);
   static void PostException(mirror::Throwable* exception)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  static void PostThreadStart(Thread* t)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  static void PostThreadDeath(Thread* t)
-      REQUIRES_SHARED(Locks::mutator_lock_);
   static void PostClassPrepare(mirror::Class* c)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -707,6 +703,10 @@ class Dbg {
     return instrumentation_events_;
   }
 
+  static ThreadLifecycleCallback* GetThreadLifecycleCallback() {
+    return &thread_lifecycle_callback_;
+  }
+
  private:
   static void ExecuteMethodWithoutPendingException(ScopedObjectAccess& soa, DebugInvokeReq* pReq)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -725,6 +725,11 @@ class Dbg {
       REQUIRES(!Locks::thread_list_lock_) REQUIRES_SHARED(Locks::mutator_lock_);
 
   static void DdmBroadcast(bool connect) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  static void PostThreadStart(Thread* t)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  static void PostThreadDeath(Thread* t)
+      REQUIRES_SHARED(Locks::mutator_lock_);
   static void PostThreadStartOrStop(Thread*, uint32_t)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -789,6 +794,14 @@ class Dbg {
   static size_t exception_catch_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
   static uint32_t instrumentation_events_ GUARDED_BY(Locks::mutator_lock_);
 
+  class DbgThreadLifecycleCallback : public ThreadLifecycleCallback {
+   public:
+    void ThreadStart(Thread* self) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+    void ThreadDeath(Thread* self) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+  };
+
+  static DbgThreadLifecycleCallback thread_lifecycle_callback_;
+
   DISALLOW_COPY_AND_ASSIGN(Dbg);
 };
 
index 55e1852..6ef5f26 100644 (file)
@@ -1100,6 +1100,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
   if (runtime_options.Exists(Opt::JdwpOptions)) {
     Dbg::ConfigureJdwp(runtime_options.GetOrDefault(Opt::JdwpOptions));
   }
+  callbacks_.AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback());
 
   jit_options_.reset(jit::JitOptions::CreateFromRuntimeArguments(runtime_options));
   if (IsAotCompiler()) {
index cf23d05..17e0788 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "arch/instruction_set.h"
 #include "base/macros.h"
+#include "base/mutex.h"
 #include "dex_file_types.h"
 #include "experimental_flags.h"
 #include "gc_root.h"
@@ -39,6 +40,7 @@
 #include "offsets.h"
 #include "process_state.h"
 #include "quick/quick_method_frame_info.h"
+#include "runtime_callbacks.h"
 #include "runtime_stats.h"
 
 namespace art {
@@ -659,6 +661,10 @@ class Runtime {
 
   void AttachAgent(const std::string& agent_arg);
 
+  RuntimeCallbacks& GetRuntimeCallbacks() {
+    return callbacks_;
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -916,6 +922,8 @@ class Runtime {
 
   ClassHierarchyAnalysis* cha_;
 
+  RuntimeCallbacks callbacks_;
+
   DISALLOW_COPY_AND_ASSIGN(Runtime);
 };
 std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
new file mode 100644 (file)
index 0000000..a523ddf
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "runtime_callbacks.h"
+
+#include <algorithm>
+
+#include "thread.h"
+
+namespace art {
+
+void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
+  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);
+  }
+}
+
+void RuntimeCallbacks::ThreadStart(Thread* self) {
+  for (ThreadLifecycleCallback* cb : thread_callbacks_) {
+    cb->ThreadStart(self);
+  }
+}
+
+void RuntimeCallbacks::ThreadDeath(Thread* self) {
+  for (ThreadLifecycleCallback* cb : thread_callbacks_) {
+    cb->ThreadDeath(self);
+  }
+}
+
+}  // namespace art
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
new file mode 100644 (file)
index 0000000..1060d85
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_RUNTIME_CALLBACKS_H_
+#define ART_RUNTIME_RUNTIME_CALLBACKS_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/mutex.h"
+
+namespace art {
+
+class Thread;
+class ThreadLifecycleCallback;
+
+// Note: RuntimeCallbacks uses the mutator lock to synchronize the callback lists. A thread must
+//       hold the exclusive lock to add or remove a listener. A thread must hold the shared lock
+//       to dispatch an event. This setup is chosen as some clients may want to suspend the
+//       dispatching thread or all threads.
+//
+//       To make this safe, the following restrictions apply:
+//       * Only the owner of a listener may ever add or remove said listener.
+//       * A listener must never add or remove itself or any other listener while running.
+//       * It is the responsibility of the owner to not remove the listener while it is running
+//         (and suspended).
+//
+//       The simplest way to satisfy these restrictions is to never remove a listener, and to do
+//       any state checking (is the listener enabled) in the listener itself. For an example, see
+//       Dbg.
+
+class RuntimeCallbacks {
+ public:
+  void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb)
+      REQUIRES(Locks::mutator_lock_);
+  void RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb)
+      REQUIRES(Locks::mutator_lock_);
+
+  void ThreadStart(Thread* self)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  void ThreadDeath(Thread* self)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+  std::vector<ThreadLifecycleCallback*> thread_callbacks_
+      GUARDED_BY(Locks::mutator_lock_);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_RUNTIME_CALLBACKS_H_
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
new file mode 100644 (file)
index 0000000..62729ca
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "runtime_callbacks.h"
+
+#include "jni.h"
+#include <memory>
+#include <string>
+
+#include "art_method-inl.h"
+#include "base/mutex.h"
+#include "mirror/class-inl.h"
+#include "common_runtime_test.h"
+#include "mem_map.h"
+#include "obj_ptr.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+class RuntimeCallbacksTest : public CommonRuntimeTest {
+ protected:
+  void SetUp() OVERRIDE {
+    CommonRuntimeTest::SetUp();
+
+    Thread* self = Thread::Current();
+    ScopedObjectAccess soa(self);
+    ScopedThreadSuspension sts(self, kWaitingForDebuggerToAttach);
+    ScopedSuspendAll ssa("RuntimeCallbacksTest SetUp");
+    AddListener();
+  }
+
+  void TearDown() OVERRIDE {
+    {
+      Thread* self = Thread::Current();
+      ScopedObjectAccess soa(self);
+      ScopedThreadSuspension sts(self, kWaitingForDebuggerToAttach);
+      ScopedSuspendAll ssa("RuntimeCallbacksTest TearDown");
+      AddListener();
+      RemoveListener();
+    }
+
+    CommonRuntimeTest::TearDown();
+  }
+
+  virtual void AddListener() REQUIRES(Locks::mutator_lock_) = 0;
+  virtual void RemoveListener() REQUIRES(Locks::mutator_lock_) = 0;
+
+  void MakeExecutable(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+    CHECK(klass != nullptr);
+    PointerSize pointer_size = class_linker_->GetImagePointerSize();
+    for (auto& m : klass->GetMethods(pointer_size)) {
+      if (!m.IsAbstract()) {
+        class_linker_->SetEntryPointsToInterpreter(&m);
+      }
+    }
+  }
+};
+
+class ThreadLifecycleCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest {
+ public:
+  static void* PthreadsCallback(void* arg ATTRIBUTE_UNUSED) {
+    // Attach.
+    Runtime* runtime = Runtime::Current();
+    CHECK(runtime->AttachCurrentThread("ThreadLifecycle test thread", true, nullptr, false));
+
+    // Detach.
+    runtime->DetachCurrentThread();
+
+    // Die...
+    return nullptr;
+  }
+
+ protected:
+  void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+    Runtime::Current()->GetRuntimeCallbacks().AddThreadLifecycleCallback(&cb_);
+  }
+  void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+    Runtime::Current()->GetRuntimeCallbacks().RemoveThreadLifecycleCallback(&cb_);
+  }
+
+  enum CallbackState {
+    kBase,
+    kStarted,
+    kDied,
+    kWrongStart,
+    kWrongDeath,
+  };
+
+  struct Callback : public ThreadLifecycleCallback {
+    void ThreadStart(Thread* self) OVERRIDE {
+      if (state == CallbackState::kBase) {
+        state = CallbackState::kStarted;
+        stored_self = self;
+      } else {
+        state = CallbackState::kWrongStart;
+      }
+    }
+
+    void ThreadDeath(Thread* self) OVERRIDE {
+      if (state == CallbackState::kStarted && self == stored_self) {
+        state = CallbackState::kDied;
+      } else {
+        state = CallbackState::kWrongDeath;
+      }
+    }
+
+    Thread* stored_self;
+    CallbackState state = CallbackState::kBase;
+  };
+
+  Callback cb_;
+};
+
+
+TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackJava) {
+  Thread* self = Thread::Current();
+
+  self->TransitionFromSuspendedToRunnable();
+  bool started = runtime_->Start();
+  ASSERT_TRUE(started);
+
+  cb_.state = CallbackState::kBase;  // Ignore main thread attach.
+
+  {
+    ScopedObjectAccess soa(self);
+    MakeExecutable(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread));
+  }
+
+  JNIEnv* env = self->GetJniEnv();
+
+  ScopedLocalRef<jobject> thread_name(env,
+                                      env->NewStringUTF("ThreadLifecycleCallback test thread"));
+  ASSERT_TRUE(thread_name.get() != nullptr);
+
+  ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
+  ASSERT_TRUE(thread.get() != nullptr);
+
+  env->CallNonvirtualVoidMethod(thread.get(),
+                                WellKnownClasses::java_lang_Thread,
+                                WellKnownClasses::java_lang_Thread_init,
+                                runtime_->GetMainThreadGroup(),
+                                thread_name.get(),
+                                kMinThreadPriority,
+                                JNI_FALSE);
+  ASSERT_FALSE(env->ExceptionCheck());
+
+  jmethodID start_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "start", "()V");
+  ASSERT_TRUE(start_id != nullptr);
+
+  env->CallVoidMethod(thread.get(), start_id);
+  ASSERT_FALSE(env->ExceptionCheck());
+
+  jmethodID join_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "join", "()V");
+  ASSERT_TRUE(join_id != nullptr);
+
+  env->CallVoidMethod(thread.get(), join_id);
+  ASSERT_FALSE(env->ExceptionCheck());
+
+  EXPECT_TRUE(cb_.state == CallbackState::kDied) << static_cast<int>(cb_.state);
+}
+
+TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackAttach) {
+  std::string error_msg;
+  std::unique_ptr<MemMap> stack(MemMap::MapAnonymous("ThreadLifecycleCallback Thread",
+                                                     nullptr,
+                                                     128 * kPageSize,  // Just some small stack.
+                                                     PROT_READ | PROT_WRITE,
+                                                     false,
+                                                     false,
+                                                     &error_msg));
+  ASSERT_FALSE(stack == nullptr) << error_msg;
+
+  const char* reason = "ThreadLifecycleCallback test thread";
+  pthread_attr_t attr;
+  CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason);
+  CHECK_PTHREAD_CALL(pthread_attr_setstack, (&attr, stack->Begin(), stack->Size()), reason);
+  pthread_t pthread;
+  CHECK_PTHREAD_CALL(pthread_create,
+                     (&pthread,
+                         &attr,
+                         &ThreadLifecycleCallbackRuntimeCallbacksTest::PthreadsCallback,
+                         this),
+                         reason);
+  CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), reason);
+
+  CHECK_PTHREAD_CALL(pthread_join, (pthread, nullptr), "ThreadLifecycleCallback test shutdown");
+
+  // Detach is not a ThreadDeath event, so we expect to be in state Started.
+  EXPECT_TRUE(cb_.state == CallbackState::kStarted) << static_cast<int>(cb_.state);
+}
+
+}  // namespace art
index ebf14c1..97c0c55 100644 (file)
@@ -431,7 +431,8 @@ void* Thread::CreateCallback(void* arg) {
 
     ArtField* priorityField = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority);
     self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer));
-    Dbg::PostThreadStart(self);
+
+    runtime->GetRuntimeCallbacks().ThreadStart(self);
 
     // Invoke the 'run' method of our java.lang.Thread.
     ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
@@ -793,7 +794,7 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_g
 
   {
     ScopedObjectAccess soa(self);
-    Dbg::PostThreadStart(self);
+    runtime->GetRuntimeCallbacks().ThreadStart(self);
   }
 
   return self;
@@ -1929,7 +1930,11 @@ void Thread::Destroy() {
       jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer)
           ->SetLong<false>(tlsPtr_.opeer, 0);
     }
-    Dbg::PostThreadDeath(self);
+    Runtime* runtime = Runtime::Current();
+    if (runtime != nullptr) {
+      runtime->GetRuntimeCallbacks().ThreadDeath(self);
+    }
+
 
     // Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone
     // who is waiting.
index 2b451bc..166ed21 100644 (file)
@@ -1704,6 +1704,14 @@ class ScopedTransitioningToRunnable : public ValueObject {
   Thread* const self_;
 };
 
+class ThreadLifecycleCallback {
+ public:
+  virtual ~ThreadLifecycleCallback() {}
+
+  virtual void ThreadStart(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+  virtual void ThreadDeath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
 std::ostream& operator<<(std::ostream& os, const Thread& thread);
 std::ostream& operator<<(std::ostream& os, const StackedShadowFrameType& thread);