Home | History | Annotate | Download | only in runner
      1 /*
      2  * Copyright (C) 2011 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
      5  * in compliance with the License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the License
     10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
     11  * or implied. See the License for the specific language governing permissions and limitations under
     12  * the License.
     13  */
     14 
     15 package com.google.caliper.runner;
     16 
     17 import static com.google.common.collect.ObjectArrays.concat;
     18 
     19 import com.google.caliper.config.InvalidConfigurationException;
     20 import com.google.caliper.options.CaliperOptions;
     21 import com.google.caliper.options.OptionsModule;
     22 import com.google.caliper.util.InvalidCommandException;
     23 import com.google.caliper.util.OutputModule;
     24 import com.google.common.base.Strings;
     25 import com.google.common.collect.ImmutableSortedMap;
     26 import com.google.common.util.concurrent.ServiceManager;
     27 
     28 import java.io.PrintWriter;
     29 import java.util.Map.Entry;
     30 import java.util.concurrent.TimeUnit;
     31 import java.util.concurrent.TimeoutException;
     32 
     33 import javax.annotation.Nullable;
     34 
     35 /**
     36  * Primary entry point for the caliper benchmark runner application; run with {@code --help} for
     37  * details. This class's only purpose is to take care of anything that's specific to command-line
     38  * invocation and then hand off to {@code CaliperRun}. That is, a hypothetical GUI benchmark runner
     39  * might still use {@code CaliperRun} but would skip using this class.
     40  */
     41 public final class CaliperMain {
     42   /**
     43    * Your benchmark classes can implement main() like this: <pre>   {@code
     44    *
     45    *   public static void main(String[] args) {
     46    *     CaliperMain.main(MyBenchmark.class, args);
     47    *   }}</pre>
     48    *
     49    * Note that this method does invoke {@link System#exit} when it finishes. Consider {@link
     50    * #exitlessMain} if you don't want that.
     51    *
     52    * <p>Measurement is handled in a subprocess, so it will not use {@code benchmarkClass} itself;
     53    * the class is provided here only as a shortcut for specifying the full class <i>name</i>. The
     54    * class that gets loaded later could be completely different.
     55    */
     56   public static void main(Class<?> benchmarkClass, String[] args) {
     57     main(concat(args, benchmarkClass.getName()));
     58   }
     59 
     60   /**
     61    * Entry point for the caliper benchmark runner application; run with {@code --help} for details.
     62    */
     63   public static void main(String[] args) {
     64     PrintWriter stdout = new PrintWriter(System.out, true);
     65     PrintWriter stderr = new PrintWriter(System.err, true);
     66     int code = 1; // pessimism!
     67 
     68     try {
     69       exitlessMain(args, stdout, stderr);
     70       code = 0;
     71 
     72     } catch (InvalidCommandException e) {
     73       e.display(stderr);
     74       code = e.exitCode();
     75 
     76     } catch (InvalidBenchmarkException e) {
     77       e.display(stderr);
     78 
     79     } catch (InvalidConfigurationException e) {
     80       e.display(stderr);
     81 
     82     } catch (Throwable t) {
     83       t.printStackTrace(stderr);
     84       stdout.println();
     85       stdout.println("An unexpected exception has been thrown by the caliper runner.");
     86       stdout.println("Please see https://sites.google.com/site/caliperusers/issues");
     87     }
     88 
     89     stdout.flush();
     90     stderr.flush();
     91     System.exit(code);
     92   }
     93 
     94   private static final String LEGACY_ENV = "USE_LEGACY_CALIPER";
     95 
     96   public static void exitlessMain(String[] args, PrintWriter stdout, PrintWriter stderr)
     97       throws InvalidCommandException, InvalidBenchmarkException, InvalidConfigurationException {
     98     @Nullable String legacyCaliperEnv = System.getenv(LEGACY_ENV);
     99     if (!Strings.isNullOrEmpty(legacyCaliperEnv)) {
    100       System.err.println("Legacy Caliper is no more. " + LEGACY_ENV + " has no effect.");
    101     }
    102     try {
    103       MainComponent mainComponent = DaggerMainComponent.builder()
    104           .optionsModule(OptionsModule.withBenchmarkClass(args))
    105           .outputModule(new OutputModule(stdout, stderr))
    106           .build();
    107       CaliperOptions options = mainComponent.getCaliperOptions();
    108       if (options.printConfiguration()) {
    109         stdout.println("Configuration:");
    110         ImmutableSortedMap<String, String> sortedProperties =
    111             ImmutableSortedMap.copyOf(mainComponent.getCaliperConfig().properties());
    112         for (Entry<String, String> entry : sortedProperties.entrySet()) {
    113           stdout.printf("  %s = %s%n", entry.getKey(), entry.getValue());
    114         }
    115       }
    116       // check that the parameters are valid
    117       mainComponent.getBenchmarkClass().validateParameters(options.userParameters());
    118       ServiceManager serviceManager = mainComponent.getServiceManager();
    119       serviceManager.startAsync().awaitHealthy();
    120       try {
    121         CaliperRun run = mainComponent.getCaliperRun();
    122         run.run(); // throws IBE
    123       } finally {
    124         try {
    125           // We have some shutdown logic to ensure that files are cleaned up so give it a chance to
    126           // run
    127           serviceManager.stopAsync().awaitStopped(10, TimeUnit.SECONDS);
    128         } catch (TimeoutException e) {
    129           // That's fine
    130         }
    131       }
    132     } finally {
    133       // courtesy flush
    134       stderr.flush();
    135       stdout.flush();
    136     }
    137   }
    138 }
    139