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