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  * @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.flavor == "server" & !vm.emulatedClient & vm.rtm.cpu & vm.rtm.os
  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 jdk.internal.misc.Unsafe;
  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 static final Unsafe UNSAFE = Unsafe.getUnsafe();
 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         }
 164 
 165         public void forceAbort(int a[], boolean abort) {
 166             try {
 167                 synchronized(monitor) {
 168                     a[0]++;
 169                     if (abort) {
 170                         Test.field = Test.UNSAFE.addressSize();
 171                     }
 172                 }
 173             } catch (Throwable t) {
 174                 // suppress any throwables
 175             }
 176         }
 177 
 178         /**
 179          * Usage:
 180          * Test &lt;inflate monitor&gt;
 181          */
 182         public static void main(String args[]) throws Throwable {
 183             Test t = new Test();
 184 
 185             boolean shouldBeInflated = Boolean.valueOf(args[0]);
 186             if (shouldBeInflated) {
 187                 AbortProvoker.inflateMonitor(t.monitor);
 188             }
 189 
 190             int tmp[] = new int[1];
 191 
 192             for (int i = 0; i < Test.ITERATIONS; i++ ) {
 193                 AbortProvoker.verifyMonitorState(t.monitor, shouldBeInflated);
 194                 if (i == Test.RANGE_CHECK_AT) {
 195                     t.forceAbort(new int[0], false);
 196                 } else {
 197                     boolean isThreshold
 198                             = (i == TestRTMAfterNonRTMDeopt.ABORT_THRESHOLD);
 199                     boolean isThresholdPlusRange
 200                             = (i == TestRTMAfterNonRTMDeopt.ABORT_THRESHOLD
 201                             + Test.RANGE_CHECK_AT);
 202                     t.forceAbort(tmp, isThreshold || isThresholdPlusRange);
 203                 }
 204             }
 205         }
 206     }
 207 
 208     public static void main(String args[]) throws Throwable {
 209         new TestRTMAfterNonRTMDeopt().runTestCases();
 210     }
 211 }
 212