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