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
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index d5c6520..acdd0d3 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -27,6 +27,7 @@
"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 e9b7cf5..53f8747 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 @@
}
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 @@
// 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 0000000..e36b947
--- /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 0000000..3cbda5f
--- /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 8b355c8..4936a2f 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1383,6 +1383,10 @@
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 @@
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 6344c69..e580e78 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -58,9 +58,10 @@
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 c379b5c..8974b59 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -351,8 +351,13 @@
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 @@
}
}
+ 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 @@
};
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 @@
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 @@
// 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 052fb9a..3a7d1a1 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -82,6 +82,17 @@
}
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 @@
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 2aee99b..11c51d0 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 775e5c2..faf2dc2 100644
--- a/test/901-hello-ti-agent/src/Main.java
+++ b/test/901-hello-ti-agent/src/Main.java
@@ -20,6 +20,10 @@
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 @@
}
}
+ private static native boolean checkLivePhase();
private static native void setVerboseFlag(int flag, boolean value);
}