diff options
-rw-r--r-- | runtime/common_runtime_test.cc | 8 | ||||
-rw-r--r-- | runtime/runtime.cc | 20 | ||||
-rw-r--r-- | runtime/runtime_callbacks.cc | 14 | ||||
-rw-r--r-- | runtime/runtime_callbacks.h | 23 | ||||
-rw-r--r-- | runtime/runtime_callbacks_test.cc | 64 |
5 files changed, 126 insertions, 3 deletions
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 743fcc87eb..fc82264b6a 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -133,7 +133,9 @@ void ScratchFile::Unlink() { static bool unstarted_initialized_ = false; -CommonRuntimeTestImpl::CommonRuntimeTestImpl() {} +CommonRuntimeTestImpl::CommonRuntimeTestImpl() + : class_linker_(nullptr), java_lang_dex_file_(nullptr) { +} CommonRuntimeTestImpl::~CommonRuntimeTestImpl() { // Ensure the dex files are cleaned up before the runtime. @@ -425,7 +427,9 @@ void CommonRuntimeTestImpl::TearDown() { TearDownAndroidData(android_data_, true); dalvik_cache_.clear(); - Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test + if (runtime_ != nullptr) { + runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption after the test + } } static std::string GetDexFileName(const std::string& jar_prefix, bool host) { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 02466499c8..8b355c8c19 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -304,6 +304,13 @@ Runtime::~Runtime() { Trace::Shutdown(); + // Report death. Clients me require a working thread, still, so do it before GC completes and + // all non-daemon threads are done. + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kDeath); + } + if (attach_shutdown_thread) { DetachCurrentThread(); self = nullptr; @@ -706,6 +713,13 @@ bool Runtime::Start() { Thread::FinishStartup(); + // Send the start phase event. We have to wait till here as this is when the main thread peer + // has just been generated, important root clinits have been run and JNI is completely functional. + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kStart); + } + system_class_loader_ = CreateSystemClassLoader(this); if (!is_zygote_) { @@ -742,6 +756,12 @@ bool Runtime::Start() { 0); } + // Send the initialized phase event. + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit); + } + return true; } diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc index cd38ead2c3..7b15a4f1b5 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -87,4 +87,18 @@ void RuntimeCallbacks::SigQuit() { } } +void RuntimeCallbacks::AddRuntimePhaseCallback(RuntimePhaseCallback* cb) { + phase_callbacks_.push_back(cb); +} + +void RuntimeCallbacks::RemoveRuntimePhaseCallback(RuntimePhaseCallback* cb) { + Remove(cb, &phase_callbacks_); +} + +void RuntimeCallbacks::NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase) { + for (RuntimePhaseCallback* cb : phase_callbacks_) { + cb->NextRuntimePhase(phase); + } +} + } // namespace art diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h index d700cf2d53..6344c69b1f 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -55,6 +55,19 @@ class RuntimeSigQuitCallback { virtual void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_) = 0; }; +class RuntimePhaseCallback { + public: + enum RuntimePhase { + kStart, // The runtime is started. + kInit, // The runtime is initialized (and will run user code soon). + kDeath, // The runtime just died. + }; + + virtual ~RuntimePhaseCallback() {} + + virtual void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(Locks::mutator_lock_) = 0; +}; + class RuntimeCallbacks { public: void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_); @@ -77,6 +90,14 @@ class RuntimeCallbacks { void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_); + void AddRuntimePhaseCallback(RuntimePhaseCallback* cb) + REQUIRES(Locks::mutator_lock_); + void RemoveRuntimePhaseCallback(RuntimePhaseCallback* cb) + REQUIRES(Locks::mutator_lock_); + + void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase) + REQUIRES_SHARED(Locks::mutator_lock_); + private: std::vector<ThreadLifecycleCallback*> thread_callbacks_ GUARDED_BY(Locks::mutator_lock_); @@ -84,6 +105,8 @@ class RuntimeCallbacks { GUARDED_BY(Locks::mutator_lock_); std::vector<RuntimeSigQuitCallback*> sigquit_callbacks_ GUARDED_BY(Locks::mutator_lock_); + std::vector<RuntimePhaseCallback*> phase_callbacks_ + GUARDED_BY(Locks::mutator_lock_); }; } // namespace art diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index c96bfd4c70..c379b5c267 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -62,7 +62,6 @@ class RuntimeCallbacksTest : public CommonRuntimeTest { ScopedObjectAccess soa(self); ScopedThreadSuspension sts(self, kWaitingForDebuggerToAttach); ScopedSuspendAll ssa("RuntimeCallbacksTest TearDown"); - AddListener(); RemoveListener(); } @@ -336,4 +335,67 @@ TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) { EXPECT_EQ(1u, cb_.sigquit_count); } +class RuntimePhaseCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { + protected: + void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) { + Runtime::Current()->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&cb_); + } + void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) { + Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&cb_); + } + + void TearDown() OVERRIDE { + // Bypass RuntimeCallbacksTest::TearDown, as the runtime is already gone. + CommonRuntimeTest::TearDown(); + } + + struct Callback : public RuntimePhaseCallback { + void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase p) OVERRIDE { + if (p == RuntimePhaseCallback::RuntimePhase::kStart) { + if (init_seen > 0) { + LOG(FATAL) << "Init seen before start."; + } + ++start_seen; + } else if (p == RuntimePhaseCallback::RuntimePhase::kInit) { + ++init_seen; + } else if (p == RuntimePhaseCallback::RuntimePhase::kDeath) { + ++death_seen; + } else { + LOG(FATAL) << "Unknown phase " << static_cast<uint32_t>(p); + } + } + + size_t start_seen = 0; + size_t init_seen = 0; + size_t death_seen = 0; + }; + + Callback cb_; +}; + +TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) { + ASSERT_EQ(0u, cb_.start_seen); + ASSERT_EQ(0u, cb_.init_seen); + ASSERT_EQ(0u, cb_.death_seen); + + // Start the runtime. + { + Thread* self = Thread::Current(); + self->TransitionFromSuspendedToRunnable(); + bool started = runtime_->Start(); + ASSERT_TRUE(started); + } + + ASSERT_EQ(1u, cb_.start_seen); + ASSERT_EQ(1u, cb_.init_seen); + ASSERT_EQ(0u, cb_.death_seen); + + // Delete the runtime. + runtime_.reset(); + + ASSERT_EQ(1u, cb_.start_seen); + ASSERT_EQ(1u, cb_.init_seen); + ASSERT_EQ(1u, cb_.death_seen); +} + } // namespace art |