1 /*
  2  * Copyright (c) 2020, 2021, 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 import jdk.test.lib.process.ProcessTools;
 25 import jdk.test.lib.process.OutputAnalyzer;
 26 import java.util.*;
 27 import java.util.stream.*;
 28 
 29 /*
 30  * @test
 31  * @bug 8242263
 32  * @summary Exercise DiagnoseSyncOnValueBasedClasses diagnostic flag
 33  * @library /test/lib
 34  * @run driver/timeout=180000 SyncOnValueBasedClassTest
 35  */
 36 
 37 public class SyncOnValueBasedClassTest {
 38     static final int LOOP_COUNT = 3000;
 39     static final int THREAD_COUNT = 2;
 40     static String[] fatalTests[];
 41     static String[] logTests[];
 42     static List<Object> testObjects = new ArrayList<Object>();
 43 
 44     private static final String[] specificFlags[] = {
 45         {"-Xint", "-XX:+UseBiasedLocking"},
 46         {"-Xint", "-XX:-UseBiasedLocking"},
 47         {"-Xcomp", "-XX:TieredStopAtLevel=1", "-XX:+UseBiasedLocking"},
 48         {"-Xcomp", "-XX:TieredStopAtLevel=1", "-XX:-UseBiasedLocking"},
 49         {"-Xcomp", "-XX:-TieredCompilation", "-XX:-UseBiasedLocking"},
 50         {"-Xcomp", "-XX:-TieredCompilation", "-XX:+UseBiasedLocking", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseOptoBiasInlining"},
 51         {"-Xcomp", "-XX:-TieredCompilation", "-XX:+UseBiasedLocking", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseOptoBiasInlining"}
 52     };
 53 
 54     private static void initTestObjects() {
 55         testObjects.add(Character.valueOf('H'));
 56         testObjects.add(Boolean.valueOf(true));
 57         testObjects.add(Byte.valueOf((byte)0x40));
 58         testObjects.add(Short.valueOf((short)0x4000));
 59         testObjects.add(Integer.valueOf(0x40000000));
 60         testObjects.add(Long.valueOf(0x4000000000000000L));
 61         testObjects.add(Float.valueOf(1.20f));
 62         testObjects.add(Double.valueOf(1.2345));
 63     }
 64 
 65     private static void generateTests() {
 66         initTestObjects();
 67         String[] commonFatalTestsFlags = {"-XX:+UnlockDiagnosticVMOptions", "-XX:-CreateCoredumpOnCrash", "-XX:DiagnoseSyncOnValueBasedClasses=1"};
 68         fatalTests = new String[specificFlags.length * testObjects.size()][];
 69         for (int i = 0; i < specificFlags.length; i++) {
 70             for (int j = 0; j < testObjects.size(); j++) {
 71                 int index = i * testObjects.size() + j;
 72                 fatalTests[index] = Stream.of(commonFatalTestsFlags, specificFlags[i], new String[] {"SyncOnValueBasedClassTest$FatalTest", Integer.toString(j)})
 73                                           .flatMap(Stream::of)
 74                                           .toArray(String[]::new);
 75             }
 76         }
 77         String[] commonLogTestsFlags = {"-XX:+UnlockDiagnosticVMOptions", "-XX:DiagnoseSyncOnValueBasedClasses=2"};
 78         logTests = new String[specificFlags.length][];
 79         for (int i = 0; i < specificFlags.length; i++) {
 80             logTests[i] = Stream.of(commonLogTestsFlags, specificFlags[i], new String[] {"SyncOnValueBasedClassTest$LogTest"})
 81                                 .flatMap(Stream::of)
 82                                 .toArray(String[]::new);
 83         }
 84     }
 85 
 86     public static void main(String[] args) throws Exception {
 87         generateTests();
 88         for (int i = 0; i < fatalTests.length; i++) {
 89             ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(fatalTests[i]);
 90             OutputAnalyzer output = ProcessTools.executeProcess(pb);
 91             output.shouldContain("fatal error: Synchronizing on object");
 92             output.shouldNotContain("synchronization on value based class did not fail");
 93             output.shouldNotHaveExitValue(0);
 94         }
 95         for (int i = 0; i < logTests.length; i++) {
 96             ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(logTests[i]);
 97             OutputAnalyzer output = ProcessTools.executeProcess(pb);
 98             output.shouldHaveExitValue(0);
 99             checkOutput(output);
100         }
101     }
102 
103     private static void checkOutput(OutputAnalyzer output) {
104         String out = output.getOutput();
105         assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Character.*"));
106         assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Boolean.*"));
107         assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Byte.*"));
108         assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Short.*"));
109         assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Integer.*"));
110         assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Long.*"));
111         String[] res = out.split("Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Float\\R");
112         assertTrue(res.length - 1 == (LOOP_COUNT * THREAD_COUNT + 1), res.length - 1);
113     }
114 
115     private static void assertTrue(boolean condition) {
116         if (!condition) {
117             throw new RuntimeException("No synchronization matches");
118         }
119     }
120 
121     private static void assertTrue(boolean condition, int count) {
122         if (!condition) {
123             throw new RuntimeException("Synchronization count was " + count);
124         }
125     }
126 
127     static class FatalTest {
128         public static void main(String[] args) throws Exception {
129             initTestObjects();
130             synchronized (testObjects.get(Integer.valueOf(args[0]))) {
131                 throw new RuntimeException("synchronization on value based class did not fail");
132             }
133         }
134     }
135 
136     static class LogTest implements Runnable {
137         private static long sharedCounter = 0L;
138         private static Float sharedLock1 = 0.0f;
139 
140         public static void main(String[] args) throws Exception {
141             initTestObjects();
142             for (Object obj : testObjects) {
143                 synchronized (obj) {
144                     sharedCounter++;
145                 }
146             }
147 
148             LogTest test = new LogTest();
149             Thread[] threads = new Thread[THREAD_COUNT];
150             for (int i = 0; i < threads.length; i++) {
151                 threads[i] = new Thread(test);
152                 threads[i].start();
153             }
154             for (Thread t : threads) {
155                 t.join();
156             }
157         }
158 
159         @Override
160         public void run() {
161             for (int i = 0; i < LOOP_COUNT; i++) {
162                 synchronized (sharedLock1) {
163                     sharedCounter++;
164                 }
165             }
166         }
167     }
168 }