src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java
changeset 49873 26ebfe8ce852
parent 48861 47f19ff9903c
child 50330 2cbc42a5764b
--- 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);
     }
 }