Add libgolemtiagent.so to set breakpoints on golem benchmarks

We are updating debuggable-cc configuration in golem benchmarks to
measure the performance of debugger. As a first step, we are adding
a breakpoint to measure the overhead of instrumentation related code.
To achieve this we need a tiagent to be able to set a breakpoint. This
CL adds a minimal ti agent that could be used in golem benchmarks.

The changes in this CL:
1. Adds Agent_OnLoad callback which installs a listener for VM_INIT
   event.
2. On the VM_INIT callback we set a breakpoint on Thread.stop which is a
   deprecated method and not expected to be used much.
3. Adds rules to build libgolemtiagent.so for build-art-host-golem
   target.

Bug: 232212577
Test: m build-art-host-golem
Change-Id: I2da3770ff85edb46da64d09ec0047ec6e7564fd0
diff --git a/Android.mk b/Android.mk
index f199f5a..041c23c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -706,6 +706,7 @@
                         $(TARGET_OUT_EXECUTABLES)/dex2oat_wrapper \
                         $(ART_TARGET_PLATFORM_DEPENDENCIES) \
                         $(ART_TARGET_SHARED_LIBRARY_BENCHMARK) \
+			$(TARGET_OUT_SHARED_LIBRARIES)/libgolemtiagent.so \
                         $(PRODUCT_OUT)/apex/art_boot_images/javalib/$(TARGET_ARCH)/boot.art \
                         standalone-apex-files
 	# remove debug libraries from public.libraries.txt because golem builds
@@ -723,6 +724,7 @@
 ART_HOST_SHARED_LIBRARY_BENCHMARK := $(ART_HOST_OUT_SHARED_LIBRARIES)/libartbenchmark.so
 build-art-host-golem: build-art-host \
                       $(ART_HOST_SHARED_LIBRARY_BENCHMARK) \
+		      $(ART_HOST_OUT_SHARED_LIBRARIES)/libgolemtiagent.so \
                       $(HOST_OUT_EXECUTABLES)/dex2oat_wrapper
 
 ########################################################################
diff --git a/benchmark/Android.bp b/benchmark/Android.bp
index 43f46c3..c7f9f6b 100644
--- a/benchmark/Android.bp
+++ b/benchmark/Android.bp
@@ -83,3 +83,30 @@
         },
     },
 }
+
+art_cc_library {
+    name: "libgolemtiagent",
+    host_supported: true,
+    defaults: ["art_defaults"],
+    srcs: [
+        "golem-tiagent/golem-tiagent.cc",
+    ],
+    target: {
+        // This has to be duplicated for android and host to make sure it
+        // comes after the -Wframe-larger-than warnings inserted by art.go
+        // target-specific properties
+        android: {
+            cflags: ["-Wno-frame-larger-than="],
+        },
+        host: {
+            cflags: ["-Wno-frame-larger-than="],
+        },
+    },
+    header_libs: [
+        "libnativehelper_header_only",
+	"libopenjdkjvmti_headers"
+    ],
+    shared_libs: [
+     "libbase",
+    ],
+}
diff --git a/benchmark/golem-tiagent/golem-tiagent.cc b/benchmark/golem-tiagent/golem-tiagent.cc
new file mode 100644
index 0000000..be2c727
--- /dev/null
+++ b/benchmark/golem-tiagent/golem-tiagent.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/macros.h"
+#include "android-base/logging.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace art {
+
+jvmtiEnv* jvmti_env = nullptr;
+
+void CheckJvmtiError(jvmtiEnv* env, jvmtiError error) {
+  if (error != JVMTI_ERROR_NONE) {
+    char* error_name;
+    jvmtiError name_error = env->GetErrorName(error, &error_name);
+    if (name_error != JVMTI_ERROR_NONE) {
+      LOG(FATAL) << "Unable to get error name for " << error;
+    }
+    LOG(FATAL) << "Unexpected error: " << error_name;
+  }
+}
+
+static void JNICALL VMInitCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+                                   JNIEnv* jni_env,
+                                   jthread thread ATTRIBUTE_UNUSED) {
+  // Set a breakpoint on a rare method that we won't expect to be hit.
+  // java.lang.Thread.stop is deprecated and not expected to be used.
+  jclass cl = jni_env->FindClass("java/lang/Thread");
+  if (cl == nullptr) {
+    LOG(FATAL) << "Cannot find class java/lang/Thread to set a breakpoint";
+  }
+
+  jmethodID method = jni_env->GetMethodID(cl, "stop", "()V");
+  if (method == nullptr) {
+    LOG(FATAL) << "Cannot find method to set a breapoint";
+  }
+
+  jlong start = 0;
+  jlong end;
+  CheckJvmtiError(jvmti_env, jvmti_env->GetMethodLocation(method, &start, &end));
+  CheckJvmtiError(jvmti_env, jvmti_env->SetBreakpoint(method, start));
+}
+
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
+                                               char* options ATTRIBUTE_UNUSED,
+                                               void* reserved ATTRIBUTE_UNUSED) {
+  // Setup jvmti_env
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0) != 0) {
+    LOG(ERROR) << "Unable to get jvmti env!";
+    return 1;
+  }
+
+  // Enable breakpoint capability
+  jvmtiCapabilities capabilities;
+  memset(&capabilities, 0, sizeof(capabilities));
+  capabilities.can_generate_breakpoint_events = 1;
+  CheckJvmtiError(jvmti_env, jvmti_env->AddCapabilities(&capabilities));
+
+  // Set a callback for VM_INIT phase so we can set a breakpoint. We cannot just
+  // set a breakpoint here since vm isn't fully initialized here.
+  jvmtiEventCallbacks callbacks;
+  memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+  callbacks.VMInit = VMInitCallback;
+  CheckJvmtiError(jvmti_env, jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)));
+  CheckJvmtiError(jvmti_env,
+                  jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, nullptr));
+
+  return 0;
+}
+
+}  // namespace art