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