Skip to content
Closed
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 @@ -24,14 +24,15 @@
*/
package com.oracle.svm.core.jfr;

import java.util.HashSet;
import java.util.Set;
import java.util.function.BooleanSupplier;

import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.jdk.JDK17OrEarlier;
import com.oracle.svm.util.ReflectionUtil;

/**
Expand Down Expand Up @@ -60,12 +61,19 @@ public final class JfrEvent {
public static final JfrEvent SafepointEnd = create("jdk.SafepointEnd");
public static final JfrEvent ExecuteVMOperation = create("jdk.ExecuteVMOperation");
public static final JfrEvent JavaMonitorEnter = create("jdk.JavaMonitorEnter");
public static final JfrEvent ThreadSleep = create("jdk.ThreadSleep", JDK17OrEarlier.class);
public static final JfrEvent ThreadSleep = create("jdk.ThreadSleep");
public static final JfrEvent ThreadPark = create("jdk.ThreadPark");
public static final JfrEvent JavaMonitorWait = create("jdk.JavaMonitorWait");

private final long id;
private final String name;
private static Set<Long> mirrorEvents = new HashSet<>();
public static boolean isMirrorEvent(long id) {
return mirrorEvents.contains(id);
}
public static boolean addMirrorEvent(long id) {
return mirrorEvents.add(id);
}

@Platforms(Platform.HOSTED_ONLY.class)
private static JfrEvent create(String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,25 @@
*/
package com.oracle.svm.core.jfr;

import java.util.Collections;
import java.util.HashMap;

import org.graalvm.compiler.core.common.NumUtil;
import org.graalvm.compiler.options.OptionsParser;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.util.VMError;
import com.oracle.svm.core.jdk.JDK19OrLater;

import jdk.jfr.internal.PlatformEventType;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.TypeLibrary;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.SubstrateUtil;

/**
* This class caches all JFR metadata types. This is mainly necessary because
Expand All @@ -45,12 +52,26 @@
public class JfrMetadataTypeLibrary {
private static final HashMap<String, Type> types = new HashMap<>();

private static void addMirrorEvent(Class<?> svmClass, Class<?> internalClass) {
PlatformEventType et = (PlatformEventType) TypeLibrary.createType(svmClass, Collections.emptyList(), Collections.emptyList());
et.setId(Type.getTypeId(internalClass));
types.put(et.getName(), et);
JfrEvent.addMirrorEvent(et.getId());
}

private static void addMirrorEvents() {
if (JavaVersionUtil.JAVA_SPEC > 17) {
addMirrorEvent(com.oracle.svm.core.jfr.events.ThreadSleepEvent.class, Target_jdk_internal_event_ThreadSleepEvent.class);
}
}

private static synchronized HashMap<String, Type> getTypes() {
if (types.isEmpty()) {
for (Type type : TypeLibrary.getInstance().getTypes()) {
assert !types.containsKey(type.getName());
types.put(type.getName(), type);
}
addMirrorEvents();
}
return types;
}
Expand Down Expand Up @@ -104,3 +125,43 @@ private static Type getMostSimilar(String missingTypeName) {
return mostSimilar;
}
}

@TargetClass(className = "jdk.internal.event.ThreadSleepEvent", onlyWith = JDK19OrLater.class)
final class Target_jdk_internal_event_ThreadSleepEvent {
}

@TargetClass(className = "jdk.jfr.internal.PlatformEventType")
final class Target_jdk_jfr_internal_PlatformEventType {
@Alias private boolean isJVM;
@Alias private boolean isMethodSampling;
@Alias private long period;
@Alias private boolean enabled;
@Alias private native void updateCommittable();

@Substitute
public void setEnabled(boolean enabled) {
this.enabled = enabled;
updateCommittable();
Target_jdk_jfr_internal_Type type = SubstrateUtil.cast(this, Target_jdk_jfr_internal_Type.class);
long id = type.getId();
if (isJVM) {
if (isMethodSampling) {
long p = enabled ? period : 0;
SubstrateJVM.get().setMethodSamplingInterval(id, p);
} else {
SubstrateJVM.get().setEnabled(id, enabled);
}
} else if (JfrEvent.isMirrorEvent(id)) {
// in openjdk, mirror events directly check their isEnabled boolean.
// But in svm these flags need to be stored along with the flags for JVM native events
SubstrateJVM.get().setEnabled(id, enabled);
}
}
}

@TargetClass(className = "jdk.jfr.internal.Type")
final class Target_jdk_jfr_internal_Type {
@Alias
public native long getId();

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

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

import jdk.jfr.Event;
import org.graalvm.nativeimage.StackValue;

import com.oracle.svm.core.Uninterruptible;
Expand All @@ -35,8 +36,17 @@
import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess;
import com.oracle.svm.core.jfr.JfrTicks;
import com.oracle.svm.core.jfr.SubstrateJVM;
import jdk.jfr.Category;
import jdk.jfr.Label;
import jdk.jfr.Name;
import jdk.jfr.Timespan;

public class ThreadSleepEvent {
@Category("Java Application")
@Label("Java Thread Sleep")
@Name("jdk.ThreadSleep")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that really work reliably? If I understand everything correctly, then the following is happening:

  • HotSpot uses jdk.jfr.events.ThreadSleepEvent as the mirror event.
  • HotSpot registers jdk.internal.event.ThreadSleepEvent as the thread sleep event. This creates a PlatformEventType object at build time that ends up in the image heap (instance A).
  • You create a new PlatformEventType object for the SVM-internal class com.oracle.svm.core.jfr.events.ThreadSleepEvent at build time (instance B). This new object reuses the event id of instance A but otherwise behaves like a VM-internal event (e.g., SubstrateJVM.eventSettings is used to determine if the event is enabled or disabled).
  • You unconditionally enable the thread sleep event at build time (SubstrateJVM.handleMirrorEvents()).

I see the following issues:

  • What if the event should not be enabled by default?
  • What if the event is enabled at runtime via jdk.internal.event.ThreadSleepEvent? I don't think that the updated value will be used because JDK events directly store in their PlatformEventType object if the event is enabled. However, we would access SubstrateJVM.eventSettings to determine if the event is enabled.

Copy link
Collaborator Author

@roberttoyonaga roberttoyonaga Oct 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review Christian! Yes that is what I was doing. I was under the impression that built in events were enabled by default. I have removed that unconditional enablement and instead substituted PlatformEventType.setEnabled(boolean). This way changes can be propagated to the SubstrateJVM.eventSettings if the event happens to be one of the supported mirror event types. Please let me know if this is an acceptable solution.

I think the thread sleep event was moved from hotspot native code to java code to be able to emit the events for virtual threads as well.

public class ThreadSleepEvent extends Event {
@Label("Sleep Time")
@Timespan() public long time;

public static void emit(long time, long startTicks) {
if (com.oracle.svm.core.jfr.HasJfrSupport.get()) {
Expand Down