diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java index 70d047dee930..a3a7cc9ce0c8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java @@ -53,7 +53,9 @@ public enum JfrEvent { GCPhasePauseLevel4Event("jdk.GCPhasePauseLevel4"), SafepointBegin("jdk.SafepointBegin"), SafepointEnd("jdk.SafepointEnd"), - ExecuteVMOperation("jdk.ExecuteVMOperation"); + ExecuteVMOperation("jdk.ExecuteVMOperation"), + JavaMonitorEnter("jdk.JavaMonitorEnter"); + private final long id; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorEnterEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorEnterEvent.java new file mode 100644 index 000000000000..5332f8249040 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorEnterEvent.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.core.jfr.events; + +import org.graalvm.nativeimage.StackValue; + +import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.jfr.JfrEvent; +import com.oracle.svm.core.jfr.JfrNativeEventWriter; +import com.oracle.svm.core.jfr.JfrNativeEventWriterData; +import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess; +import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.jfr.SubstrateJVM; + +public class JavaMonitorEnterEvent { + + @Uninterruptible(reason = "Accesses a JFR buffer.") + public static void emit(Object obj, org.graalvm.nativeimage.IsolateThread previousOwner, long startTicks) { + emit(obj, com.oracle.svm.core.jfr.SubstrateJVM.get().getThreadId(previousOwner), startTicks); + } + + @Uninterruptible(reason = "Accesses a JFR buffer.") + public static void emit(Object obj, long previousOwner, long startTicks) { + if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(JfrEvent.JavaMonitorEnter)) { + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + + JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.JavaMonitorEnter); + JfrNativeEventWriter.putLong(data, startTicks); + JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks() - startTicks); + JfrNativeEventWriter.putEventThread(data); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorEnter.getId(), 0)); + JfrNativeEventWriter.putClass(data, obj.getClass()); + JfrNativeEventWriter.putLong(data, previousOwner); + JfrNativeEventWriter.putLong(data, org.graalvm.compiler.word.Word.objectToUntrackedPointer(obj).rawValue()); + JfrNativeEventWriter.endSmallEvent(data); + + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/JavaMonitor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/JavaMonitor.java new file mode 100644 index 000000000000..9a78d25ec899 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/JavaMonitor.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.core.monitor; + +import java.util.concurrent.locks.ReentrantLock; + +import org.graalvm.nativeimage.CurrentIsolate; +import com.oracle.svm.core.jfr.SubstrateJVM; +import com.oracle.svm.core.jfr.events.JavaMonitorEnterEvent; +import com.oracle.svm.core.jfr.JfrTicks; + +public class JavaMonitor extends ReentrantLock { + private long ownerTid; + + public long getOwnerTid() { + return ownerTid; + } + + public JavaMonitor() { + super(); + ownerTid = SubstrateJVM.get().getThreadId(CurrentIsolate.getCurrentThread()); + } + + public void monitorEnter(Object obj) { + if (!tryLock()) { + long startTicks = JfrTicks.elapsedTicks(); + lock(); + JavaMonitorEnterEvent.emit(obj, getOwnerTid(), startTicks); + } + ownerTid = SubstrateJVM.get().getThreadId(CurrentIsolate.getCurrentThread()); + } + + +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java index cea15cd41940..03276b954891 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java @@ -60,6 +60,7 @@ import com.oracle.svm.core.thread.VMOperationControl; import com.oracle.svm.core.util.VMError; + import jdk.internal.misc.Unsafe; /** @@ -137,7 +138,7 @@ public class MultiThreadedMonitorSupport extends MonitorSupport { * java.io.FileInputStream.close() which synchronizes on a 'Object closeLock = new * Object()' object. We cannot modify the type of the monitor since it is in JDK code. * Adding a monitor slot to java.lang.Object doesn't impact any subtypes. - * + * * This should also take care of the synchronization in * ReferenceInternals.processPendingReferences(). */ @@ -192,7 +193,7 @@ public class MultiThreadedMonitorSupport extends MonitorSupport { * Secondary storage for monitor slots. Synchronized to prevent concurrent access and * modification. */ - private final Map additionalMonitors = new WeakIdentityHashMap<>(); + private final Map additionalMonitors = new WeakIdentityHashMap<>(); private final ReentrantLock additionalMonitorsLock = new ReentrantLock(); @Override @@ -255,8 +256,8 @@ private static void slowPathMonitorEnter(Object obj) { @RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED) @Override public void monitorEnter(Object obj) { - ReentrantLock lockObject = getOrCreateMonitor(obj, true); - lockObject.lock(); + JavaMonitor lockObject = getOrCreateMonitor(obj, true); + lockObject.monitorEnter(obj); } @SubstrateForeignCallTarget(stubCallingConvention = false) @@ -301,13 +302,13 @@ public Object prepareRelockObject(Object obj) { /* * We ensure that the lock for the object exists, so that the actual re-locking during * deoptimization can be uninterruptible. - * + * * Unfortunately, we cannot do any assertion checking in this method: deoptimization can run * in any thread, i.e., not necessarily in the thread that the lock will be for. And while * the frame that is deoptimized must have had the object locked, the thread could have * given up the lock as part of a wait() - so at this time any thread is allowed to hold the * lock. - * + * * Because any thread can hold the lock at this time, there is no way we can patch any * internal state of the lock immediately here. The actual state patching therefore happens * later in doRelockObject. @@ -421,7 +422,7 @@ protected static Object replaceObject(Object unreplacedObject) { return unreplacedObject; } - protected final ReentrantLock getOrCreateMonitor(Object unreplacedObject, boolean createIfNotExisting) { + protected final JavaMonitor getOrCreateMonitor(Object unreplacedObject, boolean createIfNotExisting) { Object obj = replaceObject(unreplacedObject); assert obj != null; int monitorOffset = getMonitorOffset(obj); @@ -434,25 +435,25 @@ protected final ReentrantLock getOrCreateMonitor(Object unreplacedObject, boolea } } - protected ReentrantLock getOrCreateMonitorFromObject(Object obj, boolean createIfNotExisting, int monitorOffset) { - ReentrantLock existingMonitor = (ReentrantLock) BarrieredAccess.readObject(obj, monitorOffset); + protected JavaMonitor getOrCreateMonitorFromObject(Object obj, boolean createIfNotExisting, int monitorOffset) { + JavaMonitor existingMonitor = (JavaMonitor) BarrieredAccess.readObject(obj, monitorOffset); if (existingMonitor != null || !createIfNotExisting) { assert existingMonitor == null || isMonitorLock(existingMonitor); return existingMonitor; } /* Atomically put a new lock in place of the null at the monitorOffset. */ - ReentrantLock newMonitor = newMonitorLock(); + JavaMonitor newMonitor = newMonitorLock(); if (UNSAFE.compareAndSetObject(obj, monitorOffset, null, newMonitor)) { return newMonitor; } /* We lost the race, use the lock some other thread installed. */ - return (ReentrantLock) BarrieredAccess.readObject(obj, monitorOffset); + return (JavaMonitor) BarrieredAccess.readObject(obj, monitorOffset); } - protected ReentrantLock getOrCreateMonitorFromMap(Object obj, boolean createIfNotExisting) { + protected JavaMonitor getOrCreateMonitorFromMap(Object obj, boolean createIfNotExisting) { assert obj.getClass() != Target_java_lang_ref_ReferenceQueue_Lock.class : "ReferenceQueue.Lock must have a monitor field or we can deadlock accessing WeakIdentityHashMap below"; VMError.guarantee(!additionalMonitorsLock.isHeldByCurrentThread(), - "Recursive manipulation of the additionalMonitors map can lead to table corruptions and double insertion of a monitor for the same object"); + "Recursive manipulation of the additionalMonitors map can lead to table corruptions and double insertion of a monitor for the same object"); /* * Lock the monitor map and maybe add a monitor for this object. This serialization might be @@ -460,13 +461,13 @@ protected ReentrantLock getOrCreateMonitorFromMap(Object obj, boolean createIfNo */ additionalMonitorsLock.lock(); try { - ReentrantLock existingMonitor = additionalMonitors.get(obj); + JavaMonitor existingMonitor = additionalMonitors.get(obj); if (existingMonitor != null || !createIfNotExisting) { assert existingMonitor == null || isMonitorLock(existingMonitor); return existingMonitor; } - ReentrantLock newMonitor = newMonitorLock(); - ReentrantLock previousEntry = additionalMonitors.put(obj, newMonitor); + JavaMonitor newMonitor = newMonitorLock(); + JavaMonitor previousEntry = additionalMonitors.put(obj, newMonitor); VMError.guarantee(previousEntry == null, "Replaced monitor in secondary storage map"); return newMonitor; } finally { @@ -474,8 +475,8 @@ protected ReentrantLock getOrCreateMonitorFromMap(Object obj, boolean createIfNo } } - protected static ReentrantLock newMonitorLock() { - ReentrantLock newMonitor = new ReentrantLock(); + protected static JavaMonitor newMonitorLock() { + JavaMonitor newMonitor = new JavaMonitor(); Target_java_util_concurrent_locks_ReentrantLock lock = SubstrateUtil.cast(newMonitor, Target_java_util_concurrent_locks_ReentrantLock.class); Target_java_util_concurrent_locks_ReentrantLock_NonfairSync sync = SubstrateUtil.cast(lock.sync, Target_java_util_concurrent_locks_ReentrantLock_NonfairSync.class); sync.objectMonitorCondition = SubstrateUtil.cast(MONITOR_WITHOUT_CONDITION, Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_ConditionObject.class);