1 /*
   2  * Copyright (c) 2014, 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  */
  24 
  25 /**
  26  * @test
  27  * @bug 8031320
  28  * @summary Verify that if we use RTMDeopt, then deoptimization
  29  *          caused by reason other then rtm_state_change will reset
  30  *          method's RTM state. And if we don't use RTMDeopt, then
  31  *          RTM state remain the same after such deoptimization.
  32  * @library /test/lib /
  33  * @modules java.base/jdk.internal.misc
  34  *          java.management
  35  * @build sun.hotspot.WhiteBox
  36  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  37  *                                sun.hotspot.WhiteBox$WhiteBoxPermission
  38  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
  39  *                   -XX:+WhiteBoxAPI
  40  *                   compiler.rtm.locking.TestRTMAfterNonRTMDeopt
  41  */
  42 
  43 package compiler.rtm.locking;
  44 
  45 import compiler.testlibrary.rtm.AbortProvoker;
  46 import compiler.testlibrary.rtm.CompilableTest;
  47 import compiler.testlibrary.rtm.RTMLockingStatistics;
  48 import compiler.testlibrary.rtm.RTMTestBase;
  49 import compiler.testlibrary.rtm.predicate.SupportedCPU;
  50 import compiler.testlibrary.rtm.predicate.SupportedVM;
  51 import jdk.internal.misc.Unsafe;
  52 import jdk.test.lib.Asserts;
  53 import jdk.test.lib.process.OutputAnalyzer;
  54 import jdk.test.lib.cli.CommandLineOptionTest;
  55 import jdk.test.lib.cli.predicate.AndPredicate;
  56 
  57 import java.util.List;
  58 
  59 /**
  60  * To verify that with +UseRTMDeopt method's RTM state will be
  61  * changed to ProfileRTM on deoptimization unrelated to
  62  * rtm_state_change following sequence of events is used:
  63  * <pre>
  64  *
  65  *     rtm state ^
  66  *               |
  67  *       UseRTM  |      ******|     ******
  68  *               |            |
  69  *   ProfileRTM  |******|     |*****|
  70  *               |      |     |     |
  71  *              0-------|-----|-----|---------------------&gt; time
  72  *                      |     |     \ force abort
  73  *                      |     |
  74  *                      |     \ force deoptimization
  75  *                      |
  76  *                      \ force xabort
  77  * </pre>
  78  * When xabort is forced by native method call method should
  79  * change it's state to UseRTM, because we use RTMAbortRatio=100
  80  * and low RTMLockingThreshold, so at this point actual abort
  81  * ratio will be below 100% and there should be enough lock
  82  * attempts to recompile method without RTM profiling.
  83  */
  84 public class TestRTMAfterNonRTMDeopt extends CommandLineOptionTest {
  85     private static final int ABORT_THRESHOLD = 1000;
  86     private static final String RANGE_CHECK = "range_check";
  87 
  88     private TestRTMAfterNonRTMDeopt() {
  89         super(new AndPredicate(new SupportedCPU(), new SupportedVM()));
  90     }
  91 
  92     @Override
  93     protected void runTestCases() throws Throwable {
  94         verifyRTMAfterDeopt(false, false);
  95         verifyRTMAfterDeopt(true, false);
  96 
  97         verifyRTMAfterDeopt(false, true);
  98         verifyRTMAfterDeopt(true, true);
  99     }
 100 
 101     private void verifyRTMAfterDeopt(boolean useStackLock,
 102             boolean useRTMDeopt) throws Throwable {
 103         CompilableTest test = new Test();
 104         String logFile = String.format("rtm_%s_stack_lock_%s_deopt.xml",
 105                 (useStackLock ? "use" : "no"), (useRTMDeopt ? "use" : "no"));
 106 
 107         OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest(
 108                 logFile,
 109                 test,
 110                 "-XX:CompileThreshold=1",
 111                 CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks",
 112                         useStackLock),
 113                 CommandLineOptionTest.prepareBooleanFlag("UseRTMDeopt",
 114                         useRTMDeopt),
 115                 "-XX:RTMAbortRatio=100",
 116                 CommandLineOptionTest.prepareNumericFlag("RTMAbortThreshold",
 117                         TestRTMAfterNonRTMDeopt.ABORT_THRESHOLD),
 118                 CommandLineOptionTest.prepareNumericFlag("RTMLockingThreshold",
 119                         TestRTMAfterNonRTMDeopt.ABORT_THRESHOLD / 2L),
 120                 "-XX:RTMTotalCountIncrRate=1",
 121                 "-XX:+PrintPreciseRTMLockingStatistics",
 122                 Test.class.getName(),
 123                 Boolean.toString(!useStackLock)
 124         );
 125 
 126         outputAnalyzer.shouldHaveExitValue(0);
 127 
 128         int traps = RTMTestBase.firedRTMStateChangeTraps(logFile);
 129 
 130         if (useRTMDeopt) {
 131             Asserts.assertEQ(traps, 2, "Two uncommon traps with "
 132                     + "reason rtm_state_change should be fired.");
 133         } else {
 134             Asserts.assertEQ(traps, 0, "No uncommon traps with "
 135                     + "reason rtm_state_change should be fired.");
 136         }
 137 
 138         int rangeCheckTraps = RTMTestBase.firedUncommonTraps(logFile,
 139                 TestRTMAfterNonRTMDeopt.RANGE_CHECK);
 140 
 141         Asserts.assertEQ(rangeCheckTraps, 1,
 142                 "One range_check uncommon trap should be fired.");
 143 
 144         List<RTMLockingStatistics> statistics = RTMLockingStatistics.fromString(
 145                 test.getMethodWithLockName(), outputAnalyzer.getOutput());
 146 
 147         int expectedStatEntries = (useRTMDeopt ? 4 : 2);
 148 
 149         Asserts.assertEQ(statistics.size(), expectedStatEntries,
 150                 String.format("VM output should contain %d RTM locking "
 151                         + "statistics entries.", expectedStatEntries));
 152     }
 153 
 154     public static class Test implements CompilableTest {
 155         // Following field have to be static in order to avoid escape analysis.
 156         @SuppressWarnings("UnsuedDeclaration")
 157         private static int field = 0;
 158         private static final int ITERATIONS = 10000;
 159         private static final int RANGE_CHECK_AT = ITERATIONS / 2;
 160         private static final Unsafe UNSAFE = Unsafe.getUnsafe();
 161         private final Object monitor = new Object();
 162 
 163         @Override
 164         public String getMethodWithLockName() {
 165             return this.getClass().getName() + "::forceAbort";
 166         }
 167 
 168         @Override
 169         public String[] getMethodsToCompileNames() {
 170             return new String[] { getMethodWithLockName() };
 171         }
 172 
 173         public void forceAbort(int a[], boolean abort) {
 174             try {
 175                 synchronized(monitor) {
 176                     a[0]++;
 177                     if (abort) {
 178                         Test.field = Test.UNSAFE.addressSize();
 179                     }
 180                 }
 181             } catch (Throwable t) {
 182                 // suppress any throwables
 183             }
 184         }
 185 
 186         /**
 187          * Usage:
 188          * Test &lt;inflate monitor&gt;
 189          */
 190         public static void main(String args[]) throws Throwable {
 191             Test t = new Test();
 192 
 193             boolean shouldBeInflated = Boolean.valueOf(args[0]);
 194             if (shouldBeInflated) {
 195                 AbortProvoker.inflateMonitor(t.monitor);
 196             }
 197 
 198             int tmp[] = new int[1];
 199 
 200             for (int i = 0; i < Test.ITERATIONS; i++ ) {
 201                 AbortProvoker.verifyMonitorState(t.monitor, shouldBeInflated);
 202                 if (i == Test.RANGE_CHECK_AT) {
 203                     t.forceAbort(new int[0], false);
 204                 } else {
 205                     boolean isThreshold
 206                             = (i == TestRTMAfterNonRTMDeopt.ABORT_THRESHOLD);
 207                     boolean isThresholdPlusRange
 208                             = (i == TestRTMAfterNonRTMDeopt.ABORT_THRESHOLD
 209                             + Test.RANGE_CHECK_AT);
 210                     t.forceAbort(tmp, isThreshold || isThresholdPlusRange);
 211                 }
 212             }
 213         }
 214     }
 215 
 216     public static void main(String args[]) throws Throwable {
 217         new TestRTMAfterNonRTMDeopt().test();
 218     }
 219 }
 220