Skip to content

Commit 7927a51

Browse files
[GR-69121] Skip emission of internal JFR ThreadPark events.
PullRequest: graal/21983
2 parents c21b9c6 + 9d0f64a commit 7927a51

File tree

5 files changed

+167
-19
lines changed

5 files changed

+167
-19
lines changed

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
2-
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
3-
* Copyright (c) 2022, 2022, Red Hat Inc. All rights reserved.
2+
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2022, 2025, Red Hat Inc. All rights reserved.
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
66
* This code is free software; you can redistribute it and/or modify it
@@ -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;
@@ -37,10 +36,13 @@
3736
import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess;
3837
import com.oracle.svm.core.jfr.JfrTicks;
3938
import com.oracle.svm.core.jfr.SubstrateJVM;
39+
import com.oracle.svm.core.monitor.JavaMonitorQueuedSynchronizer;
40+
41+
import jdk.graal.compiler.word.Word;
4042

4143
public class ThreadParkEvent {
4244
public static void emit(long startTicks, Object obj, boolean isAbsolute, long time) {
43-
if (HasJfrSupport.get()) {
45+
if (HasJfrSupport.get() && !isInternalPark(obj)) {
4446
emit0(startTicks, obj, isAbsolute, time);
4547
}
4648
}
@@ -76,4 +78,22 @@ private static void emit0(long startTicks, Object obj, boolean isAbsolute, long
7678
JfrNativeEventWriter.endSmallEvent(data);
7779
}
7880
}
81+
82+
/**
83+
* Skip emission if this is an internal park ({@link JavaMonitorWaitEvent} or
84+
* {@link JavaMonitorEnterEvent} will be emitted instead).
85+
*/
86+
private static boolean isInternalPark(Object obj) {
87+
if (obj == null) {
88+
return false;
89+
}
90+
91+
Class<?> parkedClass = obj.getClass();
92+
if (JavaMonitorQueuedSynchronizer.class.isAssignableFrom(parkedClass)) {
93+
return true;
94+
}
95+
96+
Class<?> enclosingClass = parkedClass.getEnclosingClass();
97+
return enclosingClass != null && JavaMonitorQueuedSynchronizer.class.isAssignableFrom(enclosingClass);
98+
}
7999
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/JavaMonitorQueuedSynchronizer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
* </ul>
6161
*/
6262
@BasedOnJDKClass(AbstractQueuedLongSynchronizer.class)
63-
abstract class JavaMonitorQueuedSynchronizer {
63+
public abstract class JavaMonitorQueuedSynchronizer {
6464
// Node status bits, also used as argument and return values
6565
static final int WAITING = 1; // must be 1
6666
static final int CANCELLED = 0x80000000; // must be negative

substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorEnterEvent.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,24 @@ public void test() throws Throwable {
5353
String[] events = new String[]{JfrEvent.JavaMonitorEnter.getName()};
5454
Recording recording = startRecording(events);
5555

56-
Runnable first = () -> {
56+
firstThread = new Thread(() -> {
5757
try {
5858
helper.doWork();
5959
} catch (InterruptedException e) {
6060
throw new RuntimeException(e);
6161
}
62-
};
62+
});
6363

64-
Runnable second = () -> {
64+
secondThread = new Thread(() -> {
6565
try {
6666
passedCheckpoint = true;
6767
helper.doWork();
6868
} catch (InterruptedException e) {
6969
throw new RuntimeException(e);
7070
}
71-
};
72-
firstThread = new Thread(first);
73-
secondThread = new Thread(second);
71+
});
7472

73+
/* Start the first thread so that it can then start the second thread. */
7574
firstThread.start();
7675

7776
firstThread.join();

substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorInflateEvent.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ private void test(Object obj) throws Throwable {
7373
}
7474

7575
private void test(Object obj, String className) throws Throwable {
76-
Runnable first = () -> {
76+
firstThread = new Thread(() -> {
7777
try {
7878
synchronized (obj) {
7979
secondThread.start();
@@ -85,25 +85,25 @@ private void test(Object obj, String className) throws Throwable {
8585
} catch (InterruptedException e) {
8686
throw new RuntimeException(e);
8787
}
88-
};
88+
});
8989

90-
Runnable second = () -> {
90+
secondThread = new Thread(() -> {
9191
passedCheckpoint = true;
9292
synchronized (obj) {
9393
GraalDirectives.blackhole(obj);
9494
}
95-
};
95+
});
9696

9797
expectedClassName = className;
98-
passedCheckpoint = false;
99-
firstThread = new Thread(first);
100-
secondThread = new Thread(second);
10198

10299
/* Now that the data is prepared, start the JFR recording. */
103100
String[] events = new String[]{JfrEvent.JavaMonitorInflate.getName()};
104101
Recording recording = startRecording(events);
105102

106-
/* Generate event with "Monitor Enter" cause. */
103+
/*
104+
* Generate event with "Monitor Enter" cause. Start the first thread so that it can then
105+
* start the second thread.
106+
*/
107107
firstThread.start();
108108

109109
firstThread.join();
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2022, 2025, Red Hat Inc. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
27+
package com.oracle.svm.test.jfr;
28+
29+
import static org.junit.Assert.assertFalse;
30+
import static org.junit.Assert.assertTrue;
31+
import static org.junit.Assert.fail;
32+
33+
import java.util.List;
34+
import java.util.concurrent.locks.LockSupport;
35+
36+
import org.junit.Test;
37+
38+
import com.oracle.svm.core.jfr.JfrEvent;
39+
import com.oracle.svm.core.util.TimeUtils;
40+
41+
import jdk.jfr.Recording;
42+
import jdk.jfr.consumer.RecordedClass;
43+
import jdk.jfr.consumer.RecordedEvent;
44+
45+
public class TestOmitInternalParkEvents extends JfrRecordingTest {
46+
private static final int MILLIS = 100;
47+
private final MonitorHelper monitorHelper = new MonitorHelper();
48+
private Thread secondThread;
49+
private volatile boolean passedCheckpoint;
50+
51+
@Test
52+
public void test() throws Throwable {
53+
String[] events = new String[]{JfrEvent.JavaMonitorEnter.getName(), JfrEvent.JavaMonitorWait.getName(), JfrEvent.ThreadPark.getName()};
54+
Recording recording = startRecording(events);
55+
56+
/* Generate monitor enter events. */
57+
Thread firstThread = new Thread(() -> {
58+
try {
59+
monitorHelper.doWork();
60+
} catch (InterruptedException e) {
61+
throw new RuntimeException(e);
62+
}
63+
});
64+
65+
secondThread = new Thread(() -> {
66+
try {
67+
passedCheckpoint = true;
68+
monitorHelper.doWork();
69+
} catch (InterruptedException e) {
70+
throw new RuntimeException(e);
71+
}
72+
});
73+
74+
/* Start the first thread so that it can then start the second thread. */
75+
firstThread.start();
76+
77+
firstThread.join();
78+
secondThread.join();
79+
80+
/* Generate monitor wait events. */
81+
try {
82+
monitorHelper.waitUntilTimeout();
83+
} catch (InterruptedException e) {
84+
fail(e.getMessage());
85+
}
86+
87+
/* Generate thread park events. */
88+
LockSupport.parkNanos(this, TimeUtils.millisToNanos(MILLIS));
89+
90+
stopRecording(recording, this::validateEvents);
91+
}
92+
93+
private void validateEvents(List<RecordedEvent> events) {
94+
boolean found = false;
95+
for (RecordedEvent event : events) {
96+
if (event.getEventType().getName().equals(JfrEvent.ThreadPark.getName())) {
97+
RecordedClass parkedClass = event.getValue("parkedClass");
98+
if (parkedClass != null) {
99+
String parkedClassName = parkedClass.getName();
100+
assertFalse(parkedClassName.contains("JavaMonitor"));
101+
found = true;
102+
}
103+
}
104+
}
105+
assertTrue("Expected jdk.ThreadPark event not found", found);
106+
}
107+
108+
private final class MonitorHelper {
109+
private synchronized void doWork() throws InterruptedException {
110+
if (Thread.currentThread().equals(secondThread)) {
111+
/* The second thread doesn't need to do any work. */
112+
return;
113+
}
114+
115+
/* Start the second thread while this thread holds the lock. */
116+
secondThread.start();
117+
118+
/* Spin until the second thread blocks. */
119+
while (!secondThread.getState().equals(Thread.State.BLOCKED) || !passedCheckpoint) {
120+
Thread.sleep(100);
121+
}
122+
Thread.sleep(MILLIS);
123+
}
124+
125+
private synchronized void waitUntilTimeout() throws InterruptedException {
126+
wait(MILLIS);
127+
}
128+
}
129+
}

0 commit comments

Comments
 (0)