blob: e1a18610bce10bfebb3cecef3fc4a748ca833b93 [file] [log] [blame]
/*
* Copyright (C) 2019 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 art;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class Test1974 {
public static final boolean DEBUG = false;
public static int[] static_field = new int[] {1, 2, 3};
public static Object[] static_field_ref = new String[] {"a", "b", "c"};
public static final class InstanceClass {
public int[] instance_field = new int[] {1, 2, 3};
public Object[] self_ref;
public InstanceClass() {
self_ref = new Object[] {null, "A", "B", "C"};
self_ref[0] = self_ref;
}
}
static InstanceClass theInstanceClass;
static InstanceClass theOtherInstanceClass;
static {
theInstanceClass = new InstanceClass();
theOtherInstanceClass = new InstanceClass();
theOtherInstanceClass.instance_field = theInstanceClass.instance_field;
theOtherInstanceClass.self_ref = theInstanceClass.self_ref;
}
public static void DbgPrintln(String s) {
if (DEBUG) {
System.out.println(s);
}
}
public interface ThrowRunnable extends Runnable {
public default void run() {
try {
throwRun();
} catch (Exception e) {
throw new Error("Exception in runner!", e);
}
}
public void throwRun() throws Exception;
}
public static void runAsThread(ThrowRunnable r) throws Exception {
Thread t = new Thread(r);
t.start();
t.join();
System.out.println("");
}
public static void runInstance() {
System.out.println("Test instance");
DbgPrintln("Pre hash: " + theInstanceClass.instance_field.hashCode());
System.out.println(
"val is: " + Arrays.toString(theInstanceClass.instance_field) + " resize +3");
ResizeArray(() -> theInstanceClass.instance_field, theInstanceClass.instance_field.length + 5);
System.out.println("val is: " + Arrays.toString(theInstanceClass.instance_field));
DbgPrintln("Post hash: " + theInstanceClass.instance_field.hashCode());
System.out.println(
"Same value? " + (theInstanceClass.instance_field == theOtherInstanceClass.instance_field));
}
public static void runHashMap() {
System.out.println("Test HashMap");
HashMap<byte[], Comparable> map = new HashMap();
Comparable the_value = "THE VALUE";
Supplier<byte[]> get_the_value =
() ->
map.entrySet().stream()
.filter((x) -> x.getValue().equals(the_value))
.findFirst()
.get()
.getKey();
map.put(new byte[] {1, 2, 3, 4}, the_value);
map.put(new byte[] {1, 2, 3, 4}, "Other Value");
map.put(new byte[] {1, 4}, "Third value");
System.out.println("val is: " + Arrays.toString(get_the_value.get()) + " resize +3");
System.out.print("Map is: ");
map.entrySet().stream()
.sorted((x, y) -> x.getValue().compareTo(y.getValue()))
.forEach(
(e) -> {
System.out.print("(" + Arrays.toString(e.getKey()) + "->" + e.getValue() + "), ");
});
System.out.println();
ResizeArray(get_the_value, 7);
System.out.println("val is: " + Arrays.toString(get_the_value.get()));
System.out.print("Map is: ");
map.entrySet().stream()
.sorted((x, y) -> x.getValue().compareTo(y.getValue()))
.forEach(
(e) -> {
System.out.print("(" + Arrays.toString(e.getKey()) + "->" + e.getValue() + "), ");
});
System.out.println();
}
public static void runWeakReference() {
System.out.println("Test j.l.r.WeakReference");
String[] arr = new String[] {"weak", "ref"};
WeakReference<String[]> wr = new WeakReference(arr);
DbgPrintln("Pre hash: " + wr.get().hashCode());
System.out.println("val is: " + Arrays.toString(wr.get()) + " resize +3");
ResizeArray(wr::get, wr.get().length + 5);
System.out.println("val is: " + Arrays.toString(wr.get()));
DbgPrintln("Post hash: " + wr.get().hashCode());
System.out.println("Same value? " + (wr.get() == arr));
}
public static void runInstanceSelfRef() {
System.out.println("Test instance self-ref");
DbgPrintln("Pre hash: " + Integer.toHexString(theInstanceClass.self_ref.hashCode()));
String pre_to_string = theInstanceClass.self_ref.toString();
System.out.println(
"val is: "
+ Arrays.toString(theInstanceClass.self_ref).replace(pre_to_string, "<SELF REF>")
+ " resize +5 item 0 is "
+ Arrays.toString((Object[]) theInstanceClass.self_ref[0])
.replace(pre_to_string, "<SELF REF>"));
ResizeArray(() -> theInstanceClass.self_ref, theInstanceClass.self_ref.length + 5);
System.out.println(
"val is: "
+ Arrays.toString(theInstanceClass.self_ref).replace(pre_to_string, "<SELF REF>"));
System.out.println(
"val is: "
+ Arrays.toString((Object[]) theInstanceClass.self_ref[0])
.replace(pre_to_string, "<SELF REF>"));
DbgPrintln("Post hash: " + Integer.toHexString(theInstanceClass.self_ref.hashCode()));
System.out.println(
"Same value? " + (theInstanceClass.self_ref == theOtherInstanceClass.self_ref));
System.out.println(
"Same structure? " + (theInstanceClass.self_ref == theInstanceClass.self_ref[0]));
System.out.println(
"Same inner-structure? "
+ (theInstanceClass.self_ref[0] == ((Object[]) theInstanceClass.self_ref[0])[0]));
}
public static void runInstanceSelfRefSmall() {
System.out.println("Test instance self-ref smaller");
DbgPrintln("Pre hash: " + Integer.toHexString(theInstanceClass.self_ref.hashCode()));
String pre_to_string = theInstanceClass.self_ref.toString();
System.out.println(
"val is: "
+ Arrays.toString(theInstanceClass.self_ref).replace(pre_to_string, "<SELF REF>")
+ " resize -7 item 0 is "
+ Arrays.toString((Object[]) theInstanceClass.self_ref[0])
.replace(pre_to_string, "<SELF REF>"));
ResizeArray(() -> theInstanceClass.self_ref, theInstanceClass.self_ref.length - 7);
System.out.println(
"val is: "
+ Arrays.toString(theInstanceClass.self_ref).replace(pre_to_string, "<SELF REF>"));
System.out.println(
"val is: "
+ Arrays.toString((Object[]) theInstanceClass.self_ref[0])
.replace(pre_to_string, "<SELF REF>"));
DbgPrintln("Post hash: " + Integer.toHexString(theInstanceClass.self_ref.hashCode()));
System.out.println(
"Same value? " + (theInstanceClass.self_ref == theOtherInstanceClass.self_ref));
System.out.println(
"Same structure? " + (theInstanceClass.self_ref == theInstanceClass.self_ref[0]));
System.out.println(
"Same inner-structure? "
+ (theInstanceClass.self_ref[0] == ((Object[]) theInstanceClass.self_ref[0])[0]));
}
public static void runLocal() throws Exception {
final int[] arr_loc = new int[] {2, 3, 4};
int[] arr_loc_2 = arr_loc;
System.out.println("Test local");
DbgPrintln("Pre hash: " + arr_loc.hashCode());
System.out.println("val is: " + Arrays.toString(arr_loc) + " resize +5");
ResizeArray(() -> arr_loc, arr_loc.length + 5);
System.out.println("val is: " + Arrays.toString(arr_loc));
DbgPrintln("Post hash: " + arr_loc.hashCode());
System.out.println("Same value? " + (arr_loc == arr_loc_2));
}
public static void runLocalSmall() throws Exception {
final int[] arr_loc = new int[] {1, 2, 3, 4, 5};
int[] arr_loc_2 = arr_loc;
System.out.println("Test local smaller");
DbgPrintln("Pre hash: " + arr_loc.hashCode());
System.out.println("val is: " + Arrays.toString(arr_loc) + " resize -2");
ResizeArray(() -> arr_loc, arr_loc.length - 2);
System.out.println("val is: " + Arrays.toString(arr_loc));
DbgPrintln("Post hash: " + arr_loc.hashCode());
System.out.println("Same value? " + (arr_loc == arr_loc_2));
}
public static void runMultiThreadLocal() throws Exception {
final CountDownLatch cdl = new CountDownLatch(1);
final CountDownLatch start_cdl = new CountDownLatch(2);
final Supplier<Object[]> getArr =
new Supplier<Object[]>() {
public final Object[] arr = new Object[] {"1", "2", "3"};
public Object[] get() {
return arr;
}
};
final ArrayList<String> msg1 = new ArrayList();
final ArrayList<String> msg2 = new ArrayList();
final Consumer<String> print1 =
(String s) -> {
msg1.add(s);
};
final Consumer<String> print2 =
(String s) -> {
msg2.add(s);
};
Function<Consumer<String>, Runnable> r =
(final Consumer<String> c) ->
() -> {
c.accept("Test local multi-thread");
Object[] arr_loc = getArr.get();
Object[] arr_loc_2 = getArr.get();
DbgPrintln("Pre hash: " + arr_loc.hashCode());
c.accept("val is: " + Arrays.toString(arr_loc) + " resize -2");
try {
start_cdl.countDown();
cdl.await();
} catch (Exception e) {
throw new Error("failed await", e);
}
c.accept("val is: " + Arrays.toString(arr_loc));
DbgPrintln("Post hash: " + arr_loc.hashCode());
c.accept("Same value? " + (arr_loc == arr_loc_2));
};
Thread t1 = new Thread(r.apply(print1));
Thread t2 = new Thread(r.apply(print2));
t1.start();
t2.start();
start_cdl.await();
ResizeArray(getArr, 1);
cdl.countDown();
t1.join();
t2.join();
for (String s : msg1) {
System.out.println("T1: " + s);
}
for (String s : msg2) {
System.out.println("T2: " + s);
}
}
public static void runWithLocks() throws Exception {
final CountDownLatch cdl = new CountDownLatch(1);
final CountDownLatch start_cdl = new CountDownLatch(2);
final CountDownLatch waiter_start_cdl = new CountDownLatch(1);
final Supplier<Object[]> getArr =
new Supplier<Object[]>() {
public final Object[] arr = new Object[] {"A", "2", "C"};
public Object[] get() {
return arr;
}
};
// basic order of operations noted above each line.
// Waiter runs to the 'wait' then t1 runs to the cdl.await, then current thread runs.
Runnable r =
() -> {
System.out.println("Test locks");
Object[] arr_loc = getArr.get();
Object[] arr_loc_2 = getArr.get();
DbgPrintln("Pre hash: " + arr_loc.hashCode());
System.out.println("val is: " + Arrays.toString(arr_loc) + " resize -2");
try {
// OP 1
waiter_start_cdl.await();
// OP 6
synchronized (arr_loc) {
// OP 7
synchronized (arr_loc_2) {
// OP 8
start_cdl.countDown();
// OP 9
cdl.await();
// OP 13
}
}
} catch (Exception e) {
throw new Error("failed await", e);
}
System.out.println("val is: " + Arrays.toString(arr_loc));
DbgPrintln("Post hash: " + arr_loc.hashCode());
System.out.println("Same value? " + (arr_loc == arr_loc_2));
};
Thread t1 = new Thread(r);
Thread waiter =
new Thread(
() -> {
try {
Object a = getArr.get();
// OP 2
synchronized (a) {
// OP 3
waiter_start_cdl.countDown();
// OP 4
start_cdl.countDown();
// OP 5
a.wait();
// OP 15
}
} catch (Exception e) {
throw new Error("Failed wait!", e);
}
});
waiter.start();
t1.start();
// OP 10
start_cdl.await();
// OP 11
ResizeArray(getArr, 1);
// OP 12
cdl.countDown();
// OP 14
synchronized (getArr.get()) {
// Make sure thread wakes up and has the right lock.
getArr.get().notifyAll();
}
waiter.join();
t1.join();
// Make sure other threads can still lock it.
synchronized (getArr.get()) {
}
System.out.println("Locks seem to all work.");
}
public static void runWithJniGlobal() throws Exception {
Object[] arr = new Object[] {"1", "11", "111"};
final long globalID = GetGlobalJniRef(arr);
System.out.println("Test jni-ref");
DbgPrintln("Pre hash: " + ReadJniRef(globalID).hashCode());
System.out.println(
"val is: " + Arrays.toString((Object[]) ReadJniRef(globalID)) + " resize +5");
ResizeArray(() -> ReadJniRef(globalID), ((Object[]) ReadJniRef(globalID)).length + 5);
System.out.println("val is: " + Arrays.toString((Object[]) ReadJniRef(globalID)));
DbgPrintln("Post hash: " + ReadJniRef(globalID).hashCode());
System.out.println("Same value? " + (ReadJniRef(globalID) == arr));
}
public static void runWithJniWeakGlobal() throws Exception {
Object[] arr = new Object[] {"2", "22", "222"};
final long globalID = GetWeakGlobalJniRef(arr);
System.out.println("Test weak jni-ref");
DbgPrintln("Pre hash: " + ReadJniRef(globalID).hashCode());
System.out.println(
"val is: " + Arrays.toString((Object[]) ReadJniRef(globalID)) + " resize +5");
ResizeArray(() -> ReadJniRef(globalID), ((Object[]) ReadJniRef(globalID)).length + 5);
System.out.println("val is: " + Arrays.toString((Object[]) ReadJniRef(globalID)));
DbgPrintln("Post hash: " + ReadJniRef(globalID).hashCode());
System.out.println("Same value? " + (ReadJniRef(globalID) == arr));
if (ReadJniRef(globalID) != arr) {
throw new Error("Didn't update weak global!");
}
}
public static void runWithJniLocals() throws Exception {
final Object[] arr = new Object[] {"3", "32", "322"};
System.out.println("Test jni local ref");
Consumer<Object> checker = (o) -> System.out.println("Same value? " + (o == arr));
Consumer<Object> printer =
(o) -> System.out.println("val is: " + Arrays.toString((Object[]) o));
Runnable resize =
() -> {
System.out.println("Resize +4");
ResizeArray(() -> arr, arr.length + 4);
};
runNativeTest(arr, resize, printer, checker);
}
public static native void runNativeTest(
Object[] arr, Runnable resize, Consumer<Object> printer, Consumer<Object> checker);
public static void runWithJvmtiTags() throws Exception {
Object[] arr = new Object[] {"3", "33", "333"};
long globalID = 333_333_333l;
Main.setTag(arr, globalID);
System.out.println("Test jvmti-tags");
DbgPrintln("Pre hash: " + arr.hashCode());
System.out.println(
"val is: " + Arrays.deepToString(GetObjectsWithTag(globalID)) + " resize +5");
ResizeArray(() -> arr, arr.length + 5);
Object[] after_tagged_obj = GetObjectsWithTag(globalID);
System.out.println("val is: " + Arrays.deepToString(GetObjectsWithTag(globalID)));
DbgPrintln("Post hash: " + after_tagged_obj[0].hashCode());
System.out.println("Same value? " + (after_tagged_obj[0] == arr));
}
public static void runWithJvmtiTagsObsolete() throws Exception {
Object[] arr = new Object[] {"4", "44", "444"};
long globalID = 444_444_444l;
System.out.println("Test jvmti-tags with obsolete");
Main.setTag(arr, globalID);
StartCollectFrees();
StartAssignObsoleteIncrementedId();
DbgPrintln("Pre hash: " + arr.hashCode());
System.out.println(
"val is: " + Arrays.deepToString(GetObjectsWithTag(globalID)) + " resize +5");
ResizeArray(() -> arr, arr.length + 5);
Object[] after_tagged_obj = GetObjectsWithTag(globalID);
Object[] obsolete_tagged_obj = GetObjectsWithTag(globalID + 1);
System.out.println("val is: " + Arrays.deepToString(GetObjectsWithTag(globalID)));
EndAssignObsoleteIncrementedId();
long[] obsoletes_freed = CollectFreedTags();
DbgPrintln("Post hash: " + after_tagged_obj[0].hashCode());
System.out.println("Same value? " + (after_tagged_obj[0] == arr));
if (obsolete_tagged_obj.length >= 1) {
DbgPrintln("Found objects with obsolete tag: " + Arrays.deepToString(obsolete_tagged_obj));
boolean bad = false;
if (obsolete_tagged_obj.length != 1) {
System.out.println(
"Found obsolete tag'd objects: "
+ Arrays.deepHashCode(obsolete_tagged_obj)
+ " but only expected one!");
bad = true;
}
if (!Arrays.deepEquals(
Arrays.copyOf(arr, ((Object[]) obsolete_tagged_obj[0]).length),
(Object[]) obsolete_tagged_obj[0])) {
System.out.println("Obsolete array was unexpectedly different than non-obsolete one!");
bad = true;
}
if (!Arrays.stream(obsoletes_freed).anyMatch((l) -> l == globalID + 1)) {
DbgPrintln("Didn't see a free of the obsolete id");
}
if (!bad) {
System.out.println("Everything looks good WRT obsolete object!");
}
} else {
if (!Arrays.stream(obsoletes_freed).anyMatch((l) -> l == globalID + 1)) {
System.out.println("Didn't see a free of the obsolete id");
} else {
DbgPrintln("Saw a free of obsolete id!");
System.out.println("Everything looks good WRT obsolete object!");
}
}
}
public static void run() throws Exception {
// Simple
runAsThread(Test1974::runInstance);
// HashMap
runAsThread(Test1974::runHashMap);
// j.l.ref.WeakReference
runAsThread(Test1974::runWeakReference);
// Self-referential arrays.
runAsThread(Test1974::runInstanceSelfRef);
runAsThread(Test1974::runInstanceSelfRefSmall);
// Local variables simple
runAsThread(Test1974::runLocal);
runAsThread(Test1974::runLocalSmall);
// multiple threads local variables
runAsThread(Test1974::runMultiThreadLocal);
// using as monitors and waiting
runAsThread(Test1974::runWithLocks);
// Basic jni global refs
runAsThread(Test1974::runWithJniGlobal);
// Basic jni weak global refs
runAsThread(Test1974::runWithJniWeakGlobal);
// Basic JNI local refs
runAsThread(Test1974::runWithJniLocals);
// Basic jvmti tags
runAsThread(Test1974::runWithJvmtiTags);
// Grab obsolete reference using tags/detect free
runAsThread(Test1974::runWithJvmtiTagsObsolete);
}
// Use a supplier so that we don't have to have a local ref to the resized
// array if we don't want it
public static native <T> void ResizeArray(Supplier<T> arr, int new_size);
public static native <T> long GetGlobalJniRef(T t);
public static native <T> long GetWeakGlobalJniRef(T t);
public static native <T> T ReadJniRef(long t);
public static native Object[] GetObjectsWithTag(long tag);
public static native void StartCollectFrees();
public static native void StartAssignObsoleteIncrementedId();
public static native void EndAssignObsoleteIncrementedId();
public static native long[] CollectFreedTags();
}