Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,14 @@ public static void putThread(JfrNativeEventWriterData data, Thread thread) {
if (thread == null) {
putThread(data, 0L);
} else {
SubstrateJVM.maybeRegisterVirtualThread(thread);
putThread(data, SubstrateJVM.getThreadId(thread));
}
}

@Uninterruptible(reason = "Accesses a native JFR buffer.", callerMustBe = true)
public static void putThread(JfrNativeEventWriterData data, long threadId) {
SubstrateJVM.maybeRegisterVirtualThread(threadId);
putLong(data, threadId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package com.oracle.svm.core.jfr;

import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;

import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
Expand Down Expand Up @@ -92,23 +94,37 @@ public void registerRunningThreads() {
Thread thread = PlatformThreads.fromVMThread(isolateThread);
if (thread != null) {
registerThread(thread);
// Re-register vthreads that are already mounted.
Thread vthread = PlatformThreads.getMountedVirtualThread(thread);
if (vthread != null) {
registerThread(vthread);
}
}
}
}

/**
* If this method is called on platform threads, nothing will happen since they are already
* registered eagerly upon starting and at epoch changes.
*/
@Uninterruptible(reason = "Locking without transition requires that the whole critical section is uninterruptible.")
public void registerThread(long threadId) {
if (!SubstrateJVM.get().isRecording()) {
return;
}

registerThread0(threadId, 0, true, null, null);
}

@Uninterruptible(reason = "Locking without transition requires that the whole critical section is uninterruptible.")
public void registerThread(Thread thread) {
if (!SubstrateJVM.get().isRecording()) {
return;
}

long threadId = JavaThreads.getThreadId(thread);
boolean isVirtual = JavaThreads.isVirtual(thread);
long osThreadId = isVirtual ? 0 : threadId;
registerThread0(threadId, osThreadId, isVirtual, thread, thread.getName());
}

@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
private void registerThread0(long threadId, long osThreadId, boolean isVirtual, Thread thread, String name) {
JfrVisited visitedThread = StackValue.get(JfrVisited.class);
visitedThread.setId(threadId);
visitedThread.setHash(UninterruptibleUtils.Long.hashCode(threadId));
Expand All @@ -129,14 +145,12 @@ public void registerThread(Thread thread) {
JfrNativeEventWriterDataAccess.initialize(data, epochData.threadBuffer);

/* Similar to JfrThreadConstant::serialize in HotSpot. */
boolean isVirtual = JavaThreads.isVirtual(thread);
long osThreadId = isVirtual ? 0 : threadId;
long threadGroupId = registerThreadGroup(thread, isVirtual);

JfrNativeEventWriter.putLong(data, threadId);
JfrNativeEventWriter.putString(data, thread.getName()); // OS thread name
JfrNativeEventWriter.putString(data, name); // OS thread name
JfrNativeEventWriter.putLong(data, osThreadId); // OS thread id
JfrNativeEventWriter.putString(data, thread.getName()); // Java thread name
JfrNativeEventWriter.putString(data, name); // Java thread name
JfrNativeEventWriter.putLong(data, threadId); // Java thread id
JfrNativeEventWriter.putLong(data, threadGroupId); // Java thread group
JfrNativeEventWriter.putBoolean(data, isVirtual);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import java.util.List;

import com.oracle.svm.core.SubstrateUtil;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.Platform;
Expand All @@ -42,13 +43,15 @@
import com.oracle.svm.core.jfr.oldobject.JfrOldObjectRepository;
import com.oracle.svm.core.jfr.sampler.JfrExecutionSampler;
import com.oracle.svm.core.jfr.throttling.JfrEventThrottling;
import com.oracle.svm.core.jfr.traceid.JfrTraceIdEpoch;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.sampler.SamplerBufferPool;
import com.oracle.svm.core.sampler.SamplerBuffersAccess;
import com.oracle.svm.core.sampler.SamplerStatistics;
import com.oracle.svm.core.sampler.SubstrateSigprofHandler;
import com.oracle.svm.core.thread.JavaThreads;
import com.oracle.svm.core.thread.JavaVMOperation;
import com.oracle.svm.core.thread.Target_java_lang_VirtualThread;
import com.oracle.svm.core.thread.VMThreads;
import com.oracle.svm.core.util.VMError;

Expand Down Expand Up @@ -316,6 +319,32 @@ public static long getCurrentThreadId() {
return 0;
}

/**
* Register virtual threads if they aren't registered already. Platform threads are registered
* eagerly when started and at chunk rotations.
*/
@Uninterruptible(reason = "Epoch should not change while checking generation.")
public static void maybeRegisterVirtualThread(Thread thread) {
// Do quick preliminary checks to avoid global locking unless necessary.
if (JavaThreads.isVirtual(thread)) {
Target_java_lang_VirtualThread tjlv = SubstrateUtil.cast(thread, Target_java_lang_VirtualThread.class);
int currentEpochGen = JfrTraceIdEpoch.getInstance().currentEpochGeneration();
if (tjlv.jfrGeneration != currentEpochGen) {
getThreadRepo().registerThread(thread);
tjlv.jfrGeneration = currentEpochGen;
}
}
}

/**
* {@link SubstrateJVM#maybeRegisterVirtualThread(Thread)} Is preferred over this method since
* it can perform preliminary checks to avoid locking the Thread Repository unnecessarily.
*/
@Uninterruptible(reason = "Epoch should not change while checking generation.")
public static void maybeRegisterVirtualThread(long tid) {
getThreadRepo().registerThread(tid);
}

/**
* See {@link JVM#storeMetadataDescriptor}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static void writeExecutionSample(long elapsedTicks, long threadId, long s

JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ExecutionSample);
JfrNativeEventWriter.putLong(data, elapsedTicks);
JfrNativeEventWriter.putLong(data, threadId);
JfrNativeEventWriter.putThread(data, threadId);
JfrNativeEventWriter.putLong(data, stackTraceId);
JfrNativeEventWriter.putLong(data, threadState);
JfrNativeEventWriter.endSmallEvent(data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@
import com.oracle.svm.core.jfr.SubstrateJVM;

public class JavaMonitorEnterEvent {
public static void emit(Object obj, long previousOwnerTid, long startTicks) {
public static void emit(Object obj, Thread previousOwner, long startTicks) {
if (HasJfrSupport.get()) {
emit0(obj, previousOwnerTid, startTicks);
emit0(obj, previousOwner, startTicks);
}
}

@Uninterruptible(reason = "Accesses a JFR buffer.")
public static void emit0(Object obj, long previousOwnerTid, long startTicks) {
public static void emit0(Object obj, Thread previousOwner, long startTicks) {
long duration = JfrTicks.duration(startTicks);
if (JfrEvent.JavaMonitorEnter.shouldEmit(duration)) {
JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class);
Expand All @@ -58,7 +58,7 @@ public static void emit0(Object obj, long previousOwnerTid, long startTicks) {
JfrNativeEventWriter.putEventThread(data);
JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorEnter, 0));
JfrNativeEventWriter.putClass(data, obj.getClass());
JfrNativeEventWriter.putLong(data, previousOwnerTid);
JfrNativeEventWriter.putThread(data, previousOwner);
JfrNativeEventWriter.putLong(data, Word.objectToUntrackedPointer(obj).rawValue());
JfrNativeEventWriter.endSmallEvent(data);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@
import jdk.graal.compiler.word.Word;

public class JavaMonitorWaitEvent {
public static void emit(long startTicks, Object obj, long notifier, long timeout, boolean timedOut) {
public static void emit(long startTicks, Object obj, Thread notifier, long timeout, boolean timedOut) {
if (HasJfrSupport.get() && obj != null && !Target_jdk_jfr_internal_JVM_ChunkRotationMonitor.class.equals(obj.getClass()) &&
!Target_jdk_jfr_internal_management_HiddenWait.class.equals(obj.getClass())) {
emit0(startTicks, obj, notifier, timeout, timedOut);
}
}

@Uninterruptible(reason = "Accesses a JFR buffer.")
private static void emit0(long startTicks, Object obj, long notifier, long timeout, boolean timedOut) {
private static void emit0(long startTicks, Object obj, Thread notifier, long timeout, boolean timedOut) {
long duration = JfrTicks.duration(startTicks);
if (JfrEvent.JavaMonitorWait.shouldEmit(duration)) {
JfrNativeEventWriterData data = org.graalvm.nativeimage.StackValue.get(JfrNativeEventWriterData.class);
Expand All @@ -60,7 +60,7 @@ private static void emit0(long startTicks, Object obj, long notifier, long timeo
JfrNativeEventWriter.putEventThread(data);
JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorWait, 0));
JfrNativeEventWriter.putClass(data, obj.getClass());
JfrNativeEventWriter.putLong(data, notifier);
JfrNativeEventWriter.putThread(data, notifier);
JfrNativeEventWriter.putLong(data, timeout);
JfrNativeEventWriter.putBoolean(data, timedOut);
JfrNativeEventWriter.putLong(data, Word.objectToUntrackedPointer(obj).rawValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static void emit(Thread thread) {
JfrNativeEventWriter.putEventThread(data);
JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ThreadStart, 0));
JfrNativeEventWriter.putThread(data, thread);
JfrNativeEventWriter.putLong(data, JavaThreads.getParentThreadId(thread));
JfrNativeEventWriter.putThread(data, JavaThreads.getParentThreadId(thread));
JfrNativeEventWriter.endSmallEvent(data);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class JfrTraceIdEpoch {
private static final long EPOCH_0_BIT = 0b01;
private static final long EPOCH_1_BIT = 0b10;

private boolean epoch;
private int epochGeneration;

@Fold
public static JfrTraceIdEpoch getInstance() {
Expand All @@ -53,29 +53,39 @@ public static JfrTraceIdEpoch getInstance() {
public JfrTraceIdEpoch() {
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
private boolean getEpoch() {
return (epochGeneration & 1) == 0;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public void changeEpoch() {
assert VMOperation.isInProgressAtSafepoint();
epoch = !epoch;
epochGeneration++;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
long thisEpochBit() {
return epoch ? EPOCH_1_BIT : EPOCH_0_BIT;
return getEpoch() ? EPOCH_1_BIT : EPOCH_0_BIT;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
long previousEpochBit() {
return epoch ? EPOCH_0_BIT : EPOCH_1_BIT;
return getEpoch() ? EPOCH_0_BIT : EPOCH_1_BIT;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public boolean currentEpoch() {
return epoch;
return getEpoch();
}

@Uninterruptible(reason = "Avoid epoch changing while checking generation.", callerMustBe = true)
public int currentEpochGeneration() {
return epochGeneration;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public boolean previousEpoch() {
return !epoch;
return !getEpoch();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@

import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.jfr.JfrTicks;
import com.oracle.svm.core.jfr.SubstrateJVM;
import com.oracle.svm.core.jfr.events.JavaMonitorEnterEvent;
import com.oracle.svm.core.thread.JavaThreads;
import com.oracle.svm.core.util.BasedOnJDKClass;
Expand All @@ -61,20 +60,20 @@
@BasedOnJDKClass(ReentrantLock.class)
@BasedOnJDKClass(value = ReentrantLock.class, innerClass = "Sync")
public class JavaMonitor extends JavaMonitorQueuedSynchronizer {
protected long latestJfrTid;
protected Thread latest;

public JavaMonitor() {
latestJfrTid = 0;
latest = null;
}

public void monitorEnter(Object obj) {
if (!tryLock()) {
long startTicks = JfrTicks.elapsedTicks();
acquire(1);
JavaMonitorEnterEvent.emit(obj, latestJfrTid, startTicks);
JavaMonitorEnterEvent.emit(obj, latest, startTicks);
}

latestJfrTid = SubstrateJVM.getCurrentThreadId();
latest = Thread.currentThread();
}

public void monitorExit() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@

import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.jfr.JfrTicks;
import com.oracle.svm.core.jfr.SubstrateJVM;
import com.oracle.svm.core.jfr.events.JavaMonitorWaitEvent;
import com.oracle.svm.core.thread.JavaThreads;
import com.oracle.svm.core.util.BasedOnJDKClass;
Expand Down Expand Up @@ -120,7 +119,7 @@ static final class ExclusiveNode extends Node {
@BasedOnJDKClass(value = AbstractQueuedLongSynchronizer.class, innerClass = "ConditionNode")
static final class ConditionNode extends Node {
ConditionNode nextWaiter;
long notifierJfrTid;
Thread notifierJfr;

// see AbstractQueuedLongSynchronizer.ConditionNode.isReleasable()
public boolean isReleasable() {
Expand Down Expand Up @@ -457,7 +456,7 @@ private void doSignal(ConditionNode first, boolean all) {
first.nextWaiter = null; // GC assistance
}
if ((first.getAndUnsetStatus(COND) & COND) != 0) {
first.notifierJfrTid = SubstrateJVM.getCurrentThreadId();
first.notifierJfr = Thread.currentThread();
enqueue(first);
if (!all) {
break;
Expand Down Expand Up @@ -564,7 +563,7 @@ private ConditionNode newConditionNode() {
public void await(Object obj) throws InterruptedException {
long startTicks = JfrTicks.elapsedTicks();
if (Thread.interrupted()) {
JavaMonitorWaitEvent.emit(startTicks, obj, 0, 0L, false);
JavaMonitorWaitEvent.emit(startTicks, obj, null, 0L, false);
throw new InterruptedException();
}
ConditionNode node = newConditionNode();
Expand All @@ -587,7 +586,7 @@ public void await(Object obj) throws InterruptedException {
}
node.clearStatus();
// waiting is done, emit wait event
JavaMonitorWaitEvent.emit(startTicks, obj, node.notifierJfrTid, 0L, false);
JavaMonitorWaitEvent.emit(startTicks, obj, node.notifierJfr, 0L, false);
acquire(node, savedAcquisitions);
if (interrupted) {
if (cancelled) {
Expand All @@ -604,7 +603,7 @@ public boolean await(Object obj, long time, TimeUnit unit) throws InterruptedExc
long startTicks = JfrTicks.elapsedTicks();
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted()) {
JavaMonitorWaitEvent.emit(startTicks, obj, 0, 0L, false);
JavaMonitorWaitEvent.emit(startTicks, obj, null, 0L, false);
throw new InterruptedException();
}
ConditionNode node = newConditionNode();
Expand All @@ -627,7 +626,7 @@ public boolean await(Object obj, long time, TimeUnit unit) throws InterruptedExc
}
node.clearStatus();
// waiting is done, emit wait event
JavaMonitorWaitEvent.emit(startTicks, obj, node.notifierJfrTid, time, cancelled);
JavaMonitorWaitEvent.emit(startTicks, obj, node.notifierJfr, time, cancelled);
acquire(node, savedAcquisitions);
if (cancelled) {
unlinkCancelledWaiters(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ public void monitorEnter(Object obj, MonitorInflationCause cause) {
monitor = (JavaMonitor) UNSAFE.compareAndExchangeReference(obj, monitorOffset, null, newMonitor);
if (monitor == null) { // successful
JavaMonitorInflateEvent.emit(obj, startTicks, MonitorInflationCause.MONITOR_ENTER);
newMonitor.latestJfrTid = current;
newMonitor.latest = Thread.currentThread();
return;
}
}
Expand Down
Loading