1 /*
   2  * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 package org.graalvm.compiler.hotspot;
  26 
  27 import static jdk.vm.ci.common.InitTimer.timer;
  28 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
  29 import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC;
  30 import static org.graalvm.compiler.core.common.GraalOptions.HotSpotPrintInlining;
  31 
  32 import java.io.PrintStream;
  33 import java.util.ArrayList;
  34 import java.util.EnumMap;
  35 import java.util.List;
  36 import java.util.Map;
  37 import java.util.concurrent.atomic.AtomicReference;
  38 
  39 import jdk.internal.vm.compiler.collections.EconomicMap;
  40 import jdk.internal.vm.compiler.collections.EconomicSet;
  41 import jdk.internal.vm.compiler.collections.Equivalence;
  42 import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor;
  43 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
  44 import org.graalvm.compiler.api.runtime.GraalRuntime;
  45 import org.graalvm.compiler.core.CompilationWrapper.ExceptionAction;
  46 import org.graalvm.compiler.core.common.CompilationIdentifier;
  47 import org.graalvm.compiler.core.common.GraalOptions;
  48 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
  49 import org.graalvm.compiler.core.target.Backend;
  50 import org.graalvm.compiler.debug.DebugContext;
  51 import org.graalvm.compiler.debug.DebugContext.Description;
  52 import org.graalvm.compiler.debug.DebugHandlersFactory;
  53 import org.graalvm.compiler.debug.DebugOptions;
  54 import org.graalvm.compiler.debug.DiagnosticsOutputDirectory;
  55 import org.graalvm.compiler.debug.GlobalMetrics;
  56 import org.graalvm.compiler.debug.GraalError;
  57 import org.graalvm.compiler.debug.TTY;
  58 import org.graalvm.compiler.hotspot.CompilationStatistics.Options;
  59 import org.graalvm.compiler.hotspot.CompilerConfigurationFactory.BackendMap;
  60 import org.graalvm.compiler.hotspot.debug.BenchmarkCounters;
  61 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
  62 import org.graalvm.compiler.nodes.spi.StampProvider;
  63 import org.graalvm.compiler.options.EnumOptionKey;
  64 import org.graalvm.compiler.options.OptionDescriptor;
  65 import org.graalvm.compiler.options.OptionDescriptors;
  66 import org.graalvm.compiler.options.OptionKey;
  67 import org.graalvm.compiler.options.OptionValues;
  68 import org.graalvm.compiler.options.OptionsParser;
  69 import org.graalvm.compiler.phases.tiers.CompilerConfiguration;
  70 import org.graalvm.compiler.replacements.SnippetCounter;
  71 import org.graalvm.compiler.replacements.SnippetCounter.Group;
  72 import org.graalvm.compiler.runtime.RuntimeProvider;
  73 import org.graalvm.compiler.serviceprovider.GraalServices;
  74 
  75 import jdk.vm.ci.code.Architecture;
  76 import jdk.vm.ci.code.stack.StackIntrospection;
  77 import jdk.vm.ci.common.InitTimer;
  78 import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
  79 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
  80 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
  81 import jdk.vm.ci.hotspot.HotSpotResolvedJavaType;
  82 import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
  83 import jdk.vm.ci.hotspot.HotSpotVMConfigStore;
  84 import jdk.vm.ci.meta.JavaKind;
  85 import jdk.vm.ci.meta.MetaAccessProvider;
  86 import jdk.vm.ci.meta.ResolvedJavaMethod;
  87 import jdk.vm.ci.meta.ResolvedJavaType;
  88 import jdk.vm.ci.runtime.JVMCI;
  89 import jdk.vm.ci.runtime.JVMCIBackend;
  90 import jdk.vm.ci.services.Services;
  91 
  92 //JaCoCo Exclude
  93 
  94 /**
  95  * Singleton class holding the instance of the {@link GraalRuntime}.
  96  */
  97 public final class HotSpotGraalRuntime implements HotSpotGraalRuntimeProvider {
  98 
  99     private static final boolean IS_AOT = Boolean.parseBoolean(Services.getSavedProperties().get("com.oracle.graalvm.isaot"));
 100 
 101     private static boolean checkArrayIndexScaleInvariants(MetaAccessProvider metaAccess) {
 102         assert metaAccess.getArrayIndexScale(JavaKind.Byte) == 1;
 103         assert metaAccess.getArrayIndexScale(JavaKind.Boolean) == 1;
 104         assert metaAccess.getArrayIndexScale(JavaKind.Char) == 2;
 105         assert metaAccess.getArrayIndexScale(JavaKind.Short) == 2;
 106         assert metaAccess.getArrayIndexScale(JavaKind.Int) == 4;
 107         assert metaAccess.getArrayIndexScale(JavaKind.Long) == 8;
 108         assert metaAccess.getArrayIndexScale(JavaKind.Float) == 4;
 109         assert metaAccess.getArrayIndexScale(JavaKind.Double) == 8;
 110         return true;
 111     }
 112 
 113     private final String runtimeName;
 114     private final String compilerConfigurationName;
 115     private final HotSpotBackend hostBackend;
 116 
 117     public GlobalMetrics getMetricValues() {
 118         return metricValues;
 119     }
 120 
 121     private final GlobalMetrics metricValues = new GlobalMetrics();
 122     private final List<SnippetCounter.Group> snippetCounterGroups;
 123     private final HotSpotGC garbageCollector;
 124 
 125     private final EconomicMap<Class<? extends Architecture>, HotSpotBackend> backends = EconomicMap.create(Equivalence.IDENTITY);
 126 
 127     private final GraalHotSpotVMConfig config;
 128 
 129     /**
 130      * The options can be {@linkplain #setOptionValues(String[], String[]) updated} by external
 131      * interfaces such as JMX. This comes with the risk that inconsistencies can arise as an
 132      * {@link OptionValues} object can be cached by various parts of Graal instead of always
 133      * obtaining them from this object. However, concurrent updates are never lost.
 134      */
 135     private AtomicReference<OptionValues> optionsRef = new AtomicReference<>();
 136 
 137     private final DiagnosticsOutputDirectory outputDirectory;
 138     private final Map<ExceptionAction, Integer> compilationProblemsPerAction;
 139 
 140     /**
 141      * @param nameQualifier a qualifier to be added to this runtime's {@linkplain #getName() name}
 142      * @param compilerConfigurationFactory factory for the compiler configuration
 143      *            {@link CompilerConfigurationFactory#selectFactory(String, OptionValues)}
 144      */
 145     @SuppressWarnings("try")
 146     HotSpotGraalRuntime(String nameQualifier, HotSpotJVMCIRuntime jvmciRuntime, CompilerConfigurationFactory compilerConfigurationFactory, OptionValues initialOptions) {
 147         this.runtimeName = getClass().getSimpleName() + ":" + nameQualifier;
 148         HotSpotVMConfigStore store = jvmciRuntime.getConfigStore();
 149         config = GeneratePIC.getValue(initialOptions) ? new AOTGraalHotSpotVMConfig(store) : new GraalHotSpotVMConfig(store);
 150 
 151         // Only set HotSpotPrintInlining if it still has its default value (false).
 152         if (GraalOptions.HotSpotPrintInlining.getValue(initialOptions) == false && config.printInlining) {
 153             optionsRef.set(new OptionValues(initialOptions, HotSpotPrintInlining, true));
 154         } else {
 155             optionsRef.set(initialOptions);
 156         }
 157         OptionValues options = optionsRef.get();
 158 
 159         garbageCollector = getSelectedGC();
 160 
 161         outputDirectory = new DiagnosticsOutputDirectory(options);
 162         compilationProblemsPerAction = new EnumMap<>(ExceptionAction.class);
 163         snippetCounterGroups = GraalOptions.SnippetCounters.getValue(options) ? new ArrayList<>() : null;
 164         CompilerConfiguration compilerConfiguration = compilerConfigurationFactory.createCompilerConfiguration();
 165         compilerConfigurationName = compilerConfigurationFactory.getName();
 166 
 167         if (IS_AOT) {
 168             management = null;
 169         } else {
 170             management = GraalServices.loadSingle(HotSpotGraalManagementRegistration.class, false);
 171             if (management != null) {
 172                 management.initialize(this);
 173             }
 174         }
 175 
 176         BackendMap backendMap = compilerConfigurationFactory.createBackendMap();
 177 
 178         JVMCIBackend hostJvmciBackend = jvmciRuntime.getHostJVMCIBackend();
 179         Architecture hostArchitecture = hostJvmciBackend.getTarget().arch;
 180         try (InitTimer t = timer("create backend:", hostArchitecture)) {
 181             HotSpotBackendFactory factory = backendMap.getBackendFactory(hostArchitecture);
 182             if (factory == null) {
 183                 throw new GraalError("No backend available for host architecture \"%s\"", hostArchitecture);
 184             }
 185             hostBackend = registerBackend(factory.createBackend(this, compilerConfiguration, jvmciRuntime, null));
 186         }
 187 
 188         for (JVMCIBackend jvmciBackend : jvmciRuntime.getJVMCIBackends().values()) {
 189             if (jvmciBackend == hostJvmciBackend) {
 190                 continue;
 191             }
 192 
 193             Architecture gpuArchitecture = jvmciBackend.getTarget().arch;
 194             HotSpotBackendFactory factory = backendMap.getBackendFactory(gpuArchitecture);
 195             if (factory == null) {
 196                 throw new GraalError("No backend available for specified GPU architecture \"%s\"", gpuArchitecture);
 197             }
 198             try (InitTimer t = timer("create backend:", gpuArchitecture)) {
 199                 registerBackend(factory.createBackend(this, compilerConfiguration, null, hostBackend));
 200             }
 201         }
 202 
 203         // Complete initialization of backends
 204         try (InitTimer st = timer(hostBackend.getTarget().arch.getName(), ".completeInitialization")) {
 205             hostBackend.completeInitialization(jvmciRuntime, options);
 206         }
 207         for (HotSpotBackend backend : backends.getValues()) {
 208             if (backend != hostBackend) {
 209                 try (InitTimer st = timer(backend.getTarget().arch.getName(), ".completeInitialization")) {
 210                     backend.completeInitialization(jvmciRuntime, options);
 211                 }
 212             }
 213         }
 214 
 215         BenchmarkCounters.initialize(jvmciRuntime, options);
 216 
 217         assert checkArrayIndexScaleInvariants(hostJvmciBackend.getMetaAccess());
 218 
 219         runtimeStartTime = System.nanoTime();
 220         bootstrapJVMCI = config.getFlag("BootstrapJVMCI", Boolean.class);
 221     }
 222 
 223     /**
 224      * Constants denoting the GC algorithms available in HotSpot.
 225      */
 226     public enum HotSpotGC {
 227         // Supported GCs
 228         Serial(true, "UseSerialGC"),
 229         Parallel(true, "UseParallelGC", "UseParallelOldGC", "UseParNewGC"),
 230         CMS(true, "UseConcMarkSweepGC"),
 231         G1(true, "UseG1GC"),
 232 
 233         // Unsupported GCs
 234         Epsilon(false, "UseEpsilonGC"),
 235         Z(false, "UseZGC");
 236 
 237         HotSpotGC(boolean supported, String... flags) {
 238             this.supported = supported;
 239             this.flags = flags;
 240         }
 241 
 242         final boolean supported;
 243         private final String[] flags;
 244 
 245         public boolean isSelected(GraalHotSpotVMConfig config) {
 246             for (String flag : flags) {
 247                 final boolean notPresent = false;
 248                 if (config.getFlag(flag, Boolean.class, notPresent)) {
 249                     return true;
 250                 }
 251             }
 252             return false;
 253         }
 254 
 255     }
 256 
 257     private HotSpotGC getSelectedGC() throws GraalError {
 258         for (HotSpotGC gc : HotSpotGC.values()) {
 259             if (gc.isSelected(config)) {
 260                 if (!gc.supported) {
 261                     throw new GraalError(gc.name() + " garbage collector is not supported by Graal");
 262                 }
 263                 return gc;
 264             }
 265         }
 266         // As of JDK 9, exactly one GC flag is guaranteed to be selected.
 267         // On JDK 8, the default GC is Serial when no GC flag is true.
 268         return HotSpotGC.Serial;
 269     }
 270 
 271     private HotSpotBackend registerBackend(HotSpotBackend backend) {
 272         Class<? extends Architecture> arch = backend.getTarget().arch.getClass();
 273         HotSpotBackend oldValue = backends.put(arch, backend);
 274         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 275         return backend;
 276     }
 277 
 278     @Override
 279     public HotSpotProviders getHostProviders() {
 280         return getHostBackend().getProviders();
 281     }
 282 
 283     @Override
 284     public GraalHotSpotVMConfig getVMConfig() {
 285         return config;
 286     }
 287 
 288     @Override
 289     public DebugContext openDebugContext(OptionValues compilationOptions, CompilationIdentifier compilationId, Object compilable, Iterable<DebugHandlersFactory> factories, PrintStream logStream) {
 290         if (management != null && management.poll(false) != null) {
 291             if (compilable instanceof HotSpotResolvedJavaMethod) {
 292                 HotSpotResolvedObjectType type = ((HotSpotResolvedJavaMethod) compilable).getDeclaringClass();
 293                 if (type instanceof HotSpotResolvedJavaType) {
 294                     Class<?> clazz = runtime().getMirror(type);
 295                     try {
 296                         ClassLoader cl = clazz.getClassLoader();
 297                         if (cl != null) {
 298                             loaders.add(cl);
 299                         }
 300                     } catch (SecurityException e) {
 301                         // This loader can obviously not be used for resolving class names
 302                     }
 303                 }
 304             }
 305         }
 306         Description description = new Description(compilable, compilationId.toString(CompilationIdentifier.Verbosity.ID));
 307         return DebugContext.create(compilationOptions, description, metricValues, logStream, factories);
 308     }
 309 
 310     @Override
 311     public OptionValues getOptions() {
 312         return optionsRef.get();
 313     }
 314 
 315     @Override
 316     public Group createSnippetCounterGroup(String groupName) {
 317         if (snippetCounterGroups != null) {
 318             Group group = new Group(groupName);
 319             snippetCounterGroups.add(group);
 320             return group;
 321         }
 322         return null;
 323     }
 324 
 325     @Override
 326     public String getName() {
 327         return runtimeName;
 328     }
 329 
 330     @SuppressWarnings("unchecked")
 331     @Override
 332     public <T> T getCapability(Class<T> clazz) {
 333         if (clazz == RuntimeProvider.class) {
 334             return (T) this;
 335         } else if (clazz == OptionValues.class) {
 336             return (T) optionsRef.get();
 337         } else if (clazz == StackIntrospection.class) {
 338             return (T) this;
 339         } else if (clazz == SnippetReflectionProvider.class) {
 340             return (T) getHostProviders().getSnippetReflection();
 341         } else if (clazz == GraalHotSpotVMConfig.class) {
 342             return (T) getVMConfig();
 343         } else if (clazz == StampProvider.class) {
 344             return (T) getHostProviders().getStampProvider();
 345         } else if (ForeignCallsProvider.class.isAssignableFrom(clazz)) {
 346             return (T) getHostProviders().getForeignCalls();
 347         }
 348         return null;
 349     }
 350 
 351     public HotSpotGC getGarbageCollector() {
 352         return garbageCollector;
 353     }
 354 
 355     @Override
 356     public HotSpotBackend getHostBackend() {
 357         return hostBackend;
 358     }
 359 
 360     @Override
 361     public <T extends Architecture> Backend getBackend(Class<T> arch) {
 362         assert arch != Architecture.class;
 363         return backends.get(arch);
 364     }
 365 
 366     @Override
 367     public String getCompilerConfigurationName() {
 368         return compilerConfigurationName;
 369     }
 370 
 371     private long runtimeStartTime;
 372 
 373     /**
 374      * Called from compiler threads to check whether to bail out of a compilation.
 375      */
 376     private volatile boolean shutdown;
 377 
 378     /**
 379      * Take action related to entering a new execution phase.
 380      *
 381      * @param phase the execution phase being entered
 382      */
 383     void phaseTransition(String phase) {
 384         if (Options.UseCompilationStatistics.getValue(optionsRef.get())) {
 385             CompilationStatistics.clear(phase);
 386         }
 387     }
 388 
 389     void shutdown() {
 390         shutdown = true;
 391         metricValues.print(optionsRef.get());
 392 
 393         phaseTransition("final");
 394 
 395         if (snippetCounterGroups != null) {
 396             for (Group group : snippetCounterGroups) {
 397                 TTY.out().out().println(group);
 398             }
 399         }
 400         BenchmarkCounters.shutdown(runtime(), optionsRef.get(), runtimeStartTime);
 401 
 402         outputDirectory.close();
 403 
 404         shutdownLibGraal();
 405     }
 406 
 407     /**
 408      * Substituted by
 409      * {@code com.oracle.svm.graal.hotspot.libgraal.Target_org_graalvm_compiler_hotspot_HotSpotGraalRuntime}
 410      * to call {@code org.graalvm.nativeimage.VMRuntime.shutdown()}.
 411      */
 412     private static void shutdownLibGraal() {
 413     }
 414 
 415     void clearMetrics() {
 416         metricValues.clear();
 417     }
 418 
 419     private final boolean bootstrapJVMCI;
 420     private boolean bootstrapFinished;
 421 
 422     public void notifyBootstrapFinished() {
 423         bootstrapFinished = true;
 424     }
 425 
 426     @Override
 427     public boolean isBootstrapping() {
 428         return bootstrapJVMCI && !bootstrapFinished;
 429     }
 430 
 431     @Override
 432     public boolean isShutdown() {
 433         return shutdown;
 434     }
 435 
 436     @Override
 437     public DiagnosticsOutputDirectory getOutputDirectory() {
 438         return outputDirectory;
 439     }
 440 
 441     @Override
 442     public Map<ExceptionAction, Integer> getCompilationProblemsPerAction() {
 443         return compilationProblemsPerAction;
 444     }
 445 
 446     // ------- Management interface ---------
 447 
 448     private final HotSpotGraalManagementRegistration management;
 449 
 450     /**
 451      * @returns the management object for this runtime or {@code null}
 452      */
 453     public HotSpotGraalManagementRegistration getManagement() {
 454         return management;
 455     }
 456 
 457     /**
 458      * Set of weak references to {@link ClassLoader}s available for resolving class names present in
 459      * management {@linkplain #invokeManagementAction(String, Object[]) action} arguments.
 460      */
 461     private final WeakClassLoaderSet loaders = new WeakClassLoaderSet(ClassLoader.getSystemClassLoader());
 462 
 463     /**
 464      * Sets or updates this object's {@linkplain #getOptions() options} from {@code names} and
 465      * {@code values}.
 466      *
 467      * @param values the values to set. The empty string represents {@code null} which resets an
 468      *            option to its default value. For string type options, a non-empty value must be
 469      *            enclosed in double quotes.
 470      * @return an array of Strings where the element at index i is {@code names[i]} if setting the
 471      *         denoted option succeeded, {@code null} if the option is unknown otherwise an error
 472      *         message describing the failure to set the option
 473      */
 474     public String[] setOptionValues(String[] names, String[] values) {
 475         EconomicMap<String, OptionDescriptor> optionDescriptors = getOptionDescriptors();
 476         EconomicMap<OptionKey<?>, Object> newValues = EconomicMap.create(names.length);
 477         EconomicSet<OptionKey<?>> resetValues = EconomicSet.create(names.length);
 478         String[] result = new String[names.length];
 479         for (int i = 0; i < names.length; i++) {
 480             String name = names[i];
 481             OptionDescriptor option = optionDescriptors.get(name);
 482             if (option != null) {
 483                 String svalue = values[i];
 484                 Class<?> optionValueType = option.getOptionValueType();
 485                 OptionKey<?> optionKey = option.getOptionKey();
 486                 if (svalue == null || svalue.isEmpty() && !(optionKey instanceof EnumOptionKey)) {
 487                     resetValues.add(optionKey);
 488                     result[i] = name;
 489                 } else {
 490                     String valueToParse;
 491                     if (optionValueType == String.class) {
 492                         if (svalue.length() < 2 || svalue.charAt(0) != '"' || svalue.charAt(svalue.length() - 1) != '"') {
 493                             result[i] = "Invalid value for String option '" + name + "': must be the empty string or be enclosed in double quotes: " + svalue;
 494                             continue;
 495                         } else {
 496                             valueToParse = svalue.substring(1, svalue.length() - 1);
 497                         }
 498                     } else {
 499                         valueToParse = svalue;
 500                     }
 501                     try {
 502                         OptionsParser.parseOption(name, valueToParse, newValues, OptionsParser.getOptionsLoader());
 503                         result[i] = name;
 504                     } catch (IllegalArgumentException e) {
 505                         result[i] = e.getMessage();
 506                         continue;
 507                     }
 508                 }
 509             } else {
 510                 result[i] = null;
 511             }
 512         }
 513 
 514         OptionValues currentOptions;
 515         OptionValues newOptions;
 516         do {
 517             currentOptions = optionsRef.get();
 518             UnmodifiableMapCursor<OptionKey<?>, Object> cursor = currentOptions.getMap().getEntries();
 519             while (cursor.advance()) {
 520                 OptionKey<?> key = cursor.getKey();
 521                 if (!resetValues.contains(key) && !newValues.containsKey(key)) {
 522                     newValues.put(key, OptionValues.decodeNull(cursor.getValue()));
 523                 }
 524             }
 525             newOptions = new OptionValues(newValues);
 526         } while (!optionsRef.compareAndSet(currentOptions, newOptions));
 527 
 528         return result;
 529     }
 530 
 531     /**
 532      * Gets the values for the options corresponding to {@code names} encoded as strings. The empty
 533      * string represents {@code null}. For string type options, non-{@code null} values will be
 534      * enclosed in double quotes.
 535      *
 536      * @param names a list of option names
 537      * @return the values for each named option. If an element in {@code names} does not denote an
 538      *         existing option, the corresponding element in the returned array will be {@code null}
 539      */
 540     public String[] getOptionValues(String... names) {
 541         String[] values = new String[names.length];
 542         EconomicMap<String, OptionDescriptor> optionDescriptors = getOptionDescriptors();
 543         for (int i = 0; i < names.length; i++) {
 544             OptionDescriptor option = optionDescriptors.get(names[i]);
 545             if (option != null) {
 546                 OptionKey<?> optionKey = option.getOptionKey();
 547                 Object value = optionKey.getValue(getOptions());
 548                 String svalue;
 549                 if (option.getOptionValueType() == String.class && value != null) {
 550                     svalue = "\"" + value + "\"";
 551                 } else if (value == null) {
 552                     svalue = "";
 553                 } else {
 554                     svalue = String.valueOf(value);
 555                 }
 556                 values[i] = svalue;
 557             } else {
 558                 // null denotes the option does not exist
 559                 values[i] = null;
 560             }
 561         }
 562         return values;
 563     }
 564 
 565     private static EconomicMap<String, OptionDescriptor> getOptionDescriptors() {
 566         EconomicMap<String, OptionDescriptor> result = EconomicMap.create();
 567         for (OptionDescriptors set : OptionsParser.getOptionsLoader()) {
 568             for (OptionDescriptor option : set) {
 569                 result.put(option.getName(), option);
 570             }
 571         }
 572         return result;
 573     }
 574 
 575     private void dumpMethod(String className, String methodName, String filter, String host, int port) throws Exception {
 576         EconomicSet<ClassNotFoundException> failures = EconomicSet.create();
 577         EconomicSet<Class<?>> found = loaders.resolve(className, failures);
 578         if (found.isEmpty()) {
 579             ClassNotFoundException cause = failures.isEmpty() ? new ClassNotFoundException(className) : failures.iterator().next();
 580             throw new Exception("Cannot find class " + className + " to schedule recompilation", cause);
 581         }
 582         for (Class<?> clazz : found) {
 583             ResolvedJavaType type = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaType(clazz);
 584             for (ResolvedJavaMethod method : type.getDeclaredMethods()) {
 585                 if (methodName.equals(method.getName()) && method instanceof HotSpotResolvedJavaMethod) {
 586                     HotSpotResolvedJavaMethod hotSpotMethod = (HotSpotResolvedJavaMethod) method;
 587                     dumpMethod(hotSpotMethod, filter, host, port);
 588                 }
 589             }
 590         }
 591     }
 592 
 593     private void dumpMethod(HotSpotResolvedJavaMethod hotSpotMethod, String filter, String host, int port) throws Exception {
 594         EconomicMap<OptionKey<?>, Object> extra = EconomicMap.create();
 595         extra.put(DebugOptions.Dump, filter);
 596         extra.put(DebugOptions.PrintGraphHost, host);
 597         extra.put(DebugOptions.PrintGraphPort, port);
 598         OptionValues compileOptions = new OptionValues(getOptions(), extra);
 599         HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) runtime().getCompiler();
 600         compiler.compileMethod(new HotSpotCompilationRequest(hotSpotMethod, -1, 0L), false, compileOptions);
 601     }
 602 
 603     public Object invokeManagementAction(String actionName, Object[] params) throws Exception {
 604         if ("dumpMethod".equals(actionName)) {
 605             if (params.length != 0 && params[0] instanceof HotSpotResolvedJavaMethod) {
 606                 HotSpotResolvedJavaMethod method = param(params, 0, "method", HotSpotResolvedJavaMethod.class, null);
 607                 String filter = param(params, 1, "filter", String.class, ":3");
 608                 String host = param(params, 2, "host", String.class, "localhost");
 609                 Number port = param(params, 3, "port", Number.class, 4445);
 610                 dumpMethod(method, filter, host, port.intValue());
 611             } else {
 612                 String className = param(params, 0, "className", String.class, null);
 613                 String methodName = param(params, 1, "methodName", String.class, null);
 614                 String filter = param(params, 2, "filter", String.class, ":3");
 615                 String host = param(params, 3, "host", String.class, "localhost");
 616                 Number port = param(params, 4, "port", Number.class, 4445);
 617                 dumpMethod(className, methodName, filter, host, port.intValue());
 618             }
 619         }
 620         return null;
 621     }
 622 
 623     private static <T> T param(Object[] arr, int index, String name, Class<T> type, T defaultValue) {
 624         Object value = arr.length > index ? arr[index] : null;
 625         if (value == null || (value instanceof String && ((String) value).isEmpty())) {
 626             if (defaultValue == null) {
 627                 throw new IllegalArgumentException(name + " must be specified");
 628             }
 629             value = defaultValue;
 630         }
 631         if (type.isInstance(value)) {
 632             return type.cast(value);
 633         }
 634         throw new IllegalArgumentException("Expecting " + type.getName() + " for " + name + " but was " + value);
 635     }
 636 }