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