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