From 432065a55231c2ff9c66fa37c36448f7bbc7f2fc Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 16 Aug 2022 08:56:09 +0300 Subject: [PATCH 1/2] Add missing header to virtual methods CSV call-tree file Fixes: #4817 --- .../src/com/oracle/graal/pointsto/reports/CallTreePrinter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java index 809700c3985a..e44cd34d434c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java @@ -461,7 +461,7 @@ private static void addVirtualMethodEdge(int startId, InvokeNode invoke, int end } private static void printVirtualNodes(Map, Integer> virtualNodes, PrintWriter writer) { - writer.println(convertToCSV("Id", "Name", "Type", "Parameters", "Return", "Display")); + writer.println(convertToCSV("Id", "Name", "Type", "Parameters", "Return", "Display", "Flags")); virtualNodes.entrySet().stream() .map(CallTreePrinter::virtualMethodAndIdInfo) .map(CallTreePrinter::convertToCSV) From 8e6bbfd54e5df0aec2fcc8fe8cd68bbb5dcc4d4b Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Tue, 16 Aug 2022 09:18:17 +0200 Subject: [PATCH 2/2] Introduce `--enable-monitoring` option. --- .../native-image/BuildOptions.md | 1 + docs/reference-manual/native-image/JFR.md | 4 +- ...uild-and-run-native-executable-with-jfr.md | 12 ++- ...create-heap-dump-from-native-executable.md | 30 ++++--- docs/tools/visualvm.md | 9 +- sdk/mx.sdk/mx_sdk_vm_impl.py | 3 +- substratevm/CHANGELOG.md | 1 + .../jvmstat/PosixPerfMemoryProvider.java | 4 +- .../svm/core/DumpHeapOnSignalFeature.java | 85 ++++++++++++++++++ ...DumpRuntimeCompilationOnSignalFeature.java | 87 ++++++++++++++++++ ...a => DumpThreadStacksOnSignalFeature.java} | 90 ++----------------- .../com/oracle/svm/core/JavaMainWrapper.java | 7 +- .../oracle/svm/core/VMInspectionOptions.java | 86 +++++++++++++++++- .../com/oracle/svm/core/jfr/JfrFeature.java | 2 +- .../svm/core/jvmstat/PerfDataFeature.java | 6 +- .../core/sampler/SubstrateSigprofHandler.java | 6 +- .../native-image.properties | 2 +- 17 files changed, 308 insertions(+), 127 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpHeapOnSignalFeature.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpRuntimeCompilationOnSignalFeature.java rename substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/{VMInspection.java => DumpThreadStacksOnSignalFeature.java} (60%) diff --git a/docs/reference-manual/native-image/BuildOptions.md b/docs/reference-manual/native-image/BuildOptions.md index fd704fd4405b..61e5f23982de 100644 --- a/docs/reference-manual/native-image/BuildOptions.md +++ b/docs/reference-manual/native-image/BuildOptions.md @@ -28,6 +28,7 @@ The following options are supported across both GraalVM Community and Enterprise * `--enable-https`: enable HTTPS support in a native executable * `--enable-preview`: allow classes to depend on preview features of this release * `--enable-url-protocols`: list comma-separated URL protocols to enable +* `--enable-monitoring`: enable monitoring features that allow the VM to be inspected at run time. Comma-separated list can contain 'heapdump', 'jfr', 'jvmstat', or 'all'. For example: `--enable-monitoring=heapdump,jvmstat`. * `--features`: a comma-separated list of fully qualified [Feature implementation classes](https://www.graalvm.org/sdk/javadoc/index.html?org/graalvm/nativeimage/hosted/Feature.html) * `--force-fallback`: force building of a fallback native executable * `--gc=`: select Native Image garbage collector implementation. Allowed options for `` are: `G1` for G1 garbage collector (**GraalVM Enterprise only**); `epsilon` for Epsilon garbage collector; `serial` for Serial garbage collector (default). diff --git a/docs/reference-manual/native-image/JFR.md b/docs/reference-manual/native-image/JFR.md index d920c7167ba2..cebc7cbd9926 100644 --- a/docs/reference-manual/native-image/JFR.md +++ b/docs/reference-manual/native-image/JFR.md @@ -17,9 +17,9 @@ To record JFR events when running a native executable, JFR support and JFR recor To build a native executable with the JFR events support, you first need to include JFR at build time, then enable the system, start a recording, and configure logging at native executable run time. -To build a native executable with JFR, use the `-H:+AllowVMInspection` flag: +To build a native executable with JFR, use the `--enable-monitoring=jfr` flag: ```shell -native-image -H:+AllowVMInspection JavaApplication +native-image --enable-monitoring=jfr JavaApplication ``` To enable the system, start a recording, and configure logging at run time, the following options are supported: diff --git a/docs/reference-manual/native-image/guides/build-and-run-native-executable-with-jfr.md b/docs/reference-manual/native-image/guides/build-and-run-native-executable-with-jfr.md index 51eb4fa53aef..27f0e1c800e6 100644 --- a/docs/reference-manual/native-image/guides/build-and-run-native-executable-with-jfr.md +++ b/docs/reference-manual/native-image/guides/build-and-run-native-executable-with-jfr.md @@ -12,14 +12,12 @@ GraalVM Native Image supports JFR events and users can use [`jdk.jfr.Event`](htt To record JFR events when running a native executable, JFR support and JFR recording must be enabled, and this guide covers how to do that. -> Note: JFR events recording is not supported on GraalVM distribution for Windows. JFR is only supported with native executables built on GraalVM JDK 11. +> Note: JFR events recording is not supported on GraalVM JDK for Windows. JFR is only supported with native executables built on GraalVM JDK 11. ## Enable JFR Support and Record Events at Run Time -To build a native executable with the JFR events support, you first need to include JFR at build time, then enable the system, start a recording, and configure logging at native executable run time. -The following options are supported: - * `-H:+AllowVMInspection`: enable the VM inspection - * `-XX:+FlightRecorder`: use to enable JFR +To build a native executable with the JFR events support, you first need to add the `--enable-monitoring=jfr` option when invoking the `native-image` tool. Then enable the system, start a recording, and configure logging at native executable run time: + * `-XX:+FlightRecorder`: use to enable JFR at run time * `-XX:StartFlightRecording`: use to start a recording on application's startup * `-XX:FlightRecorderLogging`: use to configure the log output for the JFR system @@ -61,9 +59,9 @@ Follow the steps below to practice building a native executable with JFR support 3. Build a native executable with the VM inspection enabled: ```shell - $JAVA_HOME/bin/native-image -H:+AllowVMInspection JFRDemo + $JAVA_HOME/bin/native-image --enable-monitoring=jfr JFRDemo ``` - The `-H:+AllowVMInspection` option enables features such as JFR that can be used to inspect the VM. + The `--enable-monitoring=jfr` option enables features such as JFR that can be used to inspect the VM. 4. Run the executable and start recording: ```shell diff --git a/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md b/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md index b7920cdd5518..4b014453f8a2 100644 --- a/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md +++ b/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md @@ -9,16 +9,24 @@ permalink: /reference-manual/native-image/guides/create-heap-dump/ You can create a heap dump of a running executable to monitor its execution. Just like any other Java heap dump, it can be opened with the [VisualVM](../../../tools/visualvm.md) tool. -To enable heap dump support, native executables must be built with the `-H:+AllowVMInspection` option. Heap dumps can then be created in three different ways: +To enable heap dump support, native executables must be built with the `--enable-monitoring=heapdump` option. Heap dumps can then be created in three different ways: -1. Dump the initial heap of a native executable using the `-XX:+DumpHeapAndExit` command-line option. -2. Create heap dumps sending `USR1` (other supported signals are `QUIT/BREAK` for stack dumps and `USR2` to dump runtime compilation information). -3. Create a heap dumps programmatically through the [`org.graalvm.nativeimage.VMRuntime#dumpHeap`](https://github.com/oracle/graal/blob/master/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java) API. +1. Create heap dumps with VisualVM. +2. Dump the initial heap of a native executable using the `-XX:+DumpHeapAndExit` command-line option. +3. Create heap dumps sending a `SIGUSR1` signal at run time. +4. Create heap dumps programmatically using the [`org.graalvm.nativeimage.VMRuntime#dumpHeap`](https://github.com/oracle/graal/blob/master/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java) API. All three approaches are described below. >Note: Creating heap dumps is not available on the Microsoft Windows platform. +## Create Heap Dumps with VisualVM + +A convenient way to create heap dumps is to use [VisualVM](../../../tools/visualvm.md). +For this, you need to add `jvmstat` to the `--enable-monitoring` option (for example, `--enable-monitoring=heapdump,jvmstat`). +This will allow VisualVM to pick up and list running Native Image processes. +You can then request heap dumps in the same way you can request them when your application runs on the JVM (for example, right-click on the process, then select "Heap Dump"). + ## Dump the Initial Heap of a Native Executable Use the `-XX:+DumpHeapAndExit` command-line option to dump the initial heap of a native executable. @@ -26,17 +34,17 @@ This can be useful to identify which objects the Native Image build process allo For a HelloWorld example, use the option as follows: ```shell -$GRAALVM_HOME/bin/native-image HelloWorld -H:+AllowVMInspection +$GRAALVM_HOME/bin/native-image HelloWorld --enable-monitoring=heapdump ./helloworld -XX:+DumpHeapAndExit Heap dump created at '/path/to/helloworld.hprof'. ``` -## Handle USR1 Signals +## Create Heap Dumps with SIGUSR1 (Linux/macOS only) The following example is a simple multi-threaded Java application that runs for 60 seconds. -This provides you with enough time to send it a `USR1` signal. The application will handle the signal and create a heap dump in the application's working directory. The heap dump will contain the `Collection` of `Person`s referenced by the static variable `CROWD`. +This provides you with enough time to send it a `SIGUSR1` signal. The application will handle the signal and create a heap dump in the application's working directory. The heap dump will contain the `Collection` of `Person`s referenced by the static variable `CROWD`. -Follow these steps to build a native executable that will produce a heap dump when it receives a `USR1` signal. +Follow these steps to build a native executable that will produce a heap dump when it receives a `SIGUSR1` signal. 1. Save the following code in a file named _SVMHeapDump.java_: ```java @@ -122,11 +130,11 @@ Follow these steps to build a native executable that will produce a heap dump wh ```shell $JAVA_HOME/bin/javac SVMHeapDump.java ``` - Build a native executable using the `-H:+AllowVMInspection` command-line option. - (This causes the resulting native executable to produce a heap dump when it receives a `USR1` signal.) + Build a native executable using the `--enable-monitoring=heapdump` command-line option. + (This causes the resulting native executable to produce a heap dump when it receives a `SIGUSR1` signal.) ```shell - $JAVA_HOME/bin/native-image SVMHeapDump -H:+AllowVMInspection + $JAVA_HOME/bin/native-image SVMHeapDump --enable-monitoring=heapdump ``` (The `native-image` builder creates a native executable from the `SVMHeapDump.class`. diff --git a/docs/tools/visualvm.md b/docs/tools/visualvm.md index 4a3fbfa2a9aa..85941e03893d 100644 --- a/docs/tools/visualvm.md +++ b/docs/tools/visualvm.md @@ -36,12 +36,9 @@ To capture a heap dump of, for example, a Ruby application for later analysis, s Then right-click its process in VisualVM and invoke the Heap Dump action. A new heap viewer for the Ruby process opens. -__Note:__ [Native Image](../reference-manual/native-image/README.md) does not implement the JVMTI agent, so triggering heap dump creation from the Applications area is impossible. -Apply the `-H:+AllowVMInspection` flag with the `native-image` tool for native image processes. -This way your application will handle signals and capture a heap dump when it receives the SIGUSR1 signal. -The guest language REPL process must be started also with the `--jvm` flag to monitor it using VisualVM. -This functionality is available with [GraalVM Enterprise Edition](https://www.oracle.com/downloads/graalvm-downloads.html). -It is not available in GraalVM Community Edition. +__Note:__ Heap dump support must be explicitly enabled when using [Native Image](../reference-manual/native-image/README.md). +Add the `--enable-monitoring=heapdump,jvmstat` option when invoking the `native-image` tool to enable the heap dump feature and allow VisualVM to detect native executables via `jvmstat`. +This way your application will handle signals and capture a heap dump when it receives the `SIGUSR1` signal. See the [Generating Native Heap Dumps](../reference-manual/native-image/guides/create-heap-dump-from-native-executable.md) page for details on capturing heap dumps from a native image process. ### Analyzing Objects diff --git a/sdk/mx.sdk/mx_sdk_vm_impl.py b/sdk/mx.sdk/mx_sdk_vm_impl.py index 43456a4778a4..928441a35f4e 100644 --- a/sdk/mx.sdk/mx_sdk_vm_impl.py +++ b/sdk/mx.sdk/mx_sdk_vm_impl.py @@ -1341,7 +1341,8 @@ def contents(self): if isinstance(image_config, mx_sdk.LauncherConfig) or (isinstance(image_config, mx_sdk.LanguageLibraryConfig) and image_config.launchers): build_args += [ '--install-exit-handlers', - '-H:+AllowVMInspection', + '--enable-monitoring=all', + '-H:+DumpRuntimeCompilationOnSignal', ] if isinstance(image_config, (mx_sdk.LauncherConfig, mx_sdk.LanguageLibraryConfig)): diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index f009c25a25e8..6ad2918c1eb8 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -9,6 +9,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-40170) Add `--silent` option to silence the build output. * (GR-39475) Add initial support for jvmstat. * (GR-39563) Add support for JDK 19 and Project Loom Virtual Threads (JEP 425) for high-throughput lightweight concurrency. Enable on JDK 19 with `native-image --enable-preview`. +* (GR-40264) Add `--enable-monitoring=` option to enable fine-grained control over monitoring features enabled in native executables. `-H:±AllowVMInspection` is now deprecated and will be removed in a future release. ## Version 22.2.0 * (GR-20653) Re-enable the usage of all CPU features for JIT compilation on AMD64. diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/jvmstat/PosixPerfMemoryProvider.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/jvmstat/PosixPerfMemoryProvider.java index 13a60b072560..06d2d1dd7135 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/jvmstat/PosixPerfMemoryProvider.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/jvmstat/PosixPerfMemoryProvider.java @@ -60,7 +60,7 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; -import com.oracle.svm.core.VMInspection; +import com.oracle.svm.core.VMInspectionOptions; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.TargetClass; @@ -276,7 +276,7 @@ public void teardown() { class PosixPerfMemoryFeature implements Feature { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { - return VMInspection.isEnabled(); + return VMInspectionOptions.hasJvmstatSupport(); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpHeapOnSignalFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpHeapOnSignalFeature.java new file mode 100644 index 000000000000..28df3b9af975 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpHeapOnSignalFeature.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017, 2022, 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.core; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import org.graalvm.nativeimage.ProcessProperties; +import org.graalvm.nativeimage.VMRuntime; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.jdk.RuntimeSupport; +import com.oracle.svm.core.log.Log; + +import sun.misc.Signal; +import sun.misc.SignalHandler; + +@AutomaticFeature +public class DumpHeapOnSignalFeature implements Feature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return VMInspectionOptions.hasHeapDumpSupport(); + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + RuntimeSupport.getRuntimeSupport().addStartupHook(new DumpHeapStartupHook()); + } +} + +final class DumpHeapStartupHook implements RuntimeSupport.Hook { + @Override + public void execute(boolean isFirstIsolate) { + if (isFirstIsolate) { + DumpHeapReport.install(); + } + } +} + +class DumpHeapReport implements SignalHandler { + private static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC"); + + static void install() { + Signal.handle(new Signal("USR1"), new DumpHeapReport()); + } + + @Override + public void handle(Signal arg0) { + DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); + dateFormat.setTimeZone(UTC_TIMEZONE); + String heapDumpFileName = "svm-heapdump-" + ProcessProperties.getProcessID() + "-" + dateFormat.format(new Date()) + ".hprof"; + try { + VMRuntime.dumpHeap(heapDumpFileName, true); + } catch (IOException e) { + Log.log().string("IOException during dumpHeap: ").string(e.getMessage()).newline(); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpRuntimeCompilationOnSignalFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpRuntimeCompilationOnSignalFeature.java new file mode 100644 index 000000000000..f8db0aba4874 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpRuntimeCompilationOnSignalFeature.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2017, 2022, 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.core; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platform.WINDOWS; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.deopt.DeoptimizationSupport; +import com.oracle.svm.core.heap.VMOperationInfos; +import com.oracle.svm.core.jdk.RuntimeSupport; +import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.thread.JavaVMOperation; + +import sun.misc.Signal; +import sun.misc.SignalHandler; + +@AutomaticFeature +public class DumpRuntimeCompilationOnSignalFeature implements Feature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return VMInspectionOptions.DumpRuntimeCompilationOnSignal.getValue() && !Platform.includedIn(WINDOWS.class) && DeoptimizationSupport.enabled(); + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + RuntimeSupport.getRuntimeSupport().addStartupHook(new DumpRuntimeCompilationStartupHook()); + } +} + +final class DumpRuntimeCompilationStartupHook implements RuntimeSupport.Hook { + @Override + public void execute(boolean isFirstIsolate) { + if (isFirstIsolate) { + DumpRuntimeCompilation.install(); + } + } +} + +class DumpRuntimeCompilation implements SignalHandler { + static void install() { + Signal.handle(new Signal("USR2"), new DumpRuntimeCompilation()); + } + + @Override + public void handle(Signal arg0) { + DumpRuntimeCompiledMethodsOperation vmOp = new DumpRuntimeCompiledMethodsOperation(); + vmOp.enqueue(); + } + + private static class DumpRuntimeCompiledMethodsOperation extends JavaVMOperation { + DumpRuntimeCompiledMethodsOperation() { + super(VMOperationInfos.get(DumpRuntimeCompiledMethodsOperation.class, "Dump runtime compiled methods", SystemEffect.SAFEPOINT)); + } + + @Override + protected void operate() { + Log log = Log.log(); + SubstrateDiagnostics.dumpRuntimeCompilation(log); + log.flush(); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpThreadStacksOnSignalFeature.java similarity index 60% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpThreadStacksOnSignalFeature.java index 1029262af79c..2d0430fc1575 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpThreadStacksOnSignalFeature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, 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 @@ -24,24 +24,13 @@ */ package com.oracle.svm.core; -import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; -import java.util.function.BooleanSupplier; - -import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platform.WINDOWS; -import org.graalvm.nativeimage.ProcessProperties; -import org.graalvm.nativeimage.VMRuntime; import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.deopt.DeoptimizationSupport; import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.jdk.RuntimeSupport; import com.oracle.svm.core.log.Log; @@ -56,44 +45,24 @@ import sun.misc.SignalHandler; @AutomaticFeature -public class VMInspection implements Feature { +public class DumpThreadStacksOnSignalFeature implements Feature { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { - return isEnabled() || VMInspectionOptions.DumpThreadStacksOnSignal.getValue(); + return VMInspectionOptions.DumpThreadStacksOnSignal.getValue(); } @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - RuntimeSupport.getRuntimeSupport().addStartupHook(new VMInspectionStartupHook()); - } - - @Fold - public static boolean isEnabled() { - return VMInspectionOptions.AllowVMInspection.getValue(); - } - - public static final class IsEnabled implements BooleanSupplier { - @Override - public boolean getAsBoolean() { - return VMInspection.isEnabled(); - } + RuntimeSupport.getRuntimeSupport().addStartupHook(new DumpThreadStacksOnSignalStartupHook()); } } -final class VMInspectionStartupHook implements RuntimeSupport.Hook { +final class DumpThreadStacksOnSignalStartupHook implements RuntimeSupport.Hook { @Override public void execute(boolean isFirstIsolate) { - if (!isFirstIsolate) { - return; - } - DumpAllStacks.install(); - if (VMInspectionOptions.AllowVMInspection.getValue() && !Platform.includedIn(WINDOWS.class)) { - /* We have enough signals to enable the rest. */ - DumpHeapReport.install(); - if (DeoptimizationSupport.enabled()) { - DumpRuntimeCompilation.install(); - } + if (isFirstIsolate) { + DumpAllStacks.install(); } } } @@ -157,48 +126,3 @@ private static void dumpStack(Log log, IsolateThread vmThread) { } } } - -class DumpHeapReport implements SignalHandler { - private static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC"); - - static void install() { - Signal.handle(new Signal("USR1"), new DumpHeapReport()); - } - - @Override - public void handle(Signal arg0) { - DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); - dateFormat.setTimeZone(UTC_TIMEZONE); - String heapDumpFileName = "svm-heapdump-" + ProcessProperties.getProcessID() + "-" + dateFormat.format(new Date()) + ".hprof"; - try { - VMRuntime.dumpHeap(heapDumpFileName, true); - } catch (IOException e) { - Log.log().string("IOException during dumpHeap: ").string(e.getMessage()).newline(); - } - } -} - -class DumpRuntimeCompilation implements SignalHandler { - static void install() { - Signal.handle(new Signal("USR2"), new DumpRuntimeCompilation()); - } - - @Override - public void handle(Signal arg0) { - DumpRuntimeCompiledMethodsOperation vmOp = new DumpRuntimeCompiledMethodsOperation(); - vmOp.enqueue(); - } - - private static class DumpRuntimeCompiledMethodsOperation extends JavaVMOperation { - DumpRuntimeCompiledMethodsOperation() { - super(VMOperationInfos.get(DumpRuntimeCompiledMethodsOperation.class, "Dump runtime compiled methods", SystemEffect.SAFEPOINT)); - } - - @Override - protected void operate() { - Log log = Log.log(); - SubstrateDiagnostics.dumpRuntimeCompilation(log); - log.flush(); - } - } -} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java index b620027f89ca..3da408910d43 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java @@ -33,7 +33,6 @@ import java.util.Collections; import java.util.List; -import com.oracle.svm.core.sampler.ProfilingSampler; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.ImageSingletons; @@ -47,7 +46,6 @@ import org.graalvm.nativeimage.c.type.CCharPointerPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder; -import org.graalvm.nativeimage.impl.HeapDumpSupport; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -65,6 +63,7 @@ import com.oracle.svm.core.jdk.InternalVMMethod; import com.oracle.svm.core.jdk.RuntimeSupport; import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.sampler.ProfilingSampler; import com.oracle.svm.core.thread.JavaThreads; import com.oracle.svm.core.thread.PlatformThreads; import com.oracle.svm.core.thread.VMThreads; @@ -138,13 +137,13 @@ private static int runCore() { private static int runCore0() { try { if (SubstrateOptions.DumpHeapAndExit.getValue()) { - if (VMInspectionOptions.AllowVMInspection.getValue() && ImageSingletons.contains(HeapDumpSupport.class)) { + if (VMInspectionOptions.hasHeapDumpSupport()) { String absoluteHeapDumpPath = new File(SubstrateOptions.Name.getValue() + ".hprof").getAbsolutePath(); VMRuntime.dumpHeap(absoluteHeapDumpPath, true); System.out.println("Heap dump created at '" + absoluteHeapDumpPath + "'."); return 0; } else { - System.err.println("Unable to dump heap. Heap dumping is only supported for native executables built with `-H:+AllowVMInspection`."); + System.err.println("Unable to dump heap. Heap dumping is only supported for native executables built with `" + VMInspectionOptions.getHeapdumpsCommandArgument() + "`."); return 1; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java index 539b78e29cdd..a0cf04b0e7cc 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, 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 @@ -24,15 +24,95 @@ */ package com.oracle.svm.core; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionType; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platform.WINDOWS; +import com.oracle.svm.core.option.APIOption; import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.option.LocatableMultiOptionValue; +import com.oracle.svm.core.option.OptionUtils; +import com.oracle.svm.core.option.SubstrateOptionsParser; +import com.oracle.svm.core.util.InterruptImageBuilding; public final class VMInspectionOptions { - @Option(help = "Enables features that allow the VM to be inspected during runtime.", type = OptionType.User) // - public static final HostedOptionKey AllowVMInspection = new HostedOptionKey<>(false); + private static final String ENABLE_MONITORING_OPTION = "enable-monitoring"; + private static final String MONITORING_ALL_NAME = "all"; + private static final String MONITORING_HEAPDUMP_NAME = "heapdump"; + private static final String MONITORING_JFR_NAME = "jfr"; + private static final String MONITORING_JVMSTAT_NAME = "jvmstat"; + private static final String MONITORING_ALLOWED_VALUES = "'" + MONITORING_HEAPDUMP_NAME + "', '" + MONITORING_JFR_NAME + "', '" + MONITORING_JVMSTAT_NAME + "', or '" + MONITORING_ALL_NAME + "'"; + + @APIOption(name = ENABLE_MONITORING_OPTION) // + @Option(help = "Enable monitoring features that allow the VM to be inspected at run time. Comma-separated list can contain " + MONITORING_ALLOWED_VALUES + ". " + + "For example: `--" + ENABLE_MONITORING_OPTION + "=" + MONITORING_HEAPDUMP_NAME + "," + MONITORING_JVMSTAT_NAME + "`.", type = OptionType.User) // + public static final HostedOptionKey EnableMonitoringFeatures = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings(), + VMInspectionOptions::validateEnableMonitoringFeatures); + + public static void validateEnableMonitoringFeatures(OptionKey optionKey) { + Set enabledFeatures = getEnabledMonitoringFeatures(); + enabledFeatures.removeAll(List.of(MONITORING_HEAPDUMP_NAME, MONITORING_JFR_NAME, MONITORING_JVMSTAT_NAME, MONITORING_ALL_NAME)); + if (!enabledFeatures.isEmpty()) { + throw new InterruptImageBuilding( + "The option " + optionKey.getName() + " contains invalid value(s): " + String.join(", ", enabledFeatures) + ". It can only contain " + MONITORING_ALLOWED_VALUES + "."); + } + } + + @Fold + public static String getHeapdumpsCommandArgument() { + return SubstrateOptionsParser.commandArgument(EnableMonitoringFeatures, MONITORING_HEAPDUMP_NAME); + } + + public static Set getEnabledMonitoringFeatures() { + return new HashSet<>(OptionUtils.flatten(",", EnableMonitoringFeatures.getValue())); + } + + private static boolean hasAllOrKeywordMonitoringSupport(String keyword) { + Set enabledFeatures = getEnabledMonitoringFeatures(); + return enabledFeatures.contains(MONITORING_ALL_NAME) || enabledFeatures.contains(keyword); + } + + @Fold + public static boolean hasHeapDumpSupport() { + return hasAllOrKeywordMonitoringSupport(MONITORING_HEAPDUMP_NAME) && !Platform.includedIn(WINDOWS.class); + } + + @Fold + public static boolean hasJFRSupport() { + return hasAllOrKeywordMonitoringSupport(MONITORING_JFR_NAME) && !Platform.includedIn(WINDOWS.class); + } + + @Fold + public static boolean hasJvmstatSupport() { + return hasAllOrKeywordMonitoringSupport(MONITORING_JVMSTAT_NAME) && !Platform.includedIn(WINDOWS.class); + } + + @Option(help = "Dumps all runtime compiled methods on SIGUSR2.", type = OptionType.User) // + public static final HostedOptionKey DumpRuntimeCompilationOnSignal = new HostedOptionKey<>(false); @Option(help = "Dumps all thread stacktraces on SIGQUIT/SIGBREAK.", type = OptionType.User) // public static final HostedOptionKey DumpThreadStacksOnSignal = new HostedOptionKey<>(false); + + static class DeprecatedOptions { + @Option(help = "Enables features that allow the VM to be inspected during run time.", type = OptionType.User, // + deprecated = true, deprecationMessage = "Please use --" + ENABLE_MONITORING_OPTION) // + static final HostedOptionKey AllowVMInspection = new HostedOptionKey<>(false) { + protected void onValueUpdate(EconomicMap, Object> values, Boolean oldValue, Boolean newValue) { + EnableMonitoringFeatures.update(values, newValue ? "all" : ""); + DumpRuntimeCompilationOnSignal.update(values, true); + super.onValueUpdate(values, oldValue, newValue); + } + }; + } + + private VMInspectionOptions() { + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java index 87e7b09c9e64..601c9e08c8d7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java @@ -117,7 +117,7 @@ public static boolean isInConfiguration(boolean allowPrinting) { 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(); + boolean runtimeEnabled = VMInspectionOptions.hasJFRSupport(); if (HOSTED_ENABLED && !runtimeEnabled) { if (allowPrinting) { 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. " + diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfDataFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfDataFeature.java index 9de73761732f..a55ba27296e6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfDataFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfDataFeature.java @@ -33,7 +33,7 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; -import com.oracle.svm.core.VMInspection; +import com.oracle.svm.core.VMInspectionOptions; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.graal.InternalFeature; import com.oracle.svm.core.jdk.RuntimeSupport; @@ -43,7 +43,7 @@ /** * The performance data feature (hsperfdata) provides monitoring data that can be access by external * tools such as VisualVM and jstat. As it adds a small overhead and starts an extra thread, it is - * only enabled if {@code -H:+AllowVMInspection} is specified at image build time. + * only enabled if {@code --enable-monitoring=jvmstat} is specified at image build time. *

* Various parts of the application (GC, threading, ...) can create {@link PerfDataEntry performance * data entries}. At the moment, all native-image performance data entries live in the image heap. @@ -82,7 +82,7 @@ public class PerfDataFeature implements InternalFeature { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { - return VMInspection.isEnabled(); + return VMInspectionOptions.hasJvmstatSupport(); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SubstrateSigprofHandler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SubstrateSigprofHandler.java index 3dc86705b212..8f12c9473717 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SubstrateSigprofHandler.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SubstrateSigprofHandler.java @@ -75,7 +75,7 @@ class SubstrateSigprofHandlerFeature implements Feature { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { - return VMInspectionOptions.AllowVMInspection.getValue() && SubstrateSigprofHandler.isProfilingSupported() && ImageInfo.isExecutable(); + return VMInspectionOptions.hasJFRSupport() && SubstrateSigprofHandler.isProfilingSupported() && ImageInfo.isExecutable(); } @Override @@ -126,7 +126,7 @@ public void execute(boolean isFirstIsolate) { * instruction pointers, prepare everything necessary for stack walk, do a stack walk and write IPs * into buffer. *

- * + * *

* The signal handler is as a producer. On the other side of relation is * {@link com.oracle.svm.core.jfr.JfrRecorderThread} that is consumer. The @@ -151,7 +151,7 @@ public void execute(boolean isFirstIsolate) { * In some rare cases, the profiling is impossible e.g. no available buffers in the pool, unknown IP * during stack walk, the thread holds the pool's lock when the signal arrives, etc. *

- * + * * @see SamplerSpinLock * @see SamplerBufferStack */ diff --git a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties index a9cf43fcce2f..b3eb210c53cb 100644 --- a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties +++ b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties @@ -8,4 +8,4 @@ Args= \ --features=com.oracle.svm.test.jfr.JFRTestFeature \ --add-opens=java.base/java.lang=ALL-UNNAMED \ --add-exports=org.graalvm.nativeimage.base/com.oracle.svm.util=ALL-UNNAMED \ - -H:+AllowVMInspection \ No newline at end of file + --enable-monitoring=jfr \ No newline at end of file