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