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