--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java Tue Apr 24 08:13:30 2018 -0700
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java Tue Apr 24 09:04:57 2018 -0700
@@ -33,9 +33,12 @@
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
-import org.graalvm.collections.EconomicMap;
-import org.graalvm.collections.Equivalence;
+import jdk.internal.vm.compiler.collections.EconomicMap;
+import jdk.internal.vm.compiler.collections.EconomicSet;
+import jdk.internal.vm.compiler.collections.Equivalence;
+import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor;
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.api.runtime.GraalRuntime;
import org.graalvm.compiler.core.CompilationWrapper.ExceptionAction;
@@ -45,6 +48,7 @@
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.DebugContext.Description;
import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugOptions;
import org.graalvm.compiler.debug.DiagnosticsOutputDirectory;
import org.graalvm.compiler.debug.GlobalMetrics;
import org.graalvm.compiler.debug.GraalError;
@@ -54,19 +58,31 @@
import org.graalvm.compiler.hotspot.debug.BenchmarkCounters;
import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
import org.graalvm.compiler.nodes.spi.StampProvider;
+import org.graalvm.compiler.options.EnumOptionKey;
+import org.graalvm.compiler.options.OptionDescriptor;
+import org.graalvm.compiler.options.OptionDescriptors;
+import org.graalvm.compiler.options.OptionKey;
import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.options.OptionsParser;
import org.graalvm.compiler.phases.tiers.CompilerConfiguration;
import org.graalvm.compiler.replacements.SnippetCounter;
import org.graalvm.compiler.replacements.SnippetCounter.Group;
import org.graalvm.compiler.runtime.RuntimeProvider;
+import org.graalvm.compiler.serviceprovider.GraalServices;
import jdk.vm.ci.code.Architecture;
import jdk.vm.ci.code.stack.StackIntrospection;
import jdk.vm.ci.common.InitTimer;
+import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
+import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
+import jdk.vm.ci.hotspot.HotSpotResolvedJavaType;
+import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
import jdk.vm.ci.hotspot.HotSpotVMConfigStore;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.runtime.JVMCI;
import jdk.vm.ci.runtime.JVMCIBackend;
//JaCoCo Exclude
@@ -88,6 +104,7 @@
return true;
}
+ private final String runtimeName;
private final HotSpotBackend hostBackend;
private final GlobalMetrics metricValues = new GlobalMetrics();
private final List<SnippetCounter.Group> snippetCounterGroups;
@@ -96,25 +113,42 @@
private final GraalHotSpotVMConfig config;
- private final OptionValues options;
+ /**
+ * The options can be {@linkplain #setOptionValues(String[], String[]) updated} by external
+ * interfaces such as JMX. This comes with the risk that inconsistencies can arise as an
+ * {@link OptionValues} object can be cached by various parts of Graal instead of always
+ * obtaining them from this object. However, concurrent updates are never lost.
+ */
+ private AtomicReference<OptionValues> optionsRef = new AtomicReference<>();
+
+ private final HotSpotGraalCompiler compiler;
+
private final DiagnosticsOutputDirectory outputDirectory;
private final Map<ExceptionAction, Integer> compilationProblemsPerAction;
- private final HotSpotGraalMBean mBean;
/**
+ * @param nameQualifier a qualifier to be added to this runtime's {@linkplain #getName() name}
* @param compilerConfigurationFactory factory for the compiler configuration
* {@link CompilerConfigurationFactory#selectFactory(String, OptionValues)}
*/
@SuppressWarnings("try")
- HotSpotGraalRuntime(HotSpotJVMCIRuntime jvmciRuntime, CompilerConfigurationFactory compilerConfigurationFactory, OptionValues initialOptions) {
+ HotSpotGraalRuntime(String nameQualifier, HotSpotJVMCIRuntime jvmciRuntime, CompilerConfigurationFactory compilerConfigurationFactory, OptionValues initialOptions) {
+ this.runtimeName = getClass().getSimpleName() + ":" + nameQualifier;
HotSpotVMConfigStore store = jvmciRuntime.getConfigStore();
config = GeneratePIC.getValue(initialOptions) ? new AOTGraalHotSpotVMConfig(store) : new GraalHotSpotVMConfig(store);
// Only set HotSpotPrintInlining if it still has its default value (false).
if (GraalOptions.HotSpotPrintInlining.getValue(initialOptions) == false && config.printInlining) {
- options = new OptionValues(initialOptions, HotSpotPrintInlining, true);
+ optionsRef.set(new OptionValues(initialOptions, HotSpotPrintInlining, true));
} else {
- options = initialOptions;
+ optionsRef.set(initialOptions);
+ }
+ OptionValues options = optionsRef.get();
+
+ if (config.useCMSGC) {
+ // Graal doesn't work with the CMS collector (e.g. GR-6777)
+ // and is deprecated (http://openjdk.java.net/jeps/291).
+ throw new GraalError("Graal does not support the CMS collector");
}
outputDirectory = new DiagnosticsOutputDirectory(options);
@@ -122,8 +156,11 @@
snippetCounterGroups = GraalOptions.SnippetCounters.getValue(options) ? new ArrayList<>() : null;
CompilerConfiguration compilerConfiguration = compilerConfigurationFactory.createCompilerConfiguration();
- HotSpotGraalCompiler compiler = new HotSpotGraalCompiler(jvmciRuntime, this, initialOptions);
- this.mBean = createHotSpotGraalMBean(compiler);
+ compiler = new HotSpotGraalCompiler(jvmciRuntime, this, options);
+ management = GraalServices.loadSingle(HotSpotGraalManagementRegistration.class, false);
+ if (management != null) {
+ management.initialize(this);
+ }
BackendMap backendMap = compilerConfigurationFactory.createBackendMap();
@@ -172,14 +209,6 @@
bootstrapJVMCI = config.getFlag("BootstrapJVMCI", Boolean.class);
}
- private static HotSpotGraalMBean createHotSpotGraalMBean(HotSpotGraalCompiler compiler) {
- try {
- return HotSpotGraalMBean.create(compiler);
- } catch (LinkageError ex) {
- return null;
- }
- }
-
private HotSpotBackend registerBackend(HotSpotBackend backend) {
Class<? extends Architecture> arch = backend.getTarget().arch.getClass();
HotSpotBackend oldValue = backends.put(arch, backend);
@@ -199,24 +228,35 @@
@Override
public DebugContext openDebugContext(OptionValues compilationOptions, CompilationIdentifier compilationId, Object compilable, Iterable<DebugHandlersFactory> factories) {
+ if (management != null && management.poll(false) != null) {
+ if (compilable instanceof HotSpotResolvedJavaMethod) {
+ HotSpotResolvedObjectType type = ((HotSpotResolvedJavaMethod) compilable).getDeclaringClass();
+ if (type instanceof HotSpotResolvedJavaType) {
+ Class<?> clazz = ((HotSpotResolvedJavaType) type).mirror();
+ try {
+ ClassLoader cl = clazz.getClassLoader();
+ if (cl != null) {
+ loaders.add(cl);
+ }
+ } catch (SecurityException e) {
+ // This loader can obviously not be used for resolving class names
+ }
+ }
+ }
+ }
Description description = new Description(compilable, compilationId.toString(CompilationIdentifier.Verbosity.ID));
return DebugContext.create(compilationOptions, description, metricValues, DEFAULT_LOG_STREAM, factories);
}
@Override
public OptionValues getOptions() {
- return mBean == null ? options : mBean.optionsFor(options, null);
+ return optionsRef.get();
}
@Override
- public OptionValues getOptions(ResolvedJavaMethod forMethod) {
- return mBean == null ? options : mBean.optionsFor(options, forMethod);
- }
-
- @Override
- public Group createSnippetCounterGroup(String name) {
+ public Group createSnippetCounterGroup(String groupName) {
if (snippetCounterGroups != null) {
- Group group = new Group(name);
+ Group group = new Group(groupName);
snippetCounterGroups.add(group);
return group;
}
@@ -225,7 +265,7 @@
@Override
public String getName() {
- return getClass().getSimpleName();
+ return runtimeName;
}
@SuppressWarnings("unchecked")
@@ -234,7 +274,7 @@
if (clazz == RuntimeProvider.class) {
return (T) this;
} else if (clazz == OptionValues.class) {
- return (T) options;
+ return (T) optionsRef.get();
} else if (clazz == StackIntrospection.class) {
return (T) this;
} else if (clazz == SnippetReflectionProvider.class) {
@@ -265,14 +305,14 @@
* @param phase the execution phase being entered
*/
void phaseTransition(String phase) {
- if (Options.UseCompilationStatistics.getValue(options)) {
+ if (Options.UseCompilationStatistics.getValue(optionsRef.get())) {
CompilationStatistics.clear(phase);
}
}
void shutdown() {
shutdown = true;
- metricValues.print(options);
+ metricValues.print(optionsRef.get());
phaseTransition("final");
@@ -281,7 +321,7 @@
TTY.out().out().println(group);
}
}
- BenchmarkCounters.shutdown(runtime(), options, runtimeStartTime);
+ BenchmarkCounters.shutdown(runtime(), optionsRef.get(), runtimeStartTime);
outputDirectory.close();
}
@@ -317,7 +357,193 @@
return compilationProblemsPerAction;
}
- Object getMBean() {
- return mBean;
+ // ------- Management interface ---------
+
+ private final HotSpotGraalManagementRegistration management;
+
+ /**
+ * @returns the management object for this runtime or {@code null}
+ */
+ public HotSpotGraalManagementRegistration getManagement() {
+ return management;
+ }
+
+ /**
+ * Set of weak references to {@link ClassLoader}s available for resolving class names present in
+ * management {@linkplain #invokeManagementAction(String, Object[]) action} arguments.
+ */
+ private final WeakClassLoaderSet loaders = new WeakClassLoaderSet(ClassLoader.getSystemClassLoader());
+
+ /**
+ * Sets or updates this object's {@linkplain #getOptions() options} from {@code names} and
+ * {@code values}.
+ *
+ * @param values the values to set. The empty string represents {@code null} which resets an
+ * option to its default value. For string type options, a non-empty value must be
+ * enclosed in double quotes.
+ * @return an array of Strings where the element at index i is {@code names[i]} if setting the
+ * denoted option succeeded, {@code null} if the option is unknown otherwise an error
+ * message describing the failure to set the option
+ */
+ public String[] setOptionValues(String[] names, String[] values) {
+ EconomicMap<String, OptionDescriptor> optionDescriptors = getOptionDescriptors();
+ EconomicMap<OptionKey<?>, Object> newValues = EconomicMap.create(names.length);
+ EconomicSet<OptionKey<?>> resetValues = EconomicSet.create(names.length);
+ String[] result = new String[names.length];
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ OptionDescriptor option = optionDescriptors.get(name);
+ if (option != null) {
+ String svalue = values[i];
+ Class<?> optionValueType = option.getOptionValueType();
+ OptionKey<?> optionKey = option.getOptionKey();
+ if (svalue == null || svalue.isEmpty() && !(optionKey instanceof EnumOptionKey)) {
+ resetValues.add(optionKey);
+ result[i] = name;
+ } else {
+ String valueToParse;
+ if (optionValueType == String.class) {
+ if (svalue.length() < 2 || svalue.charAt(0) != '"' || svalue.charAt(svalue.length() - 1) != '"') {
+ result[i] = "Invalid value for String option '" + name + "': must be the empty string or be enclosed in double quotes: " + svalue;
+ continue;
+ } else {
+ valueToParse = svalue.substring(1, svalue.length() - 1);
+ }
+ } else {
+ valueToParse = svalue;
+ }
+ try {
+ OptionsParser.parseOption(name, valueToParse, newValues, OptionsParser.getOptionsLoader());
+ result[i] = name;
+ } catch (IllegalArgumentException e) {
+ result[i] = e.getMessage();
+ continue;
+ }
+ }
+ } else {
+ result[i] = null;
+ }
+ }
+
+ OptionValues currentOptions;
+ OptionValues newOptions;
+ do {
+ currentOptions = optionsRef.get();
+ UnmodifiableMapCursor<OptionKey<?>, Object> cursor = currentOptions.getMap().getEntries();
+ while (cursor.advance()) {
+ OptionKey<?> key = cursor.getKey();
+ if (!resetValues.contains(key) && !newValues.containsKey(key)) {
+ newValues.put(key, OptionValues.decodeNull(cursor.getValue()));
+ }
+ }
+ newOptions = new OptionValues(newValues);
+ } while (!optionsRef.compareAndSet(currentOptions, newOptions));
+
+ return result;
+ }
+
+ /**
+ * Gets the values for the options corresponding to {@code names} encoded as strings. The empty
+ * string represents {@code null}. For string type options, non-{@code null} values will be
+ * enclosed in double quotes.
+ *
+ * @param names a list of option names
+ * @return the values for each named option. If an element in {@code names} does not denote an
+ * existing option, the corresponding element in the returned array will be {@code null}
+ */
+ public String[] getOptionValues(String... names) {
+ String[] values = new String[names.length];
+ EconomicMap<String, OptionDescriptor> optionDescriptors = getOptionDescriptors();
+ for (int i = 0; i < names.length; i++) {
+ OptionDescriptor option = optionDescriptors.get(names[i]);
+ if (option != null) {
+ OptionKey<?> optionKey = option.getOptionKey();
+ Object value = optionKey.getValue(getOptions());
+ String svalue;
+ if (option.getOptionValueType() == String.class && value != null) {
+ svalue = "\"" + value + "\"";
+ } else if (value == null) {
+ svalue = "";
+ } else {
+ svalue = String.valueOf(value);
+ }
+ values[i] = svalue;
+ } else {
+ // null denotes the option does not exist
+ values[i] = null;
+ }
+ }
+ return values;
+ }
+
+ private static EconomicMap<String, OptionDescriptor> getOptionDescriptors() {
+ EconomicMap<String, OptionDescriptor> result = EconomicMap.create();
+ for (OptionDescriptors set : OptionsParser.getOptionsLoader()) {
+ for (OptionDescriptor option : set) {
+ result.put(option.getName(), option);
+ }
+ }
+ return result;
+ }
+
+ private void dumpMethod(String className, String methodName, String filter, String host, int port) throws Exception {
+ EconomicSet<ClassNotFoundException> failures = EconomicSet.create();
+ EconomicSet<Class<?>> found = loaders.resolve(className, failures);
+ if (found.isEmpty()) {
+ ClassNotFoundException cause = failures.isEmpty() ? new ClassNotFoundException(className) : failures.iterator().next();
+ throw new Exception("Cannot find class " + className + " to schedule recompilation", cause);
+ }
+ for (Class<?> clazz : found) {
+ ResolvedJavaType type = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaType(clazz);
+ for (ResolvedJavaMethod method : type.getDeclaredMethods()) {
+ if (methodName.equals(method.getName()) && method instanceof HotSpotResolvedJavaMethod) {
+ HotSpotResolvedJavaMethod hotSpotMethod = (HotSpotResolvedJavaMethod) method;
+ dumpMethod(hotSpotMethod, filter, host, port);
+ }
+ }
+ }
+ }
+
+ private void dumpMethod(HotSpotResolvedJavaMethod hotSpotMethod, String filter, String host, int port) throws Exception {
+ EconomicMap<OptionKey<?>, Object> extra = EconomicMap.create();
+ extra.put(DebugOptions.Dump, filter);
+ extra.put(DebugOptions.PrintGraphHost, host);
+ extra.put(DebugOptions.PrintBinaryGraphPort, port);
+ OptionValues compileOptions = new OptionValues(getOptions(), extra);
+ compiler.compileMethod(new HotSpotCompilationRequest(hotSpotMethod, -1, 0L), false, compileOptions);
+ }
+
+ public Object invokeManagementAction(String actionName, Object[] params) throws Exception {
+ if ("dumpMethod".equals(actionName)) {
+ if (params.length != 0 && params[0] instanceof HotSpotResolvedJavaMethod) {
+ HotSpotResolvedJavaMethod method = param(params, 0, "method", HotSpotResolvedJavaMethod.class, null);
+ String filter = param(params, 1, "filter", String.class, ":3");
+ String host = param(params, 2, "host", String.class, "localhost");
+ Number port = param(params, 3, "port", Number.class, 4445);
+ dumpMethod(method, filter, host, port.intValue());
+ } else {
+ String className = param(params, 0, "className", String.class, null);
+ String methodName = param(params, 1, "methodName", String.class, null);
+ String filter = param(params, 2, "filter", String.class, ":3");
+ String host = param(params, 3, "host", String.class, "localhost");
+ Number port = param(params, 4, "port", Number.class, 4445);
+ dumpMethod(className, methodName, filter, host, port.intValue());
+ }
+ }
+ return null;
+ }
+
+ private static <T> T param(Object[] arr, int index, String name, Class<T> type, T defaultValue) {
+ Object value = arr.length > index ? arr[index] : null;
+ if (value == null || (value instanceof String && ((String) value).isEmpty())) {
+ if (defaultValue == null) {
+ throw new IllegalArgumentException(name + " must be specified");
+ }
+ value = defaultValue;
+ }
+ if (type.isInstance(value)) {
+ return type.cast(value);
+ }
+ throw new IllegalArgumentException("Expecting " + type.getName() + " for " + name + " but was " + value);
}
}