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 }