1 /* 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 17 package art; 18 19 import java.lang.ref.WeakReference; 20 import java.util.ArrayList; 21 import java.util.Arrays; 22 import java.util.HashMap; 23 import java.util.concurrent.CountDownLatch; 24 import java.util.function.Consumer; 25 import java.util.function.Function; 26 import java.util.function.Supplier; 27 28 public 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 InstanceClass()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 DbgPrintln(String s)55 public static void DbgPrintln(String s) { 56 if (DEBUG) { 57 System.out.println(s); 58 } 59 } 60 61 public interface ThrowRunnable extends Runnable { run()62 public default void run() { 63 try { 64 throwRun(); 65 } catch (Exception e) { 66 throw new Error("Exception in runner!", e); 67 } 68 } 69 throwRun()70 public void throwRun() throws Exception; 71 } 72 runAsThread(ThrowRunnable r)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 runInstance()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 runHashMap()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 runWeakReference()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 runInstanceSelfRef()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 runInstanceSelfRefSmall()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 runLocal()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 runLocalSmall()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 runMultiThreadLocal()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 runWithLocks()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 runWithJniGlobal()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 runWithJniWeakGlobal()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 runWithJniLocals()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 runNativeTest( Object[] arr, Runnable resize, Consumer<Object> printer, Consumer<Object> checker)407 public static native void runNativeTest( 408 Object[] arr, Runnable resize, Consumer<Object> printer, Consumer<Object> checker); 409 runWithJvmtiTags()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 runWithJvmtiTagsObsolete()425 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) { 463 System.out.println("Everything looks good WRT obsolete object!"); 464 } 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 run()475 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); 510 511 // Grab obsolete reference using tags/detect free 512 runAsThread(Test1974::runWithJvmtiTagsObsolete); 513 } 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 ResizeArray(Supplier<T> arr, int new_size)517 public static native <T> void ResizeArray(Supplier<T> arr, int new_size); 518 GetGlobalJniRef(T t)519 public static native <T> long GetGlobalJniRef(T t); 520 GetWeakGlobalJniRef(T t)521 public static native <T> long GetWeakGlobalJniRef(T t); 522 ReadJniRef(long t)523 public static native <T> T ReadJniRef(long t); 524 GetObjectsWithTag(long tag)525 public static native Object[] GetObjectsWithTag(long tag); 526 StartCollectFrees()527 public static native void StartCollectFrees(); 528 StartAssignObsoleteIncrementedId()529 public static native void StartAssignObsoleteIncrementedId(); 530 EndAssignObsoleteIncrementedId()531 public static native void EndAssignObsoleteIncrementedId(); 532 CollectFreedTags()533 public static native long[] CollectFreedTags(); 534 } 535