CTS: Add a JVMTI allocation tracking test
Copy ART run-test for basic tracking functionality to CTS. Change
implementation to compare in code instead of text output.
This reverts commit d4c99081b67c01194b63c4e3bef959b220c3f66a.
Bug: 32072923
Test: m cts
Test: cts-tradefed run cts-dev --module CtsJvmtiTrackingHostTestCases
Change-Id: I105234a5ed5bed66ae3ed7f0cffe99fb6caa9fcf
diff --git a/hostsidetests/jvmti/allocation-tracking/Android.mk b/hostsidetests/jvmti/allocation-tracking/Android.mk
new file mode 100644
index 0000000..c5619ad
--- /dev/null
+++ b/hostsidetests/jvmti/allocation-tracking/Android.mk
@@ -0,0 +1,26 @@
+# Copyright (C) 2014 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CtsJvmtiTrackingHostTestCases
+LOCAL_STATIC_JAVA_LIBRARIES := CtsJvmtiHostTestBase
+LOCAL_MODULE_TAGS := tests
+LOCAL_COMPATIBILITY_SUITE := cts
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/jvmti/allocation-tracking/AndroidTest.xml b/hostsidetests/jvmti/allocation-tracking/AndroidTest.xml
new file mode 100644
index 0000000..fc26aae
--- /dev/null
+++ b/hostsidetests/jvmti/allocation-tracking/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<configuration description="Config for CTS JVMTI test cases">
+ <target_preparer class="android.jvmti.cts.JvmtiPreparer">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsJvmtiTrackingDeviceApp.apk" />
+ <option name="package-name" value="android.jvmti.cts.tracking" />
+ </target_preparer>
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsJvmtiTrackingHostTestCases.jar" />
+ </test>
+</configuration>
diff --git a/hostsidetests/jvmti/allocation-tracking/app/Android.mk b/hostsidetests/jvmti/allocation-tracking/app/Android.mk
new file mode 100644
index 0000000..71ab1b5
--- /dev/null
+++ b/hostsidetests/jvmti/allocation-tracking/app/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_STATIC_JAVA_LIBRARIES := CtsJvmtiDeviceAppBase
+LOCAL_JNI_SHARED_LIBRARIES := libctsjvmtiagent
+LOCAL_MULTILIB := both
+LOCAL_SDK_VERSION := current
+
+# TODO: Refactor. This is the only thing every changing.
+LOCAL_PACKAGE_NAME := CtsJvmtiTrackingDeviceApp
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/jvmti/allocation-tracking/app/AndroidManifest.xml b/hostsidetests/jvmti/allocation-tracking/app/AndroidManifest.xml
new file mode 100755
index 0000000..76d753c
--- /dev/null
+++ b/hostsidetests/jvmti/allocation-tracking/app/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.jvmti.cts.tracking">
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.jvmti.JvmtiActivity" >
+ </activity>
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for JVMTI"
+ android:targetPackage="android.jvmti.cts.tracking" >
+ </instrumentation>
+</manifest>
+
diff --git a/hostsidetests/jvmti/allocation-tracking/app/src/android/jvmti/cts/JvmtiTrackingTest.java b/hostsidetests/jvmti/allocation-tracking/app/src/android/jvmti/cts/JvmtiTrackingTest.java
new file mode 100644
index 0000000..70434d1
--- /dev/null
+++ b/hostsidetests/jvmti/allocation-tracking/app/src/android/jvmti/cts/JvmtiTrackingTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+package android.jvmti.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Check tagging-related functionality.
+ */
+public class JvmtiTrackingTest extends JvmtiTestBase {
+
+ @Before
+ public void setUp() throws Exception {
+ // Bind our native methods.
+ JniBindings.bindAgentJNI("android/jvmti/cts/JvmtiTrackingTest",
+ getClass().getClassLoader());
+
+ prefetchClassNames();
+ }
+
+ // Pre-resolve class names so the strings don't have to be allocated as a side effect of
+ // callback printing.
+ private static void prefetchClassNames() {
+ Object.class.getName();
+ Integer.class.getName();
+ Float.class.getName();
+ Short.class.getName();
+ Byte.class.getName();
+ Double.class.getName();
+ }
+
+ private ArrayList<Object> l = new ArrayList<>(100);
+
+ @Test
+ public void testTracking() throws Exception {
+ // Disable the global registration from OnLoad, to get into a known state.
+ enableAllocationTracking(null, false);
+
+ assertEquals(null, getAndResetAllocationTrackingString());
+
+ // Enable actual logging callback.
+ setupObjectAllocCallback(true);
+
+ enableAllocationTracking(null, true);
+
+ l.add(new Object());
+ l.add(new Integer(1));
+
+ enableAllocationTracking(null, false);
+
+ assertEquals(
+ "ObjectAllocated type java.lang.Object/java.lang.Object size 8#"
+ + "ObjectAllocated type java.lang.Integer/java.lang.Integer size 16#",
+ getAndResetAllocationTrackingString());
+
+ l.add(new Float(1.0f));
+
+ assertEquals(null, getAndResetAllocationTrackingString());
+
+ enableAllocationTracking(Thread.currentThread(), true);
+
+ l.add(new Short((short) 0));
+
+ enableAllocationTracking(Thread.currentThread(), false);
+
+ assertEquals("ObjectAllocated type java.lang.Short/java.lang.Short size 16#",
+ getAndResetAllocationTrackingString());
+
+ l.add(new Byte((byte) 0));
+
+ assertEquals(null, getAndResetAllocationTrackingString());
+
+ testThread(l, true, true);
+
+ l.add(new Byte((byte) 0));
+
+ assertEquals("ObjectAllocated type java.lang.Double/java.lang.Double size 16#",
+ getAndResetAllocationTrackingString());
+
+ testThread(l, true, false);
+
+ assertEquals("ObjectAllocated type java.lang.Double/java.lang.Double size 16#",
+ getAndResetAllocationTrackingString());
+
+ System.out.println("Tracking on different thread");
+
+ testThread(l, false, true);
+
+ l.add(new Byte((byte) 0));
+
+ // Disable actual logging callback and re-enable tracking, so we can keep the event enabled
+ // and
+ // check that shutdown works correctly.
+ setupObjectAllocCallback(false);
+ enableAllocationTracking(null, true);
+
+ assertEquals(null, getAndResetAllocationTrackingString());
+ }
+
+ private static void testThread(final ArrayList<Object> l, final boolean sameThread,
+ final boolean disableTracking) throws Exception {
+ final SimpleBarrier startBarrier = new SimpleBarrier(1);
+ final SimpleBarrier trackBarrier = new SimpleBarrier(1);
+
+ final Thread thisThread = Thread.currentThread();
+
+ Thread t = new Thread() {
+ @Override
+ public void run() {
+ try {
+ startBarrier.dec();
+ trackBarrier.waitFor();
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ System.exit(1);
+ }
+
+ l.add(new Double(0.0));
+
+ if (disableTracking) {
+ enableAllocationTracking(sameThread ? this : thisThread, false);
+ }
+ }
+ };
+
+ t.start();
+ startBarrier.waitFor();
+ enableAllocationTracking(sameThread ? t : Thread.currentThread(), true);
+ trackBarrier.dec();
+
+ t.join();
+ }
+
+ // Our own little barrier, to avoid behind-the-scenes allocations.
+ private static class SimpleBarrier {
+ int count;
+
+ public SimpleBarrier(int i) {
+ count = i;
+ }
+
+ public synchronized void dec() throws Exception {
+ count--;
+ notifyAll();
+ }
+
+ public synchronized void waitFor() throws Exception {
+ while (count != 0) {
+ wait();
+ }
+ }
+ }
+
+ private static native void setupObjectAllocCallback(boolean enable);
+
+ private static native void enableAllocationTracking(Thread thread, boolean enable);
+
+ private static native String getAndResetAllocationTrackingString();
+}
diff --git a/hostsidetests/jvmti/base/app/src/android/jvmti/cts/JniBindings.java b/hostsidetests/jvmti/base/app/src/android/jvmti/cts/JniBindings.java
index 822c1aa..cc473d2 100644
--- a/hostsidetests/jvmti/base/app/src/android/jvmti/cts/JniBindings.java
+++ b/hostsidetests/jvmti/base/app/src/android/jvmti/cts/JniBindings.java
@@ -46,4 +46,9 @@
// Load the given class with the given classloader, and bind all native methods to corresponding
// C methods in the agent. Will abort if any of the steps fail.
public static native void bindAgentJNI(String className, ClassLoader classLoader);
+
+ // General functionality shared between tests.
+ public static native void setTag(Object o, long tag);
+
+ public static native long getTag(Object o);
}
diff --git a/hostsidetests/jvmti/base/jni/Android.mk b/hostsidetests/jvmti/base/jni/Android.mk
index 295f4c8..578bed0 100644
--- a/hostsidetests/jvmti/base/jni/Android.mk
+++ b/hostsidetests/jvmti/base/jni/Android.mk
@@ -28,6 +28,9 @@
# Tagging.
LOCAL_SRC_FILES += tagging.cpp
+# Tracking.
+LOCAL_SRC_FILES += tracking.cpp
+
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
LOCAL_HEADER_LIBRARIES := libopenjdkjvmti_headers
diff --git a/hostsidetests/jvmti/base/jni/tagging.cpp b/hostsidetests/jvmti/base/jni/tagging.cpp
index 7ef996c..1e59e13 100644
--- a/hostsidetests/jvmti/base/jni/tagging.cpp
+++ b/hostsidetests/jvmti/base/jni/tagging.cpp
@@ -28,14 +28,14 @@
namespace jvmti {
namespace tagging {
-extern "C" JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiTaggingTest_setTag(
+extern "C" JNIEXPORT void JNICALL Java_android_jvmti_cts_JniBindings_setTag(
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj, jlong tag) {
jvmtiEnv* jvmti_env = GetJvmtiEnv();
jvmtiError ret = jvmti_env->SetTag(obj, tag);
JvmtiErrorToException(env, jvmti_env, ret);
}
-extern "C" JNIEXPORT jlong JNICALL Java_android_jvmti_cts_JvmtiTaggingTest_getTag(
+extern "C" JNIEXPORT jlong JNICALL Java_android_jvmti_cts_JniBindings_getTag(
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj) {
jvmtiEnv* jvmti_env = GetJvmtiEnv();
jlong tag = 0;
diff --git a/hostsidetests/jvmti/base/jni/tracking.cpp b/hostsidetests/jvmti/base/jni/tracking.cpp
new file mode 100644
index 0000000..a46f491
--- /dev/null
+++ b/hostsidetests/jvmti/base/jni/tracking.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 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 <mutex>
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "common.h"
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
+
+namespace cts {
+namespace jvmti {
+namespace allocation_tracking {
+
+static std::string GetClassName(JNIEnv* jni_env, jclass cls) {
+ ScopedLocalRef<jclass> class_class(jni_env, jni_env->GetObjectClass(cls));
+ jmethodID mid = jni_env->GetMethodID(class_class.get(), "getName", "()Ljava/lang/String;");
+ ScopedLocalRef<jstring> str(
+ jni_env, reinterpret_cast<jstring>(jni_env->CallObjectMethod(cls, mid)));
+ ScopedUtfChars utf_chars(jni_env, str.get());
+ return utf_chars.c_str();
+}
+
+static std::mutex gLock;
+static std::string gCollection;
+
+static void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env,
+ jthread thread ATTRIBUTE_UNUSED,
+ jobject object,
+ jclass object_klass,
+ jlong size) {
+ std::string object_klass_descriptor = GetClassName(jni_env, object_klass);
+ ScopedLocalRef<jclass> object_klass2(jni_env, jni_env->GetObjectClass(object));
+ std::string object_klass_descriptor2 = GetClassName(jni_env, object_klass2.get());
+ std::string result = android::base::StringPrintf("ObjectAllocated type %s/%s size %zu",
+ object_klass_descriptor.c_str(),
+ object_klass_descriptor2.c_str(),
+ static_cast<size_t>(size));
+ std::unique_lock<std::mutex> mu(gLock);
+ gCollection += result + "#";
+}
+
+extern "C" JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiTrackingTest_setupObjectAllocCallback(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.VMObjectAlloc = enable ? ObjectAllocated : nullptr;
+
+ jvmtiError ret = GetJvmtiEnv()->SetEventCallbacks(&callbacks, sizeof(callbacks));
+ JvmtiErrorToException(env, GetJvmtiEnv(), ret);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiTrackingTest_enableAllocationTracking(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jboolean enable) {
+ jvmtiError ret = GetJvmtiEnv()->SetEventNotificationMode(
+ enable ? JVMTI_ENABLE : JVMTI_DISABLE,
+ JVMTI_EVENT_VM_OBJECT_ALLOC,
+ thread);
+ JvmtiErrorToException(env, GetJvmtiEnv(), ret);
+}
+
+extern "C" JNIEXPORT
+jstring JNICALL Java_android_jvmti_cts_JvmtiTrackingTest_getAndResetAllocationTrackingString(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ // We will have a string allocation. So only do the C++ string retrieval under lock.
+ std::string result;
+ {
+ std::unique_lock<std::mutex> mu(gLock);
+ result.swap(gCollection);
+ }
+
+ if (result.empty()) {
+ return nullptr;
+ }
+
+ return env->NewStringUTF(result.c_str());
+}
+
+} // namespace allocation_tracking
+} // namespace jvmti
+} // namespace cts
diff --git a/hostsidetests/jvmti/tagging/app/src/android/jvmti/cts/JvmtiTaggingTest.java b/hostsidetests/jvmti/tagging/app/src/android/jvmti/cts/JvmtiTaggingTest.java
index d2cfe96..6ba9376 100644
--- a/hostsidetests/jvmti/tagging/app/src/android/jvmti/cts/JvmtiTaggingTest.java
+++ b/hostsidetests/jvmti/tagging/app/src/android/jvmti/cts/JvmtiTaggingTest.java
@@ -37,28 +37,28 @@
private static WeakReference<Object> test() {
Object o1 = new Object();
- setTag(o1, 1);
+ JniBindings.setTag(o1, 1);
Object o2 = new Object();
- setTag(o2, 2);
+ JniBindings.setTag(o2, 2);
- assertEquals(1, getTag(o1));
- assertEquals(2, getTag(o2));
+ assertEquals(1, JniBindings.getTag(o1));
+ assertEquals(2, JniBindings.getTag(o2));
Runtime.getRuntime().gc();
Runtime.getRuntime().gc();
- assertEquals(1, getTag(o1));
- assertEquals(2, getTag(o2));
+ assertEquals(1, JniBindings.getTag(o1));
+ assertEquals(2, JniBindings.getTag(o2));
Runtime.getRuntime().gc();
Runtime.getRuntime().gc();
- setTag(o1, 10);
- setTag(o2, 20);
+ JniBindings.setTag(o1, 10);
+ JniBindings.setTag(o2, 20);
- assertEquals(10, getTag(o1));
- assertEquals(20, getTag(o2));
+ assertEquals(10, JniBindings.getTag(o1));
+ assertEquals(20, JniBindings.getTag(o2));
return new WeakReference<Object>(o1);
}
@@ -93,7 +93,7 @@
Integer o = new Integer(i);
l.add(o);
if (i % 10 != 0) {
- setTag(o, i % 10);
+ JniBindings.setTag(o, i % 10);
}
}
@@ -208,6 +208,7 @@
}
@Override
+ @SuppressWarnings("unchecked")
public int compareTo(Pair p) {
if (tag != p.tag) {
return Long.compare(tag, p.tag);
@@ -242,11 +243,6 @@
}
}
-
- private static native void setTag(Object o, long tag);
-
- private static native long getTag(Object o);
-
private static native Object[] getTaggedObjects(long[] searchTags, boolean returnObjects,
boolean returnTags);
}