ART: Add ThreadStart & ThreadEnd

Add support for ThreadStart and ThreadEnd events. Add tests.

Bug: 31684920
Test: m test-art-host-run-test-924-threads
Change-Id: I516993402747ffdc9a7d66985b21b95c059be107
diff --git a/test/924-threads/expected.txt b/test/924-threads/expected.txt
index 3b7fb24..67d20eb 100644
--- a/test/924-threads/expected.txt
+++ b/test/924-threads/expected.txt
@@ -31,3 +31,7 @@
 [Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system], Thread[main,5,main]]
 JVMTI_ERROR_THREAD_NOT_ALIVE
 JVMTI_ERROR_THREAD_NOT_ALIVE
+Constructed thread
+Thread(EventTestThread): start
+Thread(EventTestThread): end
+Thread joined
diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java
index 58695f7..29c4aa3 100644
--- a/test/924-threads/src/Main.java
+++ b/test/924-threads/src/Main.java
@@ -56,6 +56,8 @@
     doAllThreadsTests();
 
     doTLSTests();
+
+    doTestEvents();
   }
 
   private static class Holder {
@@ -226,6 +228,22 @@
     }
   }
 
+  private static void doTestEvents() throws Exception {
+    enableThreadEvents(true);
+
+    Thread t = new Thread("EventTestThread");
+
+    System.out.println("Constructed thread");
+    Thread.yield();
+
+    t.start();
+    t.join();
+
+    System.out.println("Thread joined");
+
+    enableThreadEvents(false);
+  }
+
   private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
     public int compare(Thread o1, Thread o2) {
       return o1.getName().compareTo(o2.getName());
@@ -293,4 +311,5 @@
   private static native Thread[] getAllThreads();
   private static native void setTLS(Thread t, long l);
   private static native long getTLS(Thread t);
+  private static native void enableThreadEvents(boolean b);
 }
diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc
index d35eaa8..0380433 100644
--- a/test/924-threads/threads.cc
+++ b/test/924-threads/threads.cc
@@ -137,5 +137,71 @@
   JvmtiErrorToException(env, result);
 }
 
+static void JNICALL ThreadEvent(jvmtiEnv* jvmti_env,
+                                JNIEnv* jni_env,
+                                jthread thread,
+                                bool is_start) {
+  jvmtiThreadInfo info;
+  jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
+  if (result != JVMTI_ERROR_NONE) {
+    printf("Error getting thread info");
+    return;
+  }
+  printf("Thread(%s): %s\n", info.name, is_start ? "start" : "end");
+
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name));
+  jni_env->DeleteLocalRef(info.thread_group);
+  jni_env->DeleteLocalRef(info.context_class_loader);
+}
+
+static void JNICALL ThreadStart(jvmtiEnv* jvmti_env,
+                                JNIEnv* jni_env,
+                                jthread thread) {
+  ThreadEvent(jvmti_env, jni_env, thread, true);
+}
+
+static void JNICALL ThreadEnd(jvmtiEnv* jvmti_env,
+                              JNIEnv* jni_env,
+                              jthread thread) {
+  ThreadEvent(jvmti_env, jni_env, thread, false);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_enableThreadEvents(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
+  if (b == JNI_FALSE) {
+    jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+                                                         JVMTI_EVENT_THREAD_START,
+                                                         nullptr);
+    if (JvmtiErrorToException(env, ret)) {
+      return;
+    }
+    ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+                                              JVMTI_EVENT_THREAD_END,
+                                              nullptr);
+    JvmtiErrorToException(env, ret);
+    return;
+  }
+
+  jvmtiEventCallbacks callbacks;
+  memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+  callbacks.ThreadStart = ThreadStart;
+  callbacks.ThreadEnd = ThreadEnd;
+  jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+  if (JvmtiErrorToException(env, ret)) {
+    return;
+  }
+
+  ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+                                            JVMTI_EVENT_THREAD_START,
+                                            nullptr);
+  if (JvmtiErrorToException(env, ret)) {
+    return;
+  }
+  ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+                                            JVMTI_EVENT_THREAD_END,
+                                            nullptr);
+  JvmtiErrorToException(env, ret);
+}
+
 }  // namespace Test924Threads
 }  // namespace art