diff options
author | 2017-01-19 19:45:30 -0800 | |
---|---|---|
committer | 2017-01-19 23:15:31 -0800 | |
commit | 96eca7895813ec70bbbbdb2aff7a3a03dcd3b21e (patch) | |
tree | 153890b5de0ff2278011fd2ef89b3896be78a55b | |
parent | c93d203b5d9d6e5672ebfdc87f2b72ef99ae8c09 (diff) |
ART: Add GetPhase
Extend RuntimePhaseCallback. Add support for GetPhase.
Add tests.
Bug: 31455788
Test: m test-art-host-run-test-901-hello-ti-agent
Change-Id: I33b1934a0789bc713675f21c2c9f676c9d3a1ed1
-rw-r--r-- | runtime/openjdkjvmti/Android.bp | 1 | ||||
-rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 12 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_phase.cc | 105 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_phase.h | 63 | ||||
-rw-r--r-- | runtime/runtime.cc | 6 | ||||
-rw-r--r-- | runtime/runtime_callbacks.h | 7 | ||||
-rw-r--r-- | runtime/runtime_callbacks_test.cc | 13 | ||||
-rw-r--r-- | test/901-hello-ti-agent/basics.cc | 21 | ||||
-rw-r--r-- | test/901-hello-ti-agent/expected.txt | 1 | ||||
-rw-r--r-- | test/901-hello-ti-agent/src/Main.java | 5 |
10 files changed, 227 insertions, 7 deletions
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index d5c652035a..acdd0d31e9 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -27,6 +27,7 @@ cc_defaults { "ti_method.cc", "ti_monitor.cc", "ti_object.cc", + "ti_phase.cc", "ti_properties.cc", "ti_search.cc", "ti_stack.cc", diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index e9b7cf5b10..53f8747995 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -55,6 +55,7 @@ #include "ti_method.h" #include "ti_monitor.h" #include "ti_object.h" +#include "ti_phase.h" #include "ti_properties.h" #include "ti_redefine.h" #include "ti_search.h" @@ -1099,7 +1100,7 @@ class JvmtiFunctions { } static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr) { - return ERR(NOT_IMPLEMENTED); + return PhaseUtil::GetPhase(env, phase_ptr); } static jvmtiError DisposeEnvironment(jvmtiEnv* env) { @@ -1300,8 +1301,17 @@ static jint GetEnvHandler(art::JavaVMExt* vm, /*out*/void** env, jint version) { // The plugin initialization function. This adds the jvmti environment. extern "C" bool ArtPlugin_Initialize() { art::Runtime* runtime = art::Runtime::Current(); + + if (runtime->IsStarted()) { + PhaseUtil::SetToLive(); + } else { + PhaseUtil::SetToOnLoad(); + } + PhaseUtil::Register(); + runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); runtime->AddSystemWeakHolder(&gObjectTagTable); + return true; } diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc new file mode 100644 index 0000000000..e36b947b53 --- /dev/null +++ b/runtime/openjdkjvmti/ti_phase.cc @@ -0,0 +1,105 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_phase.h" + +#include "art_jvmti.h" +#include "base/macros.h" +#include "runtime.h" +#include "runtime_callbacks.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" +#include "thread_list.h" + +namespace openjdkjvmti { + +jvmtiPhase PhaseUtil::current_phase_ = static_cast<jvmtiPhase>(0); + +struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { + void NextRuntimePhase(RuntimePhase phase) OVERRIDE { + // TODO: Events. + switch (phase) { + case RuntimePhase::kInitialAgents: + PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL; + break; + case RuntimePhase::kStart: + PhaseUtil::current_phase_ = JVMTI_PHASE_START; + break; + case RuntimePhase::kInit: + PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; + break; + case RuntimePhase::kDeath: + PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD; + break; + } + } +}; + +PhaseUtil::PhaseCallback gPhaseCallback; + +jvmtiError PhaseUtil::GetPhase(jvmtiEnv* env ATTRIBUTE_UNUSED, jvmtiPhase* phase_ptr) { + if (phase_ptr == nullptr) { + return ERR(NULL_POINTER); + } + jvmtiPhase now = PhaseUtil::current_phase_; + DCHECK(now == JVMTI_PHASE_ONLOAD || + now == JVMTI_PHASE_PRIMORDIAL || + now == JVMTI_PHASE_START || + now == JVMTI_PHASE_LIVE || + now == JVMTI_PHASE_DEAD); + *phase_ptr = now; + return ERR(NONE); +} + +void PhaseUtil::SetToOnLoad() { + DCHECK_EQ(0u, static_cast<size_t>(PhaseUtil::current_phase_)); + PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD; +} + +void PhaseUtil::SetToPrimordial() { + DCHECK_EQ(static_cast<size_t>(JVMTI_PHASE_ONLOAD), static_cast<size_t>(PhaseUtil::current_phase_)); + PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD; +} + +void PhaseUtil::SetToLive() { + DCHECK_EQ(static_cast<size_t>(0), static_cast<size_t>(PhaseUtil::current_phase_)); + PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; +} + +void PhaseUtil::Register() { + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Add phase callback"); + art::Runtime::Current()->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gPhaseCallback); +} + + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h new file mode 100644 index 0000000000..3cbda5f402 --- /dev/null +++ b/runtime/openjdkjvmti/ti_phase.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class PhaseUtil { + public: + static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr); + + static void Register(); + + // Move the phase from unitialized to LOAD. + static void SetToOnLoad(); + + // Move the phase from LOAD to PRIMORDIAL. + static void SetToPrimordial(); + + // Move the phase from unitialized to LIVE. + static void SetToLive(); + + struct PhaseCallback; + + private: + static jvmtiPhase current_phase_; +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_ diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 8b355c8c19..4936a2f9a7 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1383,6 +1383,10 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { LOG(ERROR) << "Unable to load an agent: " << err; } } + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInitialAgents); + } VLOG(startup) << "Runtime::Init exiting"; @@ -1395,7 +1399,7 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so"; // Is the plugin already loaded? - for (Plugin p : *plugins) { + for (const Plugin& p : *plugins) { if (p.GetLibrary() == plugin_name) { return true; } diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h index 6344c69b1f..e580e7875e 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -58,9 +58,10 @@ class RuntimeSigQuitCallback { 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. + kInitialAgents, // Initial agent loading is done. + kStart, // The runtime is started. + kInit, // The runtime is initialized (and will run user code soon). + kDeath, // The runtime just died. }; virtual ~RuntimePhaseCallback() {} diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index c379b5c267..8974b595de 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -351,8 +351,13 @@ class RuntimePhaseCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { struct Callback : public RuntimePhaseCallback { void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase p) OVERRIDE { - if (p == RuntimePhaseCallback::RuntimePhase::kStart) { - if (init_seen > 0) { + if (p == RuntimePhaseCallback::RuntimePhase::kInitialAgents) { + if (start_seen > 0 || init_seen > 0 || death_seen > 0) { + LOG(FATAL) << "Unexpected order"; + } + ++initial_agents_seen; + } else if (p == RuntimePhaseCallback::RuntimePhase::kStart) { + if (init_seen > 0 || death_seen > 0) { LOG(FATAL) << "Init seen before start."; } ++start_seen; @@ -365,6 +370,7 @@ class RuntimePhaseCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { } } + size_t initial_agents_seen = 0; size_t start_seen = 0; size_t init_seen = 0; size_t death_seen = 0; @@ -374,6 +380,7 @@ class RuntimePhaseCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { }; TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) { + ASSERT_EQ(0u, cb_.initial_agents_seen); ASSERT_EQ(0u, cb_.start_seen); ASSERT_EQ(0u, cb_.init_seen); ASSERT_EQ(0u, cb_.death_seen); @@ -386,6 +393,7 @@ TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) { ASSERT_TRUE(started); } + ASSERT_EQ(0u, cb_.initial_agents_seen); ASSERT_EQ(1u, cb_.start_seen); ASSERT_EQ(1u, cb_.init_seen); ASSERT_EQ(0u, cb_.death_seen); @@ -393,6 +401,7 @@ TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) { // Delete the runtime. runtime_.reset(); + ASSERT_EQ(0u, cb_.initial_agents_seen); ASSERT_EQ(1u, cb_.start_seen); ASSERT_EQ(1u, cb_.init_seen); ASSERT_EQ(1u, cb_.death_seen); diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc index 052fb9ac13..3a7d1a1e99 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -82,6 +82,17 @@ jint OnLoad(JavaVM* vm, } SetAllCapabilities(jvmti_env); + jvmtiPhase current_phase; + jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase); + if (phase_result != JVMTI_ERROR_NONE) { + printf("Could not get phase"); + return 1; + } + if (current_phase != JVMTI_PHASE_ONLOAD) { + printf("Wrong phase"); + return 1; + } + return JNI_OK; } @@ -92,5 +103,15 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setVerboseFlag( JvmtiErrorToException(env, result); } +extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkLivePhase( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jvmtiPhase current_phase; + jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase); + if (JvmtiErrorToException(env, phase_result)) { + return JNI_FALSE; + } + return (current_phase == JVMTI_PHASE_LIVE) ? JNI_TRUE : JNI_FALSE; +} + } // namespace Test901HelloTi } // namespace art diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt index 2aee99b25a..11c51d0d84 100644 --- a/test/901-hello-ti-agent/expected.txt +++ b/test/901-hello-ti-agent/expected.txt @@ -1,5 +1,6 @@ Loaded Agent for test 901-hello-ti-agent Hello, world! +Agent in live phase. 0 1 2 diff --git a/test/901-hello-ti-agent/src/Main.java b/test/901-hello-ti-agent/src/Main.java index 775e5c2e0c..faf2dc2591 100644 --- a/test/901-hello-ti-agent/src/Main.java +++ b/test/901-hello-ti-agent/src/Main.java @@ -20,6 +20,10 @@ public class Main { System.out.println("Hello, world!"); + if (checkLivePhase()) { + System.out.println("Agent in live phase."); + } + set(0); // OTHER set(1); // GC set(2); // CLASS @@ -37,5 +41,6 @@ public class Main { } } + private static native boolean checkLivePhase(); private static native void setVerboseFlag(int flag, boolean value); } |