ART: Add phase events
Add VMStart, VMInit and VMDeath event support. Add removal of
jvmtiEnv from the event handler. Add and extend tests.
Bug: 31684920
Test: m test-art-host-901-hello-ti-agent
Change-Id: I914dfac98c2fb7b59efdfde69597a7fcd20fd486
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 53f8747..7882ece 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1105,6 +1105,7 @@
static jvmtiError DisposeEnvironment(jvmtiEnv* env) {
ENSURE_VALID_ENV(env);
+ gEventHandler.RemoveArtJvmTiEnv(ArtJvmTiEnv::AsArtJvmTiEnv(env));
delete env;
return OK;
}
@@ -1307,7 +1308,7 @@
} else {
PhaseUtil::SetToOnLoad();
}
- PhaseUtil::Register();
+ PhaseUtil::Register(&gEventHandler);
runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
runtime->AddSystemWeakHolder(&gObjectTagTable);
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index f38aa86..7182055 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -144,6 +144,18 @@
envs.push_back(env);
}
+void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) {
+ auto it = std::find(envs.begin(), envs.end(), env);
+ if (it != envs.end()) {
+ envs.erase(it);
+ for (size_t i = static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal);
+ i <= static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal);
+ ++i) {
+ RecalculateGlobalEventMask(static_cast<ArtJvmtiEvent>(i));
+ }
+ }
+}
+
static bool IsThreadControllable(ArtJvmtiEvent event) {
switch (event) {
case ArtJvmtiEvent::kVmInit:
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 08a8765..8e246de 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -141,6 +141,9 @@
// enabled, yet.
void RegisterArtJvmTiEnv(ArtJvmTiEnv* env);
+ // Remove an env.
+ void RemoveArtJvmTiEnv(ArtJvmTiEnv* env);
+
bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const {
if (!EventMask::EventIsInRange(event)) {
return false;
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc
index e36b947..85d6b72 100644
--- a/runtime/openjdkjvmti/ti_phase.cc
+++ b/runtime/openjdkjvmti/ti_phase.cc
@@ -33,8 +33,10 @@
#include "art_jvmti.h"
#include "base/macros.h"
+#include "events-inl.h"
#include "runtime.h"
#include "runtime_callbacks.h"
+#include "ScopedLocalRef.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
#include "thread_list.h"
@@ -44,6 +46,15 @@
jvmtiPhase PhaseUtil::current_phase_ = static_cast<jvmtiPhase>(0);
struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback {
+ inline static JNIEnv* GetJniEnv() {
+ return reinterpret_cast<JNIEnv*>(art::Thread::Current()->GetJniEnv());
+ }
+
+ inline static jthread GetCurrentJThread() {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ return soa.AddLocalReference<jthread>(soa.Self()->GetPeer());
+ }
+
void NextRuntimePhase(RuntimePhase phase) OVERRIDE {
// TODO: Events.
switch (phase) {
@@ -51,16 +62,28 @@
PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL;
break;
case RuntimePhase::kStart:
+ event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv());
PhaseUtil::current_phase_ = JVMTI_PHASE_START;
break;
case RuntimePhase::kInit:
- PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
+ {
+ ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread());
+ event_handler->DispatchEvent(nullptr,
+ ArtJvmtiEvent::kVmInit,
+ GetJniEnv(),
+ thread.get());
+ PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
+ }
break;
case RuntimePhase::kDeath:
+ event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmDeath, GetJniEnv());
PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD;
+ // TODO: Block events now.
break;
}
}
+
+ EventHandler* event_handler = nullptr;
};
PhaseUtil::PhaseCallback gPhaseCallback;
@@ -94,7 +117,8 @@
PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
}
-void PhaseUtil::Register() {
+void PhaseUtil::Register(EventHandler* handler) {
+ gPhaseCallback.event_handler = handler;
art::ScopedThreadStateChange stsc(art::Thread::Current(),
art::ThreadState::kWaitingForDebuggerToAttach);
art::ScopedSuspendAll ssa("Add phase callback");
diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h
index 3cbda5f..054652a 100644
--- a/runtime/openjdkjvmti/ti_phase.h
+++ b/runtime/openjdkjvmti/ti_phase.h
@@ -37,11 +37,13 @@
namespace openjdkjvmti {
+class EventHandler;
+
class PhaseUtil {
public:
static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr);
- static void Register();
+ static void Register(EventHandler* event_handler);
// Move the phase from unitialized to LOAD.
static void SetToOnLoad();
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc
index 3a7d1a1..0b17656 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -28,6 +28,46 @@
namespace art {
namespace Test901HelloTi {
+static void EnableEvent(jvmtiEnv* env, jvmtiEvent evt) {
+ jvmtiError error = env->SetEventNotificationMode(JVMTI_ENABLE, evt, nullptr);
+ if (error != JVMTI_ERROR_NONE) {
+ printf("Failed to enable event");
+ }
+}
+
+static void JNICALL VMStartCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED) {
+ printf("VMStart\n");
+}
+
+static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED,
+ jthread thread ATTRIBUTE_UNUSED) {
+ printf("VMInit\n");
+}
+
+static void JNICALL VMDeatchCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED) {
+ printf("VMDeath\n");
+}
+
+
+static void InstallVMEvents(jvmtiEnv* env) {
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.VMStart = VMStartCallback;
+ callbacks.VMInit = VMInitCallback;
+ callbacks.VMDeath = VMDeatchCallback;
+ jvmtiError ret = env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+ if (ret != JVMTI_ERROR_NONE) {
+ printf("Failed to install callbacks");
+ }
+
+ EnableEvent(env, JVMTI_EVENT_VM_START);
+ EnableEvent(env, JVMTI_EVENT_VM_INIT);
+ EnableEvent(env, JVMTI_EVENT_VM_DEATH);
+}
+
jint OnLoad(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
void* reserved ATTRIBUTE_UNUSED) {
@@ -72,6 +112,10 @@
printf("Unexpected version number!\n");
return -1;
}
+
+ InstallVMEvents(env);
+ InstallVMEvents(env2);
+
CHECK_CALL_SUCCESS(env->DisposeEnvironment());
CHECK_CALL_SUCCESS(env2->DisposeEnvironment());
#undef CHECK_CALL_SUCCESS
@@ -93,6 +137,8 @@
return 1;
}
+ InstallVMEvents(jvmti_env);
+
return JNI_OK;
}
diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt
index 11c51d0..c4b24cb 100644
--- a/test/901-hello-ti-agent/expected.txt
+++ b/test/901-hello-ti-agent/expected.txt
@@ -1,4 +1,6 @@
Loaded Agent for test 901-hello-ti-agent
+VMStart
+VMInit
Hello, world!
Agent in live phase.
0
@@ -7,3 +9,4 @@
4
8
JVMTI_ERROR_ILLEGAL_ARGUMENT
+VMDeath