blob: e1a18610bce10bfebb3cecef3fc4a748ca833b93 [file] [log] [blame]
Alex Lightc14ec8f2019-07-18 16:08:41 -07001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package art;
18
19import java.lang.ref.WeakReference;
20import java.util.ArrayList;
21import java.util.Arrays;
22import java.util.HashMap;
23import java.util.concurrent.CountDownLatch;
24import java.util.function.Consumer;
25import java.util.function.Function;
26import java.util.function.Supplier;
27
28public class Test1974 {
29
30 public static final boolean DEBUG = false;
31
32 public static int[] static_field = new int[] {1, 2, 3};
33 public static Object[] static_field_ref = new String[] {"a", "b", "c"};
34
35 public static final class InstanceClass {
36 public int[] instance_field = new int[] {1, 2, 3};
37 public Object[] self_ref;
38
39 public InstanceClass() {
40 self_ref = new Object[] {null, "A", "B", "C"};
41 self_ref[0] = self_ref;
42 }
43 }
44
45 static InstanceClass theInstanceClass;
46 static InstanceClass theOtherInstanceClass;
47
48 static {
49 theInstanceClass = new InstanceClass();
50 theOtherInstanceClass = new InstanceClass();
51 theOtherInstanceClass.instance_field = theInstanceClass.instance_field;
52 theOtherInstanceClass.self_ref = theInstanceClass.self_ref;
53 }
54
55 public static void DbgPrintln(String s) {
56 if (DEBUG) {
57 System.out.println(s);
58 }
59 }
60
61 public interface ThrowRunnable extends Runnable {
62 public default void run() {
63 try {
64 throwRun();
65 } catch (Exception e) {
66 throw new Error("Exception in runner!", e);
67 }
68 }
69
70 public void throwRun() throws Exception;
71 }
72
73 public static void runAsThread(ThrowRunnable r) throws Exception {
74 Thread t = new Thread(r);
75 t.start();
76 t.join();
77 System.out.println("");
78 }
79
80 public static void runInstance() {
81 System.out.println("Test instance");
82 DbgPrintln("Pre hash: " + theInstanceClass.instance_field.hashCode());
83 System.out.println(
84 "val is: " + Arrays.toString(theInstanceClass.instance_field) + " resize +3");
85 ResizeArray(() -> theInstanceClass.instance_field, theInstanceClass.instance_field.length + 5);
86 System.out.println("val is: " + Arrays.toString(theInstanceClass.instance_field));
87 DbgPrintln("Post hash: " + theInstanceClass.instance_field.hashCode());
88 System.out.println(
89 "Same value? " + (theInstanceClass.instance_field == theOtherInstanceClass.instance_field));
90 }
91
92 public static void runHashMap() {
93 System.out.println("Test HashMap");
94 HashMap<byte[], Comparable> map = new HashMap();
95 Comparable the_value = "THE VALUE";
96 Supplier<byte[]> get_the_value =
97 () ->
98 map.entrySet().stream()
99 .filter((x) -> x.getValue().equals(the_value))
100 .findFirst()
101 .get()
102 .getKey();
103 map.put(new byte[] {1, 2, 3, 4}, the_value);
104 map.put(new byte[] {1, 2, 3, 4}, "Other Value");
105 map.put(new byte[] {1, 4}, "Third value");
106 System.out.println("val is: " + Arrays.toString(get_the_value.get()) + " resize +3");
107 System.out.print("Map is: ");
108 map.entrySet().stream()
109 .sorted((x, y) -> x.getValue().compareTo(y.getValue()))
110 .forEach(
111 (e) -> {
112 System.out.print("(" + Arrays.toString(e.getKey()) + "->" + e.getValue() + "), ");
113 });
114 System.out.println();
115 ResizeArray(get_the_value, 7);
116 System.out.println("val is: " + Arrays.toString(get_the_value.get()));
117 System.out.print("Map is: ");
118 map.entrySet().stream()
119 .sorted((x, y) -> x.getValue().compareTo(y.getValue()))
120 .forEach(
121 (e) -> {
122 System.out.print("(" + Arrays.toString(e.getKey()) + "->" + e.getValue() + "), ");
123 });
124 System.out.println();
125 }
126
127 public static void runWeakReference() {
128 System.out.println("Test j.l.r.WeakReference");
129 String[] arr = new String[] {"weak", "ref"};
130 WeakReference<String[]> wr = new WeakReference(arr);
131 DbgPrintln("Pre hash: " + wr.get().hashCode());
132 System.out.println("val is: " + Arrays.toString(wr.get()) + " resize +3");
133 ResizeArray(wr::get, wr.get().length + 5);
134 System.out.println("val is: " + Arrays.toString(wr.get()));
135 DbgPrintln("Post hash: " + wr.get().hashCode());
136 System.out.println("Same value? " + (wr.get() == arr));
137 }
138
139 public static void runInstanceSelfRef() {
140 System.out.println("Test instance self-ref");
141 DbgPrintln("Pre hash: " + Integer.toHexString(theInstanceClass.self_ref.hashCode()));
142 String pre_to_string = theInstanceClass.self_ref.toString();
143 System.out.println(
144 "val is: "
145 + Arrays.toString(theInstanceClass.self_ref).replace(pre_to_string, "<SELF REF>")
146 + " resize +5 item 0 is "
147 + Arrays.toString((Object[]) theInstanceClass.self_ref[0])
148 .replace(pre_to_string, "<SELF REF>"));
149 ResizeArray(() -> theInstanceClass.self_ref, theInstanceClass.self_ref.length + 5);
150 System.out.println(
151 "val is: "
152 + Arrays.toString(theInstanceClass.self_ref).replace(pre_to_string, "<SELF REF>"));
153 System.out.println(
154 "val is: "
155 + Arrays.toString((Object[]) theInstanceClass.self_ref[0])
156 .replace(pre_to_string, "<SELF REF>"));
157 DbgPrintln("Post hash: " + Integer.toHexString(theInstanceClass.self_ref.hashCode()));
158 System.out.println(
159 "Same value? " + (theInstanceClass.self_ref == theOtherInstanceClass.self_ref));
160 System.out.println(
161 "Same structure? " + (theInstanceClass.self_ref == theInstanceClass.self_ref[0]));
162 System.out.println(
163 "Same inner-structure? "
164 + (theInstanceClass.self_ref[0] == ((Object[]) theInstanceClass.self_ref[0])[0]));
165 }
166
167 public static void runInstanceSelfRefSmall() {
168 System.out.println("Test instance self-ref smaller");
169 DbgPrintln("Pre hash: " + Integer.toHexString(theInstanceClass.self_ref.hashCode()));
170 String pre_to_string = theInstanceClass.self_ref.toString();
171 System.out.println(
172 "val is: "
173 + Arrays.toString(theInstanceClass.self_ref).replace(pre_to_string, "<SELF REF>")
174 + " resize -7 item 0 is "
175 + Arrays.toString((Object[]) theInstanceClass.self_ref[0])
176 .replace(pre_to_string, "<SELF REF>"));
177 ResizeArray(() -> theInstanceClass.self_ref, theInstanceClass.self_ref.length - 7);
178 System.out.println(
179 "val is: "
180 + Arrays.toString(theInstanceClass.self_ref).replace(pre_to_string, "<SELF REF>"));
181 System.out.println(
182 "val is: "
183 + Arrays.toString((Object[]) theInstanceClass.self_ref[0])
184 .replace(pre_to_string, "<SELF REF>"));
185 DbgPrintln("Post hash: " + Integer.toHexString(theInstanceClass.self_ref.hashCode()));
186 System.out.println(
187 "Same value? " + (theInstanceClass.self_ref == theOtherInstanceClass.self_ref));
188 System.out.println(
189 "Same structure? " + (theInstanceClass.self_ref == theInstanceClass.self_ref[0]));
190 System.out.println(
191 "Same inner-structure? "
192 + (theInstanceClass.self_ref[0] == ((Object[]) theInstanceClass.self_ref[0])[0]));
193 }
194
195 public static void runLocal() throws Exception {
196 final int[] arr_loc = new int[] {2, 3, 4};
197 int[] arr_loc_2 = arr_loc;
198
199 System.out.println("Test local");
200 DbgPrintln("Pre hash: " + arr_loc.hashCode());
201 System.out.println("val is: " + Arrays.toString(arr_loc) + " resize +5");
202 ResizeArray(() -> arr_loc, arr_loc.length + 5);
203 System.out.println("val is: " + Arrays.toString(arr_loc));
204 DbgPrintln("Post hash: " + arr_loc.hashCode());
205 System.out.println("Same value? " + (arr_loc == arr_loc_2));
206 }
207
208 public static void runLocalSmall() throws Exception {
209 final int[] arr_loc = new int[] {1, 2, 3, 4, 5};
210 int[] arr_loc_2 = arr_loc;
211
212 System.out.println("Test local smaller");
213 DbgPrintln("Pre hash: " + arr_loc.hashCode());
214 System.out.println("val is: " + Arrays.toString(arr_loc) + " resize -2");
215 ResizeArray(() -> arr_loc, arr_loc.length - 2);
216 System.out.println("val is: " + Arrays.toString(arr_loc));
217 DbgPrintln("Post hash: " + arr_loc.hashCode());
218 System.out.println("Same value? " + (arr_loc == arr_loc_2));
219 }
220
221 public static void runMultiThreadLocal() throws Exception {
222 final CountDownLatch cdl = new CountDownLatch(1);
223 final CountDownLatch start_cdl = new CountDownLatch(2);
224 final Supplier<Object[]> getArr =
225 new Supplier<Object[]>() {
226 public final Object[] arr = new Object[] {"1", "2", "3"};
227
228 public Object[] get() {
229 return arr;
230 }
231 };
232 final ArrayList<String> msg1 = new ArrayList();
233 final ArrayList<String> msg2 = new ArrayList();
234 final Consumer<String> print1 =
235 (String s) -> {
236 msg1.add(s);
237 };
238 final Consumer<String> print2 =
239 (String s) -> {
240 msg2.add(s);
241 };
242 Function<Consumer<String>, Runnable> r =
243 (final Consumer<String> c) ->
244 () -> {
245 c.accept("Test local multi-thread");
246 Object[] arr_loc = getArr.get();
247 Object[] arr_loc_2 = getArr.get();
248
249 DbgPrintln("Pre hash: " + arr_loc.hashCode());
250 c.accept("val is: " + Arrays.toString(arr_loc) + " resize -2");
251
252 try {
253 start_cdl.countDown();
254 cdl.await();
255 } catch (Exception e) {
256 throw new Error("failed await", e);
257 }
258 c.accept("val is: " + Arrays.toString(arr_loc));
259 DbgPrintln("Post hash: " + arr_loc.hashCode());
260 c.accept("Same value? " + (arr_loc == arr_loc_2));
261 };
262 Thread t1 = new Thread(r.apply(print1));
263 Thread t2 = new Thread(r.apply(print2));
264 t1.start();
265 t2.start();
266 start_cdl.await();
267 ResizeArray(getArr, 1);
268 cdl.countDown();
269 t1.join();
270 t2.join();
271 for (String s : msg1) {
272 System.out.println("T1: " + s);
273 }
274 for (String s : msg2) {
275 System.out.println("T2: " + s);
276 }
277 }
278
279 public static void runWithLocks() throws Exception {
280 final CountDownLatch cdl = new CountDownLatch(1);
281 final CountDownLatch start_cdl = new CountDownLatch(2);
282 final CountDownLatch waiter_start_cdl = new CountDownLatch(1);
283 final Supplier<Object[]> getArr =
284 new Supplier<Object[]>() {
285 public final Object[] arr = new Object[] {"A", "2", "C"};
286
287 public Object[] get() {
288 return arr;
289 }
290 };
291 // basic order of operations noted above each line.
292 // Waiter runs to the 'wait' then t1 runs to the cdl.await, then current thread runs.
293 Runnable r =
294 () -> {
295 System.out.println("Test locks");
296 Object[] arr_loc = getArr.get();
297 Object[] arr_loc_2 = getArr.get();
298
299 DbgPrintln("Pre hash: " + arr_loc.hashCode());
300 System.out.println("val is: " + Arrays.toString(arr_loc) + " resize -2");
301
302 try {
303 // OP 1
304 waiter_start_cdl.await();
305 // OP 6
306 synchronized (arr_loc) {
307 // OP 7
308 synchronized (arr_loc_2) {
309 // OP 8
310 start_cdl.countDown();
311 // OP 9
312 cdl.await();
313 // OP 13
314 }
315 }
316 } catch (Exception e) {
317 throw new Error("failed await", e);
318 }
319 System.out.println("val is: " + Arrays.toString(arr_loc));
320 DbgPrintln("Post hash: " + arr_loc.hashCode());
321 System.out.println("Same value? " + (arr_loc == arr_loc_2));
322 };
323 Thread t1 = new Thread(r);
324 Thread waiter =
325 new Thread(
326 () -> {
327 try {
328 Object a = getArr.get();
329 // OP 2
330 synchronized (a) {
331 // OP 3
332 waiter_start_cdl.countDown();
333 // OP 4
334 start_cdl.countDown();
335 // OP 5
336 a.wait();
337 // OP 15
338 }
339 } catch (Exception e) {
340 throw new Error("Failed wait!", e);
341 }
342 });
343 waiter.start();
344 t1.start();
345 // OP 10
346 start_cdl.await();
347 // OP 11
348 ResizeArray(getArr, 1);
349 // OP 12
350 cdl.countDown();
351 // OP 14
352 synchronized (getArr.get()) {
353 // Make sure thread wakes up and has the right lock.
354 getArr.get().notifyAll();
355 }
356 waiter.join();
357 t1.join();
358 // Make sure other threads can still lock it.
359 synchronized (getArr.get()) {
360 }
361 System.out.println("Locks seem to all work.");
362 }
363
364 public static void runWithJniGlobal() throws Exception {
365 Object[] arr = new Object[] {"1", "11", "111"};
366 final long globalID = GetGlobalJniRef(arr);
367 System.out.println("Test jni-ref");
368 DbgPrintln("Pre hash: " + ReadJniRef(globalID).hashCode());
369 System.out.println(
370 "val is: " + Arrays.toString((Object[]) ReadJniRef(globalID)) + " resize +5");
371 ResizeArray(() -> ReadJniRef(globalID), ((Object[]) ReadJniRef(globalID)).length + 5);
372 System.out.println("val is: " + Arrays.toString((Object[]) ReadJniRef(globalID)));
373 DbgPrintln("Post hash: " + ReadJniRef(globalID).hashCode());
374 System.out.println("Same value? " + (ReadJniRef(globalID) == arr));
375 }
376
377 public static void runWithJniWeakGlobal() throws Exception {
378 Object[] arr = new Object[] {"2", "22", "222"};
379 final long globalID = GetWeakGlobalJniRef(arr);
380 System.out.println("Test weak jni-ref");
381 DbgPrintln("Pre hash: " + ReadJniRef(globalID).hashCode());
382 System.out.println(
383 "val is: " + Arrays.toString((Object[]) ReadJniRef(globalID)) + " resize +5");
384 ResizeArray(() -> ReadJniRef(globalID), ((Object[]) ReadJniRef(globalID)).length + 5);
385 System.out.println("val is: " + Arrays.toString((Object[]) ReadJniRef(globalID)));
386 DbgPrintln("Post hash: " + ReadJniRef(globalID).hashCode());
387 System.out.println("Same value? " + (ReadJniRef(globalID) == arr));
388 if (ReadJniRef(globalID) != arr) {
389 throw new Error("Didn't update weak global!");
390 }
391 }
392
393 public static void runWithJniLocals() throws Exception {
394 final Object[] arr = new Object[] {"3", "32", "322"};
395 System.out.println("Test jni local ref");
396 Consumer<Object> checker = (o) -> System.out.println("Same value? " + (o == arr));
397 Consumer<Object> printer =
398 (o) -> System.out.println("val is: " + Arrays.toString((Object[]) o));
399 Runnable resize =
400 () -> {
401 System.out.println("Resize +4");
402 ResizeArray(() -> arr, arr.length + 4);
403 };
404 runNativeTest(arr, resize, printer, checker);
405 }
406
407 public static native void runNativeTest(
408 Object[] arr, Runnable resize, Consumer<Object> printer, Consumer<Object> checker);
409
410 public static void runWithJvmtiTags() throws Exception {
411 Object[] arr = new Object[] {"3", "33", "333"};
412 long globalID = 333_333_333l;
413 Main.setTag(arr, globalID);
414 System.out.println("Test jvmti-tags");
415 DbgPrintln("Pre hash: " + arr.hashCode());
416 System.out.println(
417 "val is: " + Arrays.deepToString(GetObjectsWithTag(globalID)) + " resize +5");
418 ResizeArray(() -> arr, arr.length + 5);
419 Object[] after_tagged_obj = GetObjectsWithTag(globalID);
420 System.out.println("val is: " + Arrays.deepToString(GetObjectsWithTag(globalID)));
421 DbgPrintln("Post hash: " + after_tagged_obj[0].hashCode());
422 System.out.println("Same value? " + (after_tagged_obj[0] == arr));
423 }
424
Alex Light72d7e942019-07-23 13:10:20 -0700425 public static void runWithJvmtiTagsObsolete() throws Exception {
426 Object[] arr = new Object[] {"4", "44", "444"};
427 long globalID = 444_444_444l;
428 System.out.println("Test jvmti-tags with obsolete");
429 Main.setTag(arr, globalID);
430 StartCollectFrees();
431 StartAssignObsoleteIncrementedId();
432 DbgPrintln("Pre hash: " + arr.hashCode());
433 System.out.println(
434 "val is: " + Arrays.deepToString(GetObjectsWithTag(globalID)) + " resize +5");
435 ResizeArray(() -> arr, arr.length + 5);
436 Object[] after_tagged_obj = GetObjectsWithTag(globalID);
437 Object[] obsolete_tagged_obj = GetObjectsWithTag(globalID + 1);
438 System.out.println("val is: " + Arrays.deepToString(GetObjectsWithTag(globalID)));
439 EndAssignObsoleteIncrementedId();
440 long[] obsoletes_freed = CollectFreedTags();
441 DbgPrintln("Post hash: " + after_tagged_obj[0].hashCode());
442 System.out.println("Same value? " + (after_tagged_obj[0] == arr));
443 if (obsolete_tagged_obj.length >= 1) {
444 DbgPrintln("Found objects with obsolete tag: " + Arrays.deepToString(obsolete_tagged_obj));
445 boolean bad = false;
446 if (obsolete_tagged_obj.length != 1) {
447 System.out.println(
448 "Found obsolete tag'd objects: "
449 + Arrays.deepHashCode(obsolete_tagged_obj)
450 + " but only expected one!");
451 bad = true;
452 }
453 if (!Arrays.deepEquals(
454 Arrays.copyOf(arr, ((Object[]) obsolete_tagged_obj[0]).length),
455 (Object[]) obsolete_tagged_obj[0])) {
456 System.out.println("Obsolete array was unexpectedly different than non-obsolete one!");
457 bad = true;
458 }
459 if (!Arrays.stream(obsoletes_freed).anyMatch((l) -> l == globalID + 1)) {
460 DbgPrintln("Didn't see a free of the obsolete id");
461 }
462 if (!bad) {
Alex Light01fc4702019-09-17 09:29:22 -0700463 System.out.println("Everything looks good WRT obsolete object!");
Alex Light72d7e942019-07-23 13:10:20 -0700464 }
465 } else {
466 if (!Arrays.stream(obsoletes_freed).anyMatch((l) -> l == globalID + 1)) {
467 System.out.println("Didn't see a free of the obsolete id");
468 } else {
469 DbgPrintln("Saw a free of obsolete id!");
470 System.out.println("Everything looks good WRT obsolete object!");
471 }
472 }
473 }
474
Alex Lightc14ec8f2019-07-18 16:08:41 -0700475 public static void run() throws Exception {
476 // Simple
477 runAsThread(Test1974::runInstance);
478
479 // HashMap
480 runAsThread(Test1974::runHashMap);
481
482 // j.l.ref.WeakReference
483 runAsThread(Test1974::runWeakReference);
484
485 // Self-referential arrays.
486 runAsThread(Test1974::runInstanceSelfRef);
487 runAsThread(Test1974::runInstanceSelfRefSmall);
488
489 // Local variables simple
490 runAsThread(Test1974::runLocal);
491 runAsThread(Test1974::runLocalSmall);
492
493 // multiple threads local variables
494 runAsThread(Test1974::runMultiThreadLocal);
495
496 // using as monitors and waiting
497 runAsThread(Test1974::runWithLocks);
498
499 // Basic jni global refs
500 runAsThread(Test1974::runWithJniGlobal);
501
502 // Basic jni weak global refs
503 runAsThread(Test1974::runWithJniWeakGlobal);
504
505 // Basic JNI local refs
506 runAsThread(Test1974::runWithJniLocals);
507
508 // Basic jvmti tags
509 runAsThread(Test1974::runWithJvmtiTags);
Alex Light72d7e942019-07-23 13:10:20 -0700510
511 // Grab obsolete reference using tags/detect free
512 runAsThread(Test1974::runWithJvmtiTagsObsolete);
Alex Lightc14ec8f2019-07-18 16:08:41 -0700513 }
514
515 // Use a supplier so that we don't have to have a local ref to the resized
516 // array if we don't want it
517 public static native <T> void ResizeArray(Supplier<T> arr, int new_size);
518
519 public static native <T> long GetGlobalJniRef(T t);
520
521 public static native <T> long GetWeakGlobalJniRef(T t);
522
523 public static native <T> T ReadJniRef(long t);
524
525 public static native Object[] GetObjectsWithTag(long tag);
Alex Light72d7e942019-07-23 13:10:20 -0700526
527 public static native void StartCollectFrees();
528
529 public static native void StartAssignObsoleteIncrementedId();
530
531 public static native void EndAssignObsoleteIncrementedId();
532
533 public static native long[] CollectFreedTags();
Alex Lightc14ec8f2019-07-18 16:08:41 -0700534}