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