From 72898a638f1421651e9ca05c292ac443be853ad9 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Fri, 10 Dec 2021 12:27:06 -0800 Subject: [PATCH] Support Flight Recorder at image build time --- substratevm/mx.substratevm/mx_substratevm.py | 2 + substratevm/mx.substratevm/suite.py | 3 + .../SubstrateGraphBuilderPlugins.java | 35 ---------- .../src/com/oracle/svm/jfr/JfrEnabled.java | 17 ++--- .../src/com/oracle/svm/jfr/JfrFeature.java | 69 +++++++++++++++++-- ...un_jmx_mbeanserver_MXBeanIntrospector.java | 43 ++++++++++++ ..._com_sun_jmx_mbeanserver_MXBeanLookup.java | 42 +++++++++++ ...t_javax_management_MBeanServerFactory.java | 43 ++++++++++++ .../jfr/Target_jdk_jfr_FlightRecorder.java | 43 ++++++++++++ 9 files changed, 245 insertions(+), 52 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_com_sun_jmx_mbeanserver_MXBeanIntrospector.java create mode 100644 substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_com_sun_jmx_mbeanserver_MXBeanLookup.java create mode 100644 substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_javax_management_MBeanServerFactory.java create mode 100644 substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_FlightRecorder.java diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 12e685f7a117..3b7b9b1b94c8 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -322,6 +322,8 @@ def svm_gate_body(args, tasks): javac_command = ['--javac-command', ' '.join(javac_image_command(svmbuild_dir()))] helloworld(['--output-path', svmbuild_dir()] + javac_command) helloworld(['--output-path', svmbuild_dir(), '--shared']) # Build and run helloworld as shared library + if not mx.is_windows(): + helloworld(['--output-path', svmbuild_dir(), '-J-XX:StartFlightRecording=dumponexit=true']) # Build and run helloworld with FlightRecorder at image build time cinterfacetutorial([]) clinittest([]) diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index d363fa320d97..ce56383eb179 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -590,6 +590,9 @@ "jdk.unsupported" ], "requiresConcealed": { + "jdk.management": [ + "com.sun.management.internal" + ], "jdk.jfr": [ "jdk.jfr.internal", "jdk.jfr.internal.consumer", diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index 8480356b2202..c5cd0a3f774a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -188,8 +188,6 @@ public static void registerInvocationPlugins(AnnotationSubstitutionProcessor ann registerArrayPlugins(plugins, snippetReflection, parsingReason); registerClassPlugins(plugins, snippetReflection); registerEdgesPlugins(metaAccess, plugins); - registerJFRThrowablePlugins(plugins, replacements); - registerJFREventTokenPlugins(plugins, replacements); registerVMConfigurationPlugins(snippetReflection, plugins); registerPlatformPlugins(snippetReflection, plugins); registerAWTPlugins(plugins); @@ -969,39 +967,6 @@ protected static long longValue(GraphBuilderContext b, ResolvedJavaMethod target return node.asJavaConstant().asLong(); } - /* - * When Java Flight Recorder is enabled during image generation, the bytecodes of some methods - * get instrumented. Undo the instrumentation so that it does not end up in the generated image. - */ - - private static void registerJFRThrowablePlugins(InvocationPlugins plugins, Replacements replacements) { - Registration r = new Registration(plugins, "oracle.jrockit.jfr.jdkevents.ThrowableTracer", replacements).setAllowOverwrite(true); - r.register2("traceError", Error.class, String.class, new InvocationPlugin() { - @Override - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode throwable, ValueNode message) { - return true; - } - }); - r.register2("traceThrowable", Throwable.class, String.class, new InvocationPlugin() { - @Override - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode throwable, ValueNode message) { - return true; - } - }); - } - - private static void registerJFREventTokenPlugins(InvocationPlugins plugins, Replacements replacements) { - Registration r = new Registration(plugins, "com.oracle.jrockit.jfr.EventToken", replacements); - r.register1("isEnabled", Receiver.class, new InvocationPlugin() { - @Override - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { - receiver.get(); - b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(false)); - return true; - } - }); - } - private static void registerVMConfigurationPlugins(SnippetReflectionProvider snippetReflection, InvocationPlugins plugins) { Registration r = new Registration(plugins, ImageSingletons.class); r.register1("contains", Class.class, new InvocationPlugin() { diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrEnabled.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrEnabled.java index e491abd834d1..d575e42bf126 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrEnabled.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrEnabled.java @@ -26,10 +26,8 @@ import java.util.function.BooleanSupplier; -import org.graalvm.compiler.serviceprovider.JavaVersionUtil; - -import com.oracle.svm.core.OS; -import com.oracle.svm.core.VMInspectionOptions; +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; /** * Used to include/exclude JFR feature and substitutions. @@ -40,15 +38,8 @@ public boolean getAsBoolean() { return get(); } + @Fold public static boolean get() { - return VMInspectionOptions.AllowVMInspection.getValue() && jvmVersionSupported() && osSupported(); - } - - private static boolean jvmVersionSupported() { - return JavaVersionUtil.JAVA_SPEC >= 11; - } - - private static boolean osSupported() { - return OS.getCurrent() == OS.LINUX || OS.getCurrent() == OS.DARWIN; + return ImageSingletons.contains(JfrFeature.class); } } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java index a3a49d7852f9..c37431e503f8 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java @@ -31,14 +31,17 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.function.BooleanSupplier; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; import org.graalvm.nativeimage.hosted.RuntimeReflection; +import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport; +import com.oracle.svm.core.OS; +import com.oracle.svm.core.VMInspectionOptions; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.hub.DynamicHub; @@ -47,12 +50,16 @@ import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.thread.ThreadListenerFeature; import com.oracle.svm.core.thread.ThreadListenerSupport; +import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.jfr.traceid.JfrTraceId; import com.oracle.svm.jfr.traceid.JfrTraceIdEpoch; import com.oracle.svm.jfr.traceid.JfrTraceIdMap; import com.oracle.svm.util.ModuleSupport; +import com.oracle.svm.util.ReflectionUtil; +import com.sun.management.HotSpotDiagnosticMXBean; +import com.sun.management.internal.PlatformMBeanProviderImpl; import jdk.jfr.Configuration; import jdk.jfr.Event; @@ -100,13 +107,59 @@ * consistent state). * */ -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) @AutomaticFeature public class JfrFeature implements Feature { + public static final class JfrHostedEnabled implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return ImageSingletons.contains(JfrFeature.class) && ImageSingletons.lookup(JfrFeature.class).hostedEnabled; + } + } + + private final boolean hostedEnabled; + + public JfrFeature() { + hostedEnabled = Boolean.valueOf(getDiagnosticBean().getVMOption("FlightRecorder").getValue()); + } + @Override public boolean isInConfiguration(IsInConfigurationAccess access) { - return JfrEnabled.get(); + boolean systemSupported = jvmVersionSupported() && osSupported(); + if (hostedEnabled && !systemSupported) { + throw UserError.abort("FlightRecorder cannot be used to profile the image generator on this platform. " + + "The image generator can only be profiled on platforms where FlightRecoder is also supported at run time."); + } + boolean runtimeEnabled = VMInspectionOptions.AllowVMInspection.getValue(); + if (hostedEnabled && !runtimeEnabled) { + // Checkstyle: stop + System.err.println("Warning: When FlightRecoder is used to profile the image generator, it is also automatically enabled in the native image at run time. " + + "This can affect the measurements because it can can make the image larger and image build time longer."); + // Checkstyle: resume + runtimeEnabled = true; + } + return runtimeEnabled && systemSupported; + } + + private static boolean jvmVersionSupported() { + return JavaVersionUtil.JAVA_SPEC >= 11; + } + + private static boolean osSupported() { + return OS.getCurrent() == OS.LINUX || OS.getCurrent() == OS.DARWIN; + } + + /** + * We cannot use the proper way of looking up the bean via + * {@link java.lang.management.ManagementFactory} because that initializes too many classes at + * image build time that we want to initialize only at run time. + */ + private static HotSpotDiagnosticMXBean getDiagnosticBean() { + try { + return (HotSpotDiagnosticMXBean) ReflectionUtil.lookupMethod(PlatformMBeanProviderImpl.class, "getDiagnosticMXBean").invoke(null); + } catch (ReflectiveOperationException ex) { + throw VMError.shouldNotReachHere(ex); + } } @Override @@ -134,6 +187,14 @@ public void afterRegistration(AfterRegistrationAccess access) { JfrSerializerSupport.get().register(new JfrFrameTypeSerializer()); JfrSerializerSupport.get().register(new JfrThreadStateSerializer()); ThreadListenerSupport.get().register(SubstrateJVM.getThreadLocal()); + + if (hostedEnabled) { + RuntimeClassInitializationSupport rci = ImageSingletons.lookup(RuntimeClassInitializationSupport.class); + rci.initializeAtBuildTime("jdk.management.jfr", "Allow FlightRecorder to be used at image build time"); + rci.initializeAtBuildTime("com.sun.jmx.mbeanserver", "Allow FlightRecorder to be used at image build time"); + rci.initializeAtBuildTime("com.sun.jmx.defaults", "Allow FlightRecorder to be used at image build time"); + rci.initializeAtBuildTime("java.beans", "Allow FlightRecorder to be used at image build time"); + } } @Override diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_com_sun_jmx_mbeanserver_MXBeanIntrospector.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_com_sun_jmx_mbeanserver_MXBeanIntrospector.java new file mode 100644 index 000000000000..9af9b02682f6 --- /dev/null +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_com_sun_jmx_mbeanserver_MXBeanIntrospector.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. 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.jfr; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.TargetClass; + +@TargetClass(className = "com.sun.jmx.mbeanserver.MXBeanIntrospector", onlyWith = JfrFeature.JfrHostedEnabled.class) +final class Target_com_sun_jmx_mbeanserver_MXBeanIntrospector { + + /* Reset caches that are used at image build time when FlightRecorder is enabled. */ + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, declClassName = "com.sun.jmx.mbeanserver.MXBeanIntrospector") // + private static Target_com_sun_jmx_mbeanserver_MXBeanIntrospector instance; + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, declClassName = "com.sun.jmx.mbeanserver.MBeanIntrospector$MBeanInfoMap") // + private static Target_com_sun_jmx_mbeanserver_MBeanIntrospector_MBeanInfoMap mbeanInfoMap; +} + +@TargetClass(className = "com.sun.jmx.mbeanserver.MBeanIntrospector", innerClass = "MBeanInfoMap") +final class Target_com_sun_jmx_mbeanserver_MBeanIntrospector_MBeanInfoMap { +} diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_com_sun_jmx_mbeanserver_MXBeanLookup.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_com_sun_jmx_mbeanserver_MXBeanLookup.java new file mode 100644 index 000000000000..14268ed9e6d1 --- /dev/null +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_com_sun_jmx_mbeanserver_MXBeanLookup.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. 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.jfr; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.jfr.JfrFeature.JfrHostedEnabled; + +@TargetClass(className = "com.sun.jmx.mbeanserver.MXBeanLookup", onlyWith = JfrHostedEnabled.class) +final class Target_com_sun_jmx_mbeanserver_MXBeanLookup { + + /* Reset caches that are used at image build time when FlightRecorder is enabled. */ + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, declClassName = "com.sun.jmx.mbeanserver.WeakIdentityHashMap") // + private static Target_com_sun_jmx_mbeanserver_WeakIdentityHashMap mbscToLookup; +} + +@TargetClass(className = "com.sun.jmx.mbeanserver.WeakIdentityHashMap") +final class Target_com_sun_jmx_mbeanserver_WeakIdentityHashMap { +} diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_javax_management_MBeanServerFactory.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_javax_management_MBeanServerFactory.java new file mode 100644 index 000000000000..56197aa94817 --- /dev/null +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_javax_management_MBeanServerFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. 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.jfr; + +import java.util.ArrayList; + +import javax.management.MBeanServerBuilder; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.TargetClass; + +@TargetClass(className = "javax.management.MBeanServerFactory", onlyWith = JfrFeature.JfrHostedEnabled.class) +final class Target_javax_management_MBeanServerFactory { + + /* Reset caches that are used at image build time when FlightRecorder is enabled. */ + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias) // + private static ArrayList mBeanServerList = new ArrayList<>(); + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias) // + private static MBeanServerBuilder builder = null; +} diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_FlightRecorder.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_FlightRecorder.java new file mode 100644 index 000000000000..f02e5d76386f --- /dev/null +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_FlightRecorder.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. 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.jfr; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.jfr.JfrFeature.JfrHostedEnabled; + +import jdk.jfr.FlightRecorder; + +@TargetClass(value = jdk.jfr.FlightRecorder.class, onlyWith = JfrHostedEnabled.class) +final class Target_jdk_jfr_FlightRecorder { + /* + * Ignore all state of the FlightRecorder maintained when profiling the image generator itself. + */ + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) // + private static FlightRecorder platformRecorder; + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) // + private static boolean initialized; +}