1 /*
  2  * Copyright (c) 2020, 2026, 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 package jdk.jfr.event.runtime;
 25 
 26 import java.time.Duration;
 27 import java.util.*;
 28 
 29 import jdk.jfr.Recording;
 30 import jdk.jfr.consumer.RecordedEvent;
 31 import jdk.jfr.consumer.RecordedThread;
 32 import jdk.test.lib.jfr.EventNames;
 33 import jdk.test.lib.jfr.Events;
 34 
 35 /**
 36  * @test
 37  * @comment The name of this test (TestSyncOnValueBasedClassEvent) makes
 38  *          the reader think that we should be able to synchronize on a
 39  *          ValueBasedClass, but that IS NOT the case because that would
 40  *          result in an IdentityException.
 41  * @comment The purpose of this test is to verify that SyncOnValueBasedClass
 42  *          JFR events are generated when we synchronize on classes that
 43  *          WOULD BE ValueBasedClasses IF --enable-preview is enabled. The
 44  *          purpose of those JFR events is to evaluate Java codebases and
 45  *          determine if synchronization is used on objects that ARE NOT
 46  *          compatible with ValueBasedClasses.
 47  * @bug 8242263
 48  * @requires vm.hasJFR
 49  * @requires vm.flagless
 50  * @requires !java.enablePreview
 51  * @library /test/lib
 52  * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=2 jdk.jfr.event.runtime.TestSyncOnValueBasedClassEvent
 53  */
 54 public class TestSyncOnValueBasedClassEvent {
 55     static final String EVENT_NAME = EventNames.SyncOnValueBasedClass;
 56     static String[] classesWanted = {"java/lang/Character", "java/lang/Boolean", "java/lang/Byte", "java/lang/Short",
 57                                      "java/lang/Integer", "java/lang/Long", "java/lang/Float", "java/lang/Double",
 58                                      "java/lang/Runtime$Version"};
 59     static List<Object> testObjects = new ArrayList<Object>();
 60     static Integer counter = 0;
 61 
 62     private static void initTestObjects() {
 63         testObjects.add(Character.valueOf('H'));
 64         testObjects.add(Boolean.valueOf(true));
 65         testObjects.add(Byte.valueOf((byte)0x40));
 66         testObjects.add(Short.valueOf((short)0x4000));
 67         testObjects.add(Integer.valueOf(0x40000000));
 68         testObjects.add(Long.valueOf(0x4000000000000000L));
 69         testObjects.add(Float.valueOf(1.20f));
 70         testObjects.add(Double.valueOf(1.2345));
 71         testObjects.add(Runtime.version());
 72     }
 73 
 74     public static void main(String[] args) throws Throwable {
 75         initTestObjects();
 76         Recording recording = new Recording();
 77         recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0));
 78         recording.start();
 79         for (Object obj : testObjects) {
 80             synchronized (obj) {
 81                 counter++;
 82             }
 83         }
 84         recording.stop();
 85 
 86         List<String> classesFound = new ArrayList<String>();
 87         List<RecordedEvent> events = Events.fromRecording(recording);
 88         Events.hasEvents(events);
 89         for (RecordedEvent event : events) {
 90             String className = Events.assertField(event, "valueBasedClass.name").notEmpty().getValue();
 91             RecordedThread jt = event.getThread();
 92             if (Thread.currentThread().getName().equals(jt.getJavaName())) {
 93                 classesFound.add(className);
 94             }
 95         }
 96         for (String classWanted : classesWanted) {
 97             if (!classesFound.contains(classWanted)) {
 98                 throw new AssertionError("No matching event SyncOnValueBasedClass with \"valueBasedClass=" + classWanted + "\" and current thread as caller");
 99             }
100         }
101         if (classesFound.size() != classesWanted.length) {
102             throw new AssertionError("Invalid number of SyncOnValueBasedClass events for current thread");
103         }
104     }
105 }