1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.hotspot.test; 24 25 import java.lang.management.ManagementFactory; 26 import java.lang.management.MonitorInfo; 27 import java.lang.management.ThreadInfo; 28 import java.lang.management.ThreadMXBean; 29 import java.util.Collection; 30 import java.util.HashMap; 31 import java.util.HashSet; 32 33 import org.graalvm.compiler.api.directives.GraalDirectives; 34 import org.graalvm.compiler.core.phases.HighTier; 35 import org.graalvm.compiler.debug.DebugContext; 36 import org.graalvm.compiler.debug.GraalError; 37 import org.graalvm.compiler.debug.TTY; 38 import org.graalvm.compiler.hotspot.phases.OnStackReplacementPhase; 39 import org.graalvm.compiler.options.OptionKey; 40 import org.graalvm.compiler.options.OptionValues; 41 import org.graalvm.util.EconomicMap; 42 import org.junit.Assert; 43 import org.junit.Test; 44 45 import jdk.vm.ci.meta.ResolvedJavaMethod; 46 import org.junit.Assume; 47 48 /** 49 * Test on-stack-replacement with locks. 50 */ 51 public class GraalOSRLockTest extends GraalOSRTestBase { 52 53 private static boolean TestInSeparateThread = false; 54 55 public GraalOSRLockTest() { 56 try { 57 Class.forName("java.lang.management.ManagementFactory"); 58 } catch (ClassNotFoundException ex) { 59 Assume.assumeNoException("cannot check for monitors without java.management JDK9 module", ex); 60 } 61 } 62 63 // testing only 64 public static boolean isMonitorLockHeld(Object o) { 65 return isMonitorLockHeldByThread(o, null); 66 } 67 68 public static boolean isMonitorLockHeldByThread(Object o, Thread t) { 69 int oihc = System.identityHashCode(o); 70 ThreadMXBean tmxbean = ManagementFactory.getThreadMXBean(); 71 ThreadInfo[] tinfos = tmxbean.dumpAllThreads(true, false); 72 73 for (ThreadInfo ti : tinfos) { 74 if (!(t != null && t.getId() != ti.getThreadId())) { 75 for (MonitorInfo mi : ti.getLockedMonitors()) { 76 if (mi.getIdentityHashCode() == oihc) { 77 return true; 78 } 79 } 80 } 81 } 82 return false; 83 } 84 85 protected static void run(Runnable r) { 86 if (TestInSeparateThread) { 87 Thread t = new Thread(new Runnable() { 88 @Override 89 public void run() { 90 beforeOSRLockTest(); 91 r.run(); 92 afterOSRLockTest(); 93 } 94 }); 95 t.start(); 96 try { 97 t.join(); 98 } catch (Throwable t1) { 99 throw new GraalError(t1); 100 } 101 } else { 102 beforeOSRLockTest(); 103 r.run(); 104 afterOSRLockTest(); 105 } 106 } 107 108 private static boolean wasLocked() { 109 return isMonitorLockHeld(lock) || isMonitorLockHeld(lock1); 110 } 111 112 protected static EconomicMap<OptionKey<?>, Object> osrLockNoDeopt() { 113 EconomicMap<OptionKey<?>, Object> overrides = OptionValues.newOptionMap(); 114 overrides.put(OnStackReplacementPhase.Options.DeoptAfterOSR, false); 115 overrides.put(OnStackReplacementPhase.Options.SupportOSRWithLocks, true); 116 return overrides; 117 } 118 119 protected static EconomicMap<OptionKey<?>, Object> osrLockDeopt() { 120 EconomicMap<OptionKey<?>, Object> overrides = OptionValues.newOptionMap(); 121 overrides.put(OnStackReplacementPhase.Options.SupportOSRWithLocks, true); 122 return overrides; 123 } 124 125 public static int SideEffectI; 126 127 private static void lockOnObject(Object o, String msg) { 128 Thread t = new Thread(new Runnable() { 129 @Override 130 public void run() { 131 synchronized (o) { 132 SideEffectI = 1; 133 } 134 } 135 }); 136 t.start(); 137 try { 138 t.join(1000); 139 } catch (InterruptedException e) { 140 Assert.fail("Object " + msg + " was locked"); 141 } 142 } 143 144 private static void beforeOSRLockTest() { 145 // try lock both objects 146 lockOnObject(lock, "lock"); 147 lockOnObject(lock1, "lock1"); 148 Assert.assertFalse(wasLocked()); 149 } 150 151 private static void afterOSRLockTest() { 152 // try lock both objects 153 lockOnObject(lock, "lock"); 154 lockOnObject(lock1, "lock1"); 155 Assert.assertFalse(wasLocked()); 156 // force a safepoint and hope the inflated locks are deflated 157 System.gc(); 158 } 159 160 // @Test 161 @SuppressWarnings("try") 162 public void testLockOSROuterImmediateDeoptAfter() { 163 run(() -> { 164 OptionValues options = new OptionValues(getInitialOptions(), osrLockDeopt()); 165 testOSR(options, "testOuterLockImmediateDeoptAfter"); 166 }); 167 } 168 169 static class A { 170 171 } 172 173 static class B { 174 @SuppressWarnings("unused") 175 B(A a) { 176 177 } 178 } 179 180 HashMap<String, HashSet<A>> listeners = new HashMap<>(); 181 182 public synchronized ReturnValue synchronizedSnippet() { 183 /* 184 * Test method for which liveness would conclude the original object is no longer alive 185 * although it is. 186 */ 187 Collection<HashSet<A>> allListeners = listeners.values(); 188 for (HashSet<A> group : allListeners) { 189 GraalDirectives.blackhole(group); 190 } 191 return ReturnValue.SUCCESS; 192 } 193 194 @Test 195 @SuppressWarnings("try") 196 public void testSynchronizedSnippet() { 197 GraalOSRLockTest instance = new GraalOSRLockTest(); 198 // enough entries to trigger OSR 199 for (int i = 0; i < 100000; i++) { 200 instance.listeners.put("hello" + i, null); 201 } 202 testOSR(getInitialOptions(), "synchronizedSnippet", instance); 203 Assert.assertFalse(isMonitorLockHeld(instance)); 204 } 205 206 @Test 207 @SuppressWarnings("try") 208 public void testOSRTrivialLoop() { 209 run(() -> { 210 OptionValues options = new OptionValues(getInitialOptions(), osrLockDeopt()); 211 try { 212 testOSR(options, "testReduceOSRTrivialLoop"); 213 } catch (Throwable t) { 214 Assert.assertEquals("OSR compilation without OSR entry loop.", t.getMessage()); 215 } 216 }); 217 } 218 219 @Test 220 @SuppressWarnings("try") 221 public void testLockOSROuterInnerImmediateDeoptAfter() { 222 run(() -> { 223 OptionValues options = new OptionValues(getInitialOptions(), osrLockDeopt()); 224 testOSR(options, "testOuterInnerLockImmediateDeoptAfter"); 225 }); 226 } 227 228 @Test 229 @SuppressWarnings("try") 230 public void testLockOSROuterCompileRestOfMethod() { 231 run(() -> { 232 EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt(); 233 overrides.put(HighTier.Options.Inline, false); 234 OptionValues options = new OptionValues(getInitialOptions(), overrides); 235 testOSR(options, "testOuterLockCompileRestOfMethod"); 236 }); 237 } 238 239 @Test 240 @SuppressWarnings("try") 241 public void testLockOSROuterInnerCompileRestOfMethod() { 242 run(() -> { 243 OptionValues options = new OptionValues(getInitialOptions(), osrLockNoDeopt()); 244 testOSR(options, "testOuterInnerLockCompileRestOfMethod"); 245 }); 246 } 247 248 @Test 249 @SuppressWarnings("try") 250 public void testLockOSROuterInnerLockDepthCompileRestOfMethod() { 251 run(() -> { 252 EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt(); 253 overrides.put(HighTier.Options.Inline, false); 254 OptionValues options = new OptionValues(getInitialOptions(), overrides); 255 testOSR(options, "testOuterInnerLockDepth1CompileRestOfMethod"); 256 }); 257 } 258 259 @Test 260 @SuppressWarnings("try") 261 public void testLockOSROuterInnerLockDepthDeopt() { 262 run(() -> { 263 EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt(); 264 overrides.put(HighTier.Options.Inline, false); 265 OptionValues options = new OptionValues(getInitialOptions(), overrides); 266 testOSR(options, "testOuterInnerLockDepth1DeoptAfter"); 267 }); 268 } 269 270 @Test 271 @SuppressWarnings("try") 272 public void testLockOSROuterInnerLockDepthRecursiveCompileRestOfMethod0() { 273 run(() -> { 274 OptionValues options = new OptionValues(getInitialOptions(), osrLockNoDeopt()); 275 testOSR(options, "testOuterInnerLockDepth1RecursiveCompileRestOfMethod1"); 276 }); 277 } 278 279 @Test 280 @SuppressWarnings("try") 281 public void testLockOSROuterInnerLockDepthRecursiveCompileRestOfMethod1() { 282 run(() -> { 283 OptionValues options = new OptionValues(getInitialOptions(), osrLockNoDeopt()); 284 testOSR(options, "testOuterInnerLockDepth1RecursiveCompileRestOfMethod2"); 285 }); 286 } 287 288 @Test 289 @SuppressWarnings("try") 290 public void testLockOSROuterCompileRestOfMethodSubsequentLock() { 291 run(() -> { 292 OptionValues options = new OptionValues(getInitialOptions(), osrLockNoDeopt()); 293 testOSR(options, "testOuterLockCompileRestOfMethodSubsequentLock"); 294 }); 295 } 296 297 @Test 298 @SuppressWarnings("try") 299 public void testLockOSROuterInnerSameLockCompileRestOfMethod() { 300 run(() -> { 301 OptionValues options = new OptionValues(getInitialOptions(), osrLockNoDeopt()); 302 testOSR(options, "testOuterInnerSameLockCompileRestOfMethod"); 303 }); 304 } 305 306 @Test 307 @SuppressWarnings("try") 308 public void testLockOSRRecursive() { 309 run(() -> { 310 // call it 311 testRecursiveLockingLeaf(); 312 ResolvedJavaMethod leaf = getResolvedJavaMethod("testRecursiveLockingLeaf"); 313 // profile it 314 leaf.reprofile(); 315 testRecursiveLockingLeaf(); 316 EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt(); 317 overrides.put(HighTier.Options.Inline, false); 318 OptionValues options = new OptionValues(getInitialOptions(), overrides); 319 DebugContext debug = getDebugContext(options); 320 compile(debug, leaf, -1); 321 testOSR(options, "testRecursiveLockingRoot"); 322 }); 323 } 324 325 @Test 326 @SuppressWarnings("try") 327 public void testLockOSRRecursiveLeafOSR() { 328 run(() -> { 329 testRecursiveRootNoOSR(); 330 ResolvedJavaMethod root = getResolvedJavaMethod("testRecursiveRootNoOSR"); 331 EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt(); 332 overrides.put(HighTier.Options.Inline, false); 333 OptionValues options = new OptionValues(getInitialOptions(), overrides); 334 DebugContext debug = getDebugContext(options); 335 compile(debug, root, -1); 336 testOSR(options, "testRecursiveLeafOSR"); 337 // force a safepoint and hope the inflated locks are deflated 338 System.gc(); 339 // call the root to call into the leaf and enter the osr-ed code 340 testRecursiveRootNoOSR(); 341 342 }); 343 } 344 345 protected static int limit = 10000; 346 protected static Object lock = new Object(); 347 protected static Object lock1 = new Object(); 348 private static final boolean LOG = false; 349 350 static { 351 // force identity hash code for easy displaced mark identification 352 int h1 = System.identityHashCode(lock); 353 int h2 = System.identityHashCode(lock1); 354 if (LOG) { 355 TTY.println("Forcing a system identity hashcode on lock object " + h1); 356 TTY.println("Forcing a system identity hashcode on lock1 object " + h2); 357 } 358 } 359 360 public static ReturnValue testReduceOSRTrivialLoop() { 361 for (int i = 0; i < limit * limit; i++) { 362 GraalDirectives.blackhole(i); 363 if (GraalDirectives.inCompiledCode()) { 364 return ReturnValue.SUCCESS; 365 } 366 } 367 return ReturnValue.FAILURE; 368 } 369 370 public static ReturnValue testOuterLockImmediateDeoptAfter() { 371 ReturnValue ret = ReturnValue.FAILURE; 372 synchronized (lock) { 373 for (int i = 1; i < 10 * limit; i++) { 374 GraalDirectives.blackhole(i); 375 if (i % 33 == 0) { 376 ret = ReturnValue.SUCCESS; 377 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 378 GraalDirectives.blackhole(ret); 379 } 380 } 381 } 382 GraalDirectives.controlFlowAnchor(); 383 if (GraalDirectives.inCompiledCode()) { 384 throw new Error("Must not be part of compiled code"); 385 } 386 return ret; 387 } 388 } 389 390 public static ReturnValue testOuterInnerLockImmediateDeoptAfter() { 391 ReturnValue ret = ReturnValue.FAILURE; 392 synchronized (lock) { 393 for (int i = 1; i < 10 * limit; i++) { 394 synchronized (lock1) { 395 GraalDirectives.blackhole(i); 396 if (i % 33 == 0) { 397 ret = ReturnValue.SUCCESS; 398 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 399 GraalDirectives.blackhole(ret); 400 } 401 } 402 } 403 } 404 GraalDirectives.controlFlowAnchor(); 405 GraalDirectives.deoptimize(); 406 return ret; 407 } 408 } 409 410 public static ReturnValue testOuterLockCompileRestOfMethod() { 411 ReturnValue ret = ReturnValue.FAILURE; 412 synchronized (lock) { 413 for (int i = 1; i < limit; i++) { 414 GraalDirectives.blackhole(i); 415 if (i % 1001 == 0) { 416 ret = ReturnValue.SUCCESS; 417 System.gc(); 418 } 419 } 420 return ret; 421 } 422 } 423 424 public static ReturnValue testOuterInnerLockCompileRestOfMethod() { 425 ReturnValue ret = ReturnValue.FAILURE; 426 synchronized (lock) { 427 for (int i = 1; i < 10 * limit; i++) { 428 synchronized (lock1) { 429 GraalDirectives.blackhole(i); 430 if (i % 33 == 0) { 431 ret = ReturnValue.SUCCESS; 432 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 433 GraalDirectives.blackhole(ret); 434 System.gc(); 435 } 436 } 437 } 438 } 439 GraalDirectives.controlFlowAnchor(); 440 if (!GraalDirectives.inCompiledCode()) { 441 throw new Error("Must part of compiled code"); 442 } 443 return ret; 444 } 445 } 446 447 public static ReturnValue testOuterInnerLockDepth1CompileRestOfMethod() { 448 // testing the order of the lock releasing 449 ReturnValue ret = ReturnValue.FAILURE; 450 synchronized (lock) { 451 synchronized (lock1) { 452 for (int i = 1; i < limit; i++) { 453 GraalDirectives.blackhole(i); 454 if (i % 1001 == 0) { 455 ret = ReturnValue.SUCCESS; 456 if (GraalDirectives.inCompiledCode() && i + 33 > (limit)) { 457 GraalDirectives.blackhole(ret); 458 System.gc(); 459 } 460 } 461 } 462 } 463 GraalDirectives.controlFlowAnchor(); 464 if (!GraalDirectives.inCompiledCode()) { 465 throw new Error("Must part of compiled code"); 466 } else { 467 // lock 1 must be free 468 if (isMonitorLockHeld(lock1)) { 469 throw new Error("Lock 1 must have been released already"); 470 } 471 472 // lock 2 must still be locked and cannot be acquired by another thread 473 if (!isMonitorLockHeldByThread(lock, Thread.currentThread())) { 474 throw new Error("Lock must not have been released already"); 475 } 476 } 477 return ret; 478 } 479 } 480 481 public static ReturnValue testOuterInnerLockDepth1DeoptAfter() { 482 // testing the order of the lock releasing 483 ReturnValue ret = ReturnValue.FAILURE; 484 synchronized (lock) { 485 synchronized (lock1) { 486 for (int i = 1; i < 10 * limit; i++) { 487 GraalDirectives.blackhole(i); 488 if (i % 33 == 0) { 489 ret = ReturnValue.SUCCESS; 490 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 491 GraalDirectives.blackhole(ret); 492 } 493 } 494 } 495 GraalDirectives.controlFlowAnchor(); 496 GraalDirectives.deoptimize(); 497 if (GraalDirectives.inCompiledCode()) { 498 throw new Error("Must not part of compiled code"); 499 } 500 } 501 } 502 return ret; 503 } 504 505 public static ReturnValue testOuterInnerLockDepth1RecursiveCompileRestOfMethod1() { 506 // testing the order of the lock releasing 507 ReturnValue ret = ReturnValue.FAILURE; 508 synchronized (lock) { 509 synchronized (lock) { 510 for (int i = 1; i < 10 * limit; i++) { 511 GraalDirectives.blackhole(i); 512 if (i % 33 == 0) { 513 ret = ReturnValue.SUCCESS; 514 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 515 GraalDirectives.blackhole(ret); 516 } 517 } 518 } 519 } 520 GraalDirectives.controlFlowAnchor(); 521 if (!GraalDirectives.inCompiledCode()) { 522 throw new Error("Must part of compiled code"); 523 } 524 return ret; 525 } 526 } 527 528 public static ReturnValue testOuterInnerLockDepth1RecursiveCompileRestOfMethod2() { 529 // testing the order of the lock releasing 530 final Object l = lock; 531 ReturnValue ret = ReturnValue.FAILURE; 532 synchronized (l) { 533 synchronized (l) { 534 for (int i = 1; i < 10 * limit; i++) { 535 GraalDirectives.blackhole(i); 536 if (i % 33 == 0) { 537 ret = ReturnValue.SUCCESS; 538 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 539 GraalDirectives.blackhole(ret); 540 } 541 } 542 } 543 } 544 GraalDirectives.controlFlowAnchor(); 545 if (!GraalDirectives.inCompiledCode()) { 546 throw new Error("Must part of compiled code"); 547 } 548 return ret; 549 } 550 } 551 552 public static ReturnValue testRecursiveLockingRoot() { 553 // testing the order of the lock releasing 554 final Object l = lock; 555 ReturnValue ret = ReturnValue.FAILURE; 556 synchronized (l) { 557 synchronized (l) { 558 for (int i = 1; i < limit; i++) { 559 GraalDirectives.blackhole(i); 560 testRecursiveLockingLeaf(); 561 if (i % 33 == 0) { 562 ret = ReturnValue.SUCCESS; 563 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 564 GraalDirectives.blackhole(ret); 565 } 566 } 567 } 568 } 569 GraalDirectives.controlFlowAnchor(); 570 if (!GraalDirectives.inCompiledCode()) { 571 throw new Error("Must part of compiled code"); 572 } 573 return ret; 574 } 575 } 576 577 public static ReturnValue testRecursiveLockingLeaf() { 578 // testing the order of the lock releasing 579 final Object l = lock; 580 ReturnValue ret = ReturnValue.FAILURE; 581 synchronized (l) { 582 synchronized (l) { 583 for (int i = 1; i < limit; i++) { 584 GraalDirectives.blackhole(i); 585 if (i % 33 == 0) { 586 ret = ReturnValue.SUCCESS; 587 } 588 } 589 } 590 return ret; 591 } 592 } 593 594 public static ReturnValue testRecursiveRootNoOSR() { 595 // testing the order of the lock releasing 596 final Object l = lock; 597 synchronized (l) { 598 ReturnValue ret = ReturnValue.FAILURE; 599 for (int i = 0; i < 5; i++) { 600 if (GraalDirectives.inCompiledCode()) { 601 ret = testRecursiveLeafOSR(); 602 } 603 GraalDirectives.controlFlowAnchor(); 604 if (ret == ReturnValue.FAILURE) { 605 return ret; 606 } 607 } 608 GraalDirectives.controlFlowAnchor(); 609 return ret; 610 } 611 } 612 613 public static ReturnValue testRecursiveLeafOSR() { 614 ReturnValue ret = ReturnValue.FAILURE; 615 // lock is already locked by the caller 616 synchronized (lock) { 617 for (int i = 1; i < 10 * limit; i++) { 618 GraalDirectives.blackhole(i); 619 if (i % 33 == 0) { 620 ret = ReturnValue.SUCCESS; 621 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 622 GraalDirectives.blackhole(ret); 623 } 624 } 625 } 626 GraalDirectives.controlFlowAnchor(); 627 return ret; 628 } 629 } 630 631 // test cases for optimizations 632 public static ReturnValue testOuterLockCompileRestOfMethodSubsequentLock() { 633 final Object monitor = lock; 634 ReturnValue ret = ReturnValue.FAILURE; 635 synchronized (monitor) { 636 for (int i = 1; i < 10 * limit; i++) { 637 GraalDirectives.blackhole(i); 638 if (i % 33 == 0) { 639 ret = ReturnValue.SUCCESS; 640 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 641 GraalDirectives.blackhole(ret); 642 } 643 } 644 } 645 } 646 synchronized (monitor) { 647 GraalDirectives.controlFlowAnchor(); 648 if (!GraalDirectives.inCompiledCode()) { 649 throw new Error("Must part of compiled code"); 650 } 651 } 652 return ret; 653 654 } 655 656 public static ReturnValue testOuterInnerSameLockCompileRestOfMethod() { 657 final Object monitor = lock; 658 ReturnValue ret = ReturnValue.FAILURE; 659 synchronized (monitor) { 660 for (int i = 1; i < 10 * limit; i++) { 661 synchronized (monitor) { 662 GraalDirectives.blackhole(i); 663 if (i % 33 == 0) { 664 ret = ReturnValue.SUCCESS; 665 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 666 GraalDirectives.blackhole(ret); 667 } 668 } 669 } 670 } 671 GraalDirectives.controlFlowAnchor(); 672 if (!GraalDirectives.inCompiledCode()) { 673 throw new Error("Must part of compiled code"); 674 } 675 return ret; 676 } 677 } 678 679 }