1 /* 2 * Copyright (c) 2005, 2013, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package org.openjdk.jmh.runner.options; 26 27 import joptsimple.*; 28 import org.openjdk.jmh.annotations.Mode; 29 import org.openjdk.jmh.profile.ProfilerFactory; 30 import org.openjdk.jmh.results.format.ResultFormatType; 31 import org.openjdk.jmh.util.HashMultimap; 32 import org.openjdk.jmh.util.Multimap; 33 import org.openjdk.jmh.util.Optional; 34 import org.openjdk.jmh.util.Utils; 35 36 import java.io.IOException; 37 import java.util.*; 38 import java.util.concurrent.TimeUnit; 39 40 /** 41 * Class that handles all the command line options. 42 */ 43 public class CommandLineOptions implements Options { 44 private static final long serialVersionUID = 5565183446360224399L; 45 46 private final Optional<Integer> iterations; 47 private final Optional<TimeValue> timeout; 48 private final Optional<TimeValue> runTime; 49 private final Optional<Integer> batchSize; 50 private final Optional<Integer> warmupIterations; 51 private final Optional<TimeValue> warmupTime; 52 private final Optional<Integer> warmupBatchSize; 53 private final List<Mode> benchMode = new ArrayList<Mode>(); 54 private final Optional<Integer> threads; 55 private final List<Integer> threadGroups = new ArrayList<Integer>(); 56 private final Optional<Boolean> synchIterations; 57 private final Optional<Boolean> gcEachIteration; 58 private final Optional<VerboseMode> verbose; 59 private final Optional<Boolean> failOnError; 60 private final List<ProfilerConfig> profilers = new ArrayList<ProfilerConfig>(); 61 private final Optional<TimeUnit> timeUnit; 62 private final Optional<Integer> opsPerInvocation; 63 private final List<String> regexps = new ArrayList<String>(); 64 private final Optional<Integer> fork; 65 private final Optional<Integer> warmupFork; 66 private final Optional<String> output; 67 private final Optional<String> result; 68 private final Optional<ResultFormatType> resultFormat; 69 private final Optional<String> jvm; 70 private final Optional<Collection<String>> jvmArgs; 71 private final Optional<Collection<String>> jvmArgsAppend; 72 private final Optional<Collection<String>> jvmArgsPrepend; 73 private final List<String> excludes = new ArrayList<String>(); 74 private final Optional<WarmupMode> warmupMode; 75 private final List<String> warmupMicros = new ArrayList<String>(); 76 private final Multimap<String, String> params = new HashMultimap<String, String>(); 77 private final boolean list; 78 private final boolean listWithParams; 79 private final boolean listResultFormats; 80 private final boolean help; 81 private final boolean listProfilers; 82 83 private final transient OptionParser parser; 84 85 /** 86 * Parses the given command line. 87 * @param argv argument list 88 * @throws CommandLineOptionException if some options are misspelled 89 */ 90 public CommandLineOptions(String... argv) throws CommandLineOptionException { 91 parser = new OptionParser(); 92 parser.formatHelpWith(new OptionFormatter()); 93 94 OptionSpec<Integer> optMeasureCount = parser.accepts("i", "Number of measurement iterations to do.") 95 .withRequiredArg().withValuesConvertedBy(IntegerValueConverter.POSITIVE).describedAs("int"); 96 97 OptionSpec<Integer> optMeasureBatchSize = parser.accepts("bs", "Batch size: number of benchmark method calls per operation. " + 98 "(some benchmark modes can ignore this setting)") 99 .withRequiredArg().withValuesConvertedBy(IntegerValueConverter.POSITIVE).describedAs("int"); 100 101 OptionSpec<TimeValue> optMeasureTime = parser.accepts("r", "Time to spend at each measurement iteration.") 102 .withRequiredArg().ofType(TimeValue.class).describedAs("time"); 103 104 OptionSpec<Integer> optWarmupCount = parser.accepts("wi", "Number of warmup iterations to do.") 105 .withRequiredArg().withValuesConvertedBy(IntegerValueConverter.NON_NEGATIVE).describedAs("int"); 106 107 OptionSpec<Integer> optWarmupBatchSize = parser.accepts("wbs", "Warmup batch size: number of benchmark method calls per operation. " + 108 "(some benchmark modes can ignore this setting)") 109 .withRequiredArg().withValuesConvertedBy(IntegerValueConverter.POSITIVE).describedAs("int"); 110 111 OptionSpec<TimeValue> optWarmupTime = parser.accepts("w", "Time to spend at each warmup iteration.") 112 .withRequiredArg().ofType(TimeValue.class).describedAs("time"); 113 114 OptionSpec<TimeValue> optTimeoutTime = parser.accepts("to", "Timeout for benchmark iteration.") 115 .withRequiredArg().ofType(TimeValue.class).describedAs("time"); 116 117 OptionSpec<Integer> optThreads = parser.accepts("t", "Number of worker threads to run with. 'max' means Runtime.getRuntime().availableProcessors().") 118 .withRequiredArg().withValuesConvertedBy(ThreadsValueConverter.INSTANCE).describedAs("int"); 119 120 OptionSpec<String> optBenchmarkMode = parser.accepts("bm", "Benchmark mode. Available modes are: " + Mode.getKnown()) 121 .withRequiredArg().ofType(String.class).withValuesSeparatedBy(',').describedAs("mode"); 122 123 OptionSpec<Boolean> optSyncIters = parser.accepts("si", "Synchronize iterations?") 124 .withRequiredArg().ofType(Boolean.class).describedAs("bool"); 125 126 OptionSpec<Boolean> optGC = parser.accepts("gc", "Should JMH force GC between iterations?") 127 .withRequiredArg().ofType(Boolean.class).describedAs("bool"); 128 129 OptionSpec<Boolean> optFOE = parser.accepts("foe", "Should JMH fail immediately if any benchmark had" + 130 " experienced the unrecoverable error?") 131 .withRequiredArg().ofType(Boolean.class).describedAs("bool"); 132 133 OptionSpec<String> optVerboseMode = parser.accepts("v", "Verbosity mode. Available modes are: " + Arrays.toString(VerboseMode.values())) 134 .withRequiredArg().ofType(String.class).describedAs("mode"); 135 136 OptionSpec<String> optArgs = parser.nonOptions("Benchmarks to run (regexp+).") 137 .describedAs("regexp+"); 138 139 OptionSpec<Integer> optForks = parser.accepts("f", "How many times to forks a single benchmark." + 140 " Use 0 to disable forking altogether (WARNING: disabling forking may have detrimental" + 141 " impact on benchmark and infrastructure reliability, you might want to use different" + 142 " warmup mode instead).") 143 .withRequiredArg().withValuesConvertedBy(IntegerValueConverter.NON_NEGATIVE).describedAs("int"); 144 145 OptionSpec<Integer> optWarmupForks = parser.accepts("wf", "How many warmup forks to make " + 146 "for a single benchmark. 0 to disable warmup forks.") 147 .withRequiredArg().withValuesConvertedBy(IntegerValueConverter.NON_NEGATIVE).describedAs("int"); 148 149 OptionSpec<String> optOutput = parser.accepts("o", "Redirect human-readable output to file.") 150 .withRequiredArg().ofType(String.class).describedAs("filename"); 151 152 OptionSpec<String> optOutputResults = parser.accepts("rff", "Write results to given file.") 153 .withRequiredArg().ofType(String.class).describedAs("filename"); 154 155 OptionSpec<String> optProfilers = parser.accepts("prof", "Use profilers to collect additional data." + 156 " See the list of available profilers first.") 157 .withRequiredArg().ofType(String.class).describedAs("profiler"); 158 159 OptionSpec<Integer> optThreadGroups = parser.accepts("tg", "Override thread group distribution for asymmetric benchmarks.") 160 .withRequiredArg().withValuesSeparatedBy(',').ofType(Integer.class) 161 .withValuesConvertedBy(IntegerValueConverter.NON_NEGATIVE).describedAs("int+"); 162 163 OptionSpec<String> optJvm = parser.accepts("jvm", "Custom JVM to use when forking (path to JVM executable).") 164 .withRequiredArg().ofType(String.class).describedAs("string"); 165 166 OptionSpec<String> optJvmArgs = parser.accepts("jvmArgs", "Custom JVM args to use when forking.") 167 .withRequiredArg().ofType(String.class).describedAs("string"); 168 169 OptionSpec<String> optJvmArgsAppend = parser.accepts("jvmArgsAppend", "Custom JVM args to use when forking (append these)") 170 .withRequiredArg().ofType(String.class).describedAs("string"); 171 172 OptionSpec<String> optJvmArgsPrepend = parser.accepts("jvmArgsPrepend", "Custom JVM args to use when forking (prepend these)") 173 .withRequiredArg().ofType(String.class).describedAs("string"); 174 175 OptionSpec<String> optTU = parser.accepts("tu", "Output time unit. Available time units are: [m, s, ms, us, ns].") 176 .withRequiredArg().ofType(String.class).describedAs("TU"); 177 178 OptionSpec<Integer> optOPI = parser.accepts("opi", "Operations per invocation.") 179 .withRequiredArg().withValuesConvertedBy(IntegerValueConverter.POSITIVE).describedAs("int"); 180 181 OptionSpec<String> optResultFormat = parser.accepts("rf", "Result format type. See the list of available result formats first.") 182 .withRequiredArg().ofType(String.class).describedAs("type"); 183 184 OptionSpec<String> optWarmupMode = parser.accepts("wm", "Warmup mode for warming up selected benchmarks. Warmup modes are: " + Arrays.toString(WarmupMode.values()) + ".") 185 .withRequiredArg().ofType(String.class).describedAs("mode"); 186 187 OptionSpec<String> optExcludes = parser.accepts("e", "Benchmarks to exclude from the run.") 188 .withRequiredArg().withValuesSeparatedBy(',').ofType(String.class).describedAs("regexp+"); 189 190 OptionSpec<String> optParams = parser.accepts("p", "Benchmark parameters. This option is expected to be used once" + 191 " per parameter. Parameter name and parameter values should be separated with equals sign." + 192 " Parameter values should be separated with commas.") 193 .withRequiredArg().ofType(String.class).describedAs("param={v,}*"); 194 195 OptionSpec<String> optWarmupBenchmarks = parser.accepts("wmb", "Warmup benchmarks to include in the run " + 196 "in addition to already selected. JMH will not measure these benchmarks, but only use them" + 197 " for the warmup.") 198 .withRequiredArg().withValuesSeparatedBy(',').ofType(String.class).describedAs("regexp+"); 199 200 parser.accepts("l", "List matching benchmarks and exit."); 201 parser.accepts("lp", "List matching benchmarks with parameters and exit."); 202 parser.accepts("lrf", "List result formats."); 203 parser.accepts("lprof", "List profilers."); 204 parser.accepts("h", "Display help."); 205 206 try { 207 OptionSet set = parser.parse(argv); 208 209 if (set.has(optExcludes)) { 210 excludes.addAll(optExcludes.values(set)); 211 } 212 213 if (set.has(optWarmupBenchmarks)) { 214 warmupMicros.addAll(optWarmupBenchmarks.values(set)); 215 } 216 217 if (set.has(optTU)) { 218 String va = optTU.value(set); 219 TimeUnit tu; 220 if (va.equalsIgnoreCase("ns")) { 221 tu = TimeUnit.NANOSECONDS; 222 } else if (va.equalsIgnoreCase("us")) { 223 tu = TimeUnit.MICROSECONDS; 224 } else if (va.equalsIgnoreCase("ms")) { 225 tu = TimeUnit.MILLISECONDS; 226 } else if (va.equalsIgnoreCase("s")) { 227 tu = TimeUnit.SECONDS; 228 } else if (va.equalsIgnoreCase("m")) { 229 tu = TimeUnit.MINUTES; 230 } else if (va.equalsIgnoreCase("h")) { 231 tu = TimeUnit.HOURS; 232 } else { 233 throw new CommandLineOptionException("Unknown time unit: " + va); 234 } 235 timeUnit = Optional.of(tu); 236 } else { 237 timeUnit = Optional.none(); 238 } 239 240 opsPerInvocation = toOptional(optOPI, set); 241 242 if (set.has(optWarmupMode)) { 243 try { 244 warmupMode = Optional.of(WarmupMode.valueOf(optWarmupMode.value(set))); 245 } catch (IllegalArgumentException iae) { 246 throw new CommandLineOptionException(iae.getMessage(), iae); 247 } 248 } else { 249 warmupMode = Optional.none(); 250 } 251 252 if (set.has(optResultFormat)) { 253 try { 254 resultFormat = Optional.of(ResultFormatType.valueOf(optResultFormat.value(set).toUpperCase())); 255 } catch (IllegalArgumentException iae) { 256 throw new CommandLineOptionException(iae.getMessage(), iae); 257 } 258 } else { 259 resultFormat = Optional.none(); 260 } 261 262 help = set.has("h"); 263 list = set.has("l"); 264 listWithParams = set.has("lp"); 265 listResultFormats = set.has("lrf"); 266 listProfilers = set.has("lprof"); 267 268 iterations = toOptional(optMeasureCount, set); 269 batchSize = toOptional(optMeasureBatchSize, set); 270 runTime = toOptional(optMeasureTime, set); 271 warmupIterations = toOptional(optWarmupCount, set); 272 warmupBatchSize = toOptional(optWarmupBatchSize, set); 273 warmupTime = toOptional(optWarmupTime, set); 274 timeout = toOptional(optTimeoutTime, set); 275 threads = toOptional(optThreads, set); 276 synchIterations = toOptional(optSyncIters, set); 277 gcEachIteration = toOptional(optGC, set); 278 failOnError = toOptional(optFOE, set); 279 fork = toOptional(optForks, set); 280 warmupFork = toOptional(optWarmupForks, set); 281 output = toOptional(optOutput, set); 282 result = toOptional(optOutputResults, set); 283 284 if (set.has(optBenchmarkMode)) { 285 try { 286 List<Mode> modes = new ArrayList<Mode>(); 287 for (String m : optBenchmarkMode.values(set)) { 288 modes.add(Mode.deepValueOf(m)); 289 } 290 benchMode.addAll(modes); 291 } catch (IllegalArgumentException iae) { 292 throw new CommandLineOptionException(iae.getMessage(), iae); 293 } 294 } 295 296 if (set.has(optVerboseMode)) { 297 try { 298 if (set.hasArgument(optVerboseMode)) { 299 verbose = Optional.of(VerboseMode.valueOf(set.valueOf(optVerboseMode).toUpperCase())); 300 } else { 301 verbose = Optional.of(VerboseMode.EXTRA); 302 } 303 } catch (IllegalArgumentException iae) { 304 throw new CommandLineOptionException(iae.getMessage(), iae); 305 } 306 } else { 307 verbose = Optional.none(); 308 } 309 310 regexps.addAll(set.valuesOf(optArgs)); 311 312 if (set.has(optProfilers)) { 313 try { 314 for (String m : optProfilers.values(set)) { 315 int idx = m.indexOf(":"); 316 String profName = (idx == -1) ? m : m.substring(0, idx); 317 String params = (idx == -1) ? "" : m.substring(idx + 1); 318 profilers.add(new ProfilerConfig(profName, params)); 319 } 320 } catch (IllegalArgumentException iae) { 321 throw new CommandLineOptionException(iae.getMessage(), iae); 322 } 323 } 324 325 if (set.has(optThreadGroups)) { 326 threadGroups.addAll(set.valuesOf(optThreadGroups)); 327 int total = 0; 328 for (int group : threadGroups) { 329 total += group; 330 } 331 if (total <= 0) { 332 throw new CommandLineOptionException("Group thread count should be positive, but it is " + total); 333 } 334 } 335 336 jvm = toOptional(optJvm, set); 337 338 jvmArgs = treatQuoted(set, optJvmArgs); 339 jvmArgsAppend = treatQuoted(set, optJvmArgsAppend); 340 jvmArgsPrepend = treatQuoted(set, optJvmArgsPrepend); 341 342 if (set.hasArgument(optParams)) { 343 for (String p : optParams.values(set)) { 344 String[] keys = p.split("=", 2); 345 if (keys.length != 2) { 346 throw new CommandLineOptionException("Unable to parse parameter string \"" + p + "\""); 347 } 348 params.putAll(keys[0], Arrays.asList(keys[1].split(","))); 349 } 350 } 351 352 } catch (OptionException e) { 353 String message = e.getMessage(); 354 Throwable cause = e.getCause(); 355 if (cause instanceof ValueConversionException) { 356 message += ". " + cause.getMessage(); 357 } 358 throw new CommandLineOptionException(message, e); 359 } 360 } 361 362 private static <T> Optional<T> toOptional(OptionSpec<T> option, OptionSet set) { 363 if (set.has(option)) { 364 return Optional.eitherOf(option.value(set)); 365 } 366 return Optional.none(); 367 } 368 369 public Optional<Collection<String>> treatQuoted(OptionSet set, OptionSpec<String> spec) { 370 if (set.hasArgument(spec)) { 371 try { 372 List<String> vals = spec.values(set); 373 if (vals.size() != 1) { 374 return Optional.<Collection<String>>of(vals); 375 } else { 376 // Windows launcher somehow ends up here, fall-through to single value treatment 377 } 378 } catch (OptionException e) { 379 // only a single value, fall through 380 } 381 return Optional.of(Utils.splitQuotedEscape(spec.value(set))); 382 } 383 return Optional.none(); 384 } 385 386 public void showHelp() throws IOException { 387 parser.printHelpOn(System.err); 388 } 389 390 public void listProfilers() { 391 ProfilerFactory.listProfilers(System.out); 392 } 393 394 public void listResultFormats() { 395 StringBuilder sb = new StringBuilder(); 396 397 for (ResultFormatType f : ResultFormatType.values()) { 398 sb.append(f.toString().toLowerCase()); 399 sb.append(", "); 400 } 401 sb.setLength(sb.length() - 2); 402 403 System.out.println("Available formats: " + sb.toString()); 404 } 405 406 public boolean shouldList() { 407 return list; 408 } 409 410 public boolean shouldListWithParams() { 411 return listWithParams; 412 } 413 414 public boolean shouldListResultFormats() { 415 return listResultFormats; 416 } 417 418 public boolean shouldHelp() { 419 return help; 420 } 421 422 public boolean shouldListProfilers() { 423 return listProfilers; 424 } 425 426 @Override 427 public Optional<WarmupMode> getWarmupMode() { 428 return warmupMode; 429 } 430 431 @Override 432 public List<String> getIncludes() { 433 return regexps; 434 } 435 436 @Override 437 public List<String> getExcludes() { 438 return excludes; 439 } 440 441 @Override 442 public List<String> getWarmupIncludes() { 443 return warmupMicros; 444 } 445 446 @Override 447 public Optional<String> getJvm() { 448 return jvm; 449 } 450 451 @Override 452 public Optional<Collection<String>> getJvmArgs() { 453 return jvmArgs; 454 } 455 456 @Override 457 public Optional<Collection<String>> getJvmArgsAppend() { 458 return jvmArgsAppend; 459 } 460 461 @Override 462 public Optional<Collection<String>> getJvmArgsPrepend() { 463 return jvmArgsPrepend; 464 } 465 466 @Override 467 public Optional<Collection<String>> getParameter(String name) { 468 Collection<String> list = params.get(name); 469 if (list == null || list.isEmpty()){ 470 return Optional.none(); 471 } else { 472 return Optional.of(list); 473 } 474 } 475 476 @Override 477 public Optional<Integer> getForkCount() { 478 return fork; 479 } 480 481 @Override 482 public Optional<Integer> getWarmupForkCount() { 483 return warmupFork; 484 } 485 486 @Override 487 public Optional<String> getOutput() { 488 return output; 489 } 490 491 @Override 492 public Optional<ResultFormatType> getResultFormat() { 493 return resultFormat; 494 } 495 496 @Override 497 public Optional<String> getResult() { 498 return result; 499 } 500 501 @Override 502 public Optional<Integer> getMeasurementIterations() { 503 return iterations; 504 } 505 506 @Override 507 public Optional<Integer> getMeasurementBatchSize() { 508 return batchSize; 509 } 510 511 @Override 512 public Optional<TimeValue> getMeasurementTime() { 513 return runTime; 514 } 515 516 @Override 517 public Optional<TimeValue> getWarmupTime() { 518 return warmupTime; 519 } 520 521 @Override 522 public Optional<Integer> getWarmupIterations() { 523 return warmupIterations; 524 } 525 526 @Override 527 public Optional<Integer> getWarmupBatchSize() { 528 return warmupBatchSize; 529 } 530 531 @Override 532 public Optional<Integer> getThreads() { 533 return threads; 534 } 535 536 @Override 537 public Optional<int[]> getThreadGroups() { 538 if (threadGroups.isEmpty()) { 539 return Optional.none(); 540 } else { 541 int[] r = new int[threadGroups.size()]; 542 for (int c = 0; c < r.length; c++) { 543 r[c] = threadGroups.get(c); 544 } 545 return Optional.of(r); 546 } 547 } 548 549 @Override 550 public Optional<Boolean> shouldDoGC() { 551 return gcEachIteration; 552 } 553 554 @Override 555 public Optional<Boolean> shouldSyncIterations() { 556 return synchIterations; 557 } 558 559 @Override 560 public Optional<VerboseMode> verbosity() { 561 return verbose; 562 } 563 564 @Override 565 public Optional<TimeUnit> getTimeUnit() { 566 return timeUnit; 567 } 568 569 @Override 570 public Optional<Integer> getOperationsPerInvocation() { 571 return opsPerInvocation; 572 } 573 574 @Override 575 public Optional<Boolean> shouldFailOnError() { 576 return failOnError; 577 } 578 579 @Override 580 public List<ProfilerConfig> getProfilers() { 581 return profilers; 582 } 583 584 @Override 585 public Collection<Mode> getBenchModes() { 586 return new HashSet<Mode>(benchMode); 587 } 588 589 @Override 590 public Optional<TimeValue> getTimeout() { 591 return timeout; 592 } 593 }