Skip to content

Commit 4fceea2

Browse files
[GR-57064] Skip unnecessary JFR registrations for virtual threads.
PullRequest: graal/20630
2 parents 96eef59 + 54c5223 commit 4fceea2

File tree

9 files changed

+76
-23
lines changed

9 files changed

+76
-23
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrNativeEventWriter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
*/
2525
package com.oracle.svm.core.jfr;
2626

27-
import jdk.graal.compiler.word.Word;
2827
import org.graalvm.word.Pointer;
2928
import org.graalvm.word.UnsignedWord;
3029

@@ -35,6 +34,8 @@
3534
import com.oracle.svm.core.util.DuplicatedInNativeCode;
3635
import com.oracle.svm.core.util.VMError;
3736

37+
import jdk.graal.compiler.word.Word;
38+
3839
/**
3940
* A JFR event writer that does not allocate any objects in the Java heap. Can only be used from
4041
* {@link Uninterruptible} code to prevent races between threads that try to write a native JFR

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThreadRepository.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
*/
2525
package com.oracle.svm.core.jfr;
2626

27-
import jdk.graal.compiler.word.Word;
2827
import org.graalvm.nativeimage.IsolateThread;
2928
import org.graalvm.nativeimage.Platform;
3029
import org.graalvm.nativeimage.Platforms;
@@ -40,9 +39,12 @@
4039
import com.oracle.svm.core.thread.JavaThreads;
4140
import com.oracle.svm.core.thread.PlatformThreads;
4241
import com.oracle.svm.core.thread.Target_java_lang_Thread;
42+
import com.oracle.svm.core.thread.Target_java_lang_VirtualThread;
4343
import com.oracle.svm.core.thread.VMOperation;
4444
import com.oracle.svm.core.thread.VMThreads;
4545

46+
import jdk.graal.compiler.word.Word;
47+
4648
/**
4749
* Repository that collects all metadata about threads and thread groups.
4850
*
@@ -101,14 +103,23 @@ public void registerRunningThreads() {
101103
}
102104
}
103105

104-
@Uninterruptible(reason = "Locking without transition requires that the whole critical section is uninterruptible.")
106+
@Uninterruptible(reason = "Prevent epoch changes. Prevent races with VM operations that start/stop recording.")
105107
public void registerThread(Thread thread) {
106108
if (!SubstrateJVM.get().isRecording()) {
107109
return;
108110
}
109111

110-
long threadId = JavaThreads.getThreadId(thread);
112+
boolean isVirtual = JavaThreads.isVirtual(thread);
113+
if (isVirtual && isVirtualThreadAlreadyRegistered(thread)) {
114+
return;
115+
}
116+
117+
registerThread0(thread, isVirtual);
118+
}
111119

120+
@Uninterruptible(reason = "Locking without transition requires that the whole critical section is uninterruptible.")
121+
private void registerThread0(Thread thread, boolean isVirtual) {
122+
long threadId = JavaThreads.getThreadId(thread);
112123
JfrVisited visitedThread = StackValue.get(JfrVisited.class);
113124
visitedThread.setId(threadId);
114125
visitedThread.setHash(UninterruptibleUtils.Long.hashCode(threadId));
@@ -129,21 +140,26 @@ public void registerThread(Thread thread) {
129140
JfrNativeEventWriterDataAccess.initialize(data, epochData.threadBuffer);
130141

131142
/* Similar to JfrThreadConstant::serialize in HotSpot. */
132-
boolean isVirtual = JavaThreads.isVirtual(thread);
133143
long osThreadId = isVirtual ? 0 : threadId;
134144
long threadGroupId = registerThreadGroup(thread, isVirtual);
145+
String name = thread.getName();
135146

136147
JfrNativeEventWriter.putLong(data, threadId);
137-
JfrNativeEventWriter.putString(data, thread.getName()); // OS thread name
148+
JfrNativeEventWriter.putString(data, name); // OS thread name
138149
JfrNativeEventWriter.putLong(data, osThreadId); // OS thread id
139-
JfrNativeEventWriter.putString(data, thread.getName()); // Java thread name
150+
JfrNativeEventWriter.putString(data, name); // Java thread name
140151
JfrNativeEventWriter.putLong(data, threadId); // Java thread id
141152
JfrNativeEventWriter.putLong(data, threadGroupId); // Java thread group
142153
JfrNativeEventWriter.putBoolean(data, isVirtual);
143154
if (!JfrNativeEventWriter.commit(data)) {
144155
return;
145156
}
146157

158+
if (isVirtual) {
159+
Target_java_lang_VirtualThread vthread = JavaThreads.toVirtualTarget(thread);
160+
vthread.jfrEpochId = JfrTraceIdEpoch.getInstance().currentEpochId();
161+
}
162+
147163
epochData.unflushedThreadCount++;
148164
/* The buffer may have been replaced with a new one. */
149165
epochData.threadBuffer = data.getJfrBuffer();
@@ -152,6 +168,16 @@ public void registerThread(Thread thread) {
152168
}
153169
}
154170

171+
@Uninterruptible(reason = "Epoch must not change while in this method.", callerMustBe = true)
172+
private static boolean isVirtualThreadAlreadyRegistered(Thread thread) {
173+
assert JavaThreads.isVirtual(thread);
174+
175+
/* Threads only need to be registered once per epoch. */
176+
Target_java_lang_VirtualThread vthread = JavaThreads.toVirtualTarget(thread);
177+
long epochId = JfrTraceIdEpoch.getInstance().currentEpochId();
178+
return vthread.jfrEpochId == epochId;
179+
}
180+
155181
@Uninterruptible(reason = "Epoch must not change while in this method.")
156182
private long registerThreadGroup(Thread thread, boolean isVirtual) {
157183
if (isVirtual) {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecutionSampleEvent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public static void writeExecutionSample(long elapsedTicks, long threadId, long s
4141

4242
JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ExecutionSample);
4343
JfrNativeEventWriter.putLong(data, elapsedTicks);
44-
JfrNativeEventWriter.putLong(data, threadId);
44+
JfrNativeEventWriter.putThread(data, threadId);
4545
JfrNativeEventWriter.putLong(data, stackTraceId);
4646
JfrNativeEventWriter.putLong(data, threadState);
4747
JfrNativeEventWriter.endSmallEvent(data);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorEnterEvent.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
package com.oracle.svm.core.jfr.events;
2828

29-
import jdk.graal.compiler.word.Word;
3029
import org.graalvm.nativeimage.StackValue;
3130

3231
import com.oracle.svm.core.Uninterruptible;
@@ -38,6 +37,8 @@
3837
import com.oracle.svm.core.jfr.JfrTicks;
3938
import com.oracle.svm.core.jfr.SubstrateJVM;
4039

40+
import jdk.graal.compiler.word.Word;
41+
4142
public class JavaMonitorEnterEvent {
4243
public static void emit(Object obj, long previousOwnerTid, long startTicks) {
4344
if (HasJfrSupport.get()) {
@@ -58,7 +59,7 @@ public static void emit0(Object obj, long previousOwnerTid, long startTicks) {
5859
JfrNativeEventWriter.putEventThread(data);
5960
JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorEnter, 0));
6061
JfrNativeEventWriter.putClass(data, obj.getClass());
61-
JfrNativeEventWriter.putLong(data, previousOwnerTid);
62+
JfrNativeEventWriter.putThread(data, previousOwnerTid);
6263
JfrNativeEventWriter.putLong(data, Word.objectToUntrackedPointer(obj).rawValue());
6364
JfrNativeEventWriter.endSmallEvent(data);
6465
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorWaitEvent.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@
4040
import jdk.graal.compiler.word.Word;
4141

4242
public class JavaMonitorWaitEvent {
43-
public static void emit(long startTicks, Object obj, long notifier, long timeout, boolean timedOut) {
43+
public static void emit(long startTicks, Object obj, long notifierTid, long timeout, boolean timedOut) {
4444
if (HasJfrSupport.get() && obj != null && !Target_jdk_jfr_internal_JVM_ChunkRotationMonitor.class.equals(obj.getClass()) &&
4545
!Target_jdk_jfr_internal_management_HiddenWait.class.equals(obj.getClass())) {
46-
emit0(startTicks, obj, notifier, timeout, timedOut);
46+
emit0(startTicks, obj, notifierTid, timeout, timedOut);
4747
}
4848
}
4949

5050
@Uninterruptible(reason = "Accesses a JFR buffer.")
51-
private static void emit0(long startTicks, Object obj, long notifier, long timeout, boolean timedOut) {
51+
private static void emit0(long startTicks, Object obj, long notifierTid, long timeout, boolean timedOut) {
5252
long duration = JfrTicks.duration(startTicks);
5353
if (JfrEvent.JavaMonitorWait.shouldEmit(duration)) {
5454
JfrNativeEventWriterData data = org.graalvm.nativeimage.StackValue.get(JfrNativeEventWriterData.class);
@@ -60,7 +60,7 @@ private static void emit0(long startTicks, Object obj, long notifier, long timeo
6060
JfrNativeEventWriter.putEventThread(data);
6161
JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorWait, 0));
6262
JfrNativeEventWriter.putClass(data, obj.getClass());
63-
JfrNativeEventWriter.putLong(data, notifier);
63+
JfrNativeEventWriter.putThread(data, notifierTid);
6464
JfrNativeEventWriter.putLong(data, timeout);
6565
JfrNativeEventWriter.putBoolean(data, timedOut);
6666
JfrNativeEventWriter.putLong(data, Word.objectToUntrackedPointer(obj).rawValue());

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadStartEvent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public static void emit(Thread thread) {
4747
JfrNativeEventWriter.putEventThread(data);
4848
JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ThreadStart, 0));
4949
JfrNativeEventWriter.putThread(data, thread);
50-
JfrNativeEventWriter.putLong(data, JavaThreads.getParentThreadId(thread));
50+
JfrNativeEventWriter.putThread(data, JavaThreads.getParentThreadId(thread));
5151
JfrNativeEventWriter.endSmallEvent(data);
5252
}
5353
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/traceid/JfrTraceIdEpoch.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@
2525

2626
package com.oracle.svm.core.jfr.traceid;
2727

28-
import jdk.graal.compiler.api.replacements.Fold;
2928
import org.graalvm.nativeimage.ImageSingletons;
3029
import org.graalvm.nativeimage.Platform;
3130
import org.graalvm.nativeimage.Platforms;
3231

3332
import com.oracle.svm.core.Uninterruptible;
3433
import com.oracle.svm.core.thread.VMOperation;
3534

35+
import jdk.graal.compiler.api.replacements.Fold;
36+
3637
/**
3738
* Class holding the current JFR epoch. JFR uses an epoch system to safely separate constant pool
3839
* entries between adjacent chunks. Used to get the current or previous epoch and switch from one
@@ -42,7 +43,7 @@ public class JfrTraceIdEpoch {
4243
private static final long EPOCH_0_BIT = 0b01;
4344
private static final long EPOCH_1_BIT = 0b10;
4445

45-
private boolean epoch;
46+
private long epochId;
4647

4748
@Fold
4849
public static JfrTraceIdEpoch getInstance() {
@@ -56,26 +57,36 @@ public JfrTraceIdEpoch() {
5657
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
5758
public void changeEpoch() {
5859
assert VMOperation.isInProgressAtSafepoint();
59-
epoch = !epoch;
60+
epochId++;
6061
}
6162

6263
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
6364
long thisEpochBit() {
64-
return epoch ? EPOCH_1_BIT : EPOCH_0_BIT;
65+
return getEpoch() ? EPOCH_1_BIT : EPOCH_0_BIT;
6566
}
6667

6768
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
6869
long previousEpochBit() {
69-
return epoch ? EPOCH_0_BIT : EPOCH_1_BIT;
70+
return getEpoch() ? EPOCH_0_BIT : EPOCH_1_BIT;
7071
}
7172

7273
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
7374
public boolean currentEpoch() {
74-
return epoch;
75+
return getEpoch();
7576
}
7677

7778
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
7879
public boolean previousEpoch() {
79-
return !epoch;
80+
return !getEpoch();
81+
}
82+
83+
@Uninterruptible(reason = "Prevent epoch from changing.", callerMustBe = true)
84+
public long currentEpochId() {
85+
return epochId;
86+
}
87+
88+
@Uninterruptible(reason = "Prevent epoch from changing.", callerMustBe = true)
89+
private boolean getEpoch() {
90+
return (epochId & 1) == 0;
8091
}
8192
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaThreads.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.core.thread;
2626

27+
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
28+
2729
import java.lang.Thread.UncaughtExceptionHandler;
2830
import java.security.AccessControlContext;
2931
import java.security.AccessController;
@@ -220,8 +222,9 @@ static Target_java_lang_ThreadGroup toTarget(ThreadGroup threadGroup) {
220222
return Target_java_lang_ThreadGroup.class.cast(threadGroup);
221223
}
222224

225+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
223226
@SuppressFBWarnings(value = "BC", justification = "Cast for @TargetClass")
224-
private static Target_java_lang_VirtualThread toVirtualTarget(Thread thread) {
227+
public static Target_java_lang_VirtualThread toVirtualTarget(Thread thread) {
225228
return Target_java_lang_VirtualThread.class.cast(thread);
226229
}
227230

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_VirtualThread.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ public final class Target_java_lang_VirtualThread {
122122
@TargetElement(onlyWith = JDKLatest.class) @Alias @InjectAccessors(AlwaysFalseAccessor.class) boolean notified;
123123
// Checkstyle: resume
124124

125+
@Inject //
126+
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ResetToMinusOneTransformer.class) //
127+
public long jfrEpochId = -1;
128+
125129
@Alias
126130
private static native ForkJoinPool createDefaultScheduler();
127131

@@ -598,3 +602,10 @@ static Thread asThread(Object obj) {
598602
private VirtualThreadHelper() {
599603
}
600604
}
605+
606+
final class ResetToMinusOneTransformer implements FieldValueTransformer {
607+
@Override
608+
public Object transform(Object receiver, Object originalValue) {
609+
return -1L;
610+
}
611+
}

0 commit comments

Comments
 (0)