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");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.caliper.runner;
     18 
     19 import static com.google.caliper.runner.CommonInstrumentOptions.GC_BEFORE_EACH_OPTION;
     20 import static com.google.common.base.Throwables.propagateIfInstanceOf;
     21 
     22 import com.google.caliper.api.SkipThisScenarioException;
     23 import com.google.caliper.bridge.AbstractLogMessageVisitor;
     24 import com.google.caliper.bridge.StopMeasurementLogMessage;
     25 import com.google.caliper.model.ArbitraryMeasurement;
     26 import com.google.caliper.model.Measurement;
     27 import com.google.caliper.platform.Platform;
     28 import com.google.caliper.platform.SupportedPlatform;
     29 import com.google.caliper.util.Util;
     30 import com.google.caliper.worker.ArbitraryMeasurementWorker;
     31 import com.google.caliper.worker.Worker;
     32 import com.google.common.base.Optional;
     33 import com.google.common.collect.ImmutableList;
     34 import com.google.common.collect.ImmutableMap;
     35 import com.google.common.collect.ImmutableSet;
     36 import com.google.common.collect.Iterables;
     37 
     38 import java.lang.reflect.InvocationTargetException;
     39 import java.lang.reflect.Method;
     40 
     41 /**
     42  * Instrument for taking an arbitrary measurement. When using this instrument, the benchmark code
     43  * itself returns the value. See {@link ArbitraryMeasurement}.
     44  */
     45 @SupportedPlatform(Platform.Type.JVM)
     46 public final class ArbitraryMeasurementInstrument extends Instrument {
     47   @Override public boolean isBenchmarkMethod(Method method) {
     48     return method.isAnnotationPresent(ArbitraryMeasurement.class);
     49   }
     50 
     51   @Override
     52   public Instrumentation createInstrumentation(Method benchmarkMethod)
     53       throws InvalidBenchmarkException {
     54     if (benchmarkMethod.getParameterTypes().length != 0) {
     55       throw new InvalidBenchmarkException(
     56           "Arbitrary measurement methods should take no parameters: " + benchmarkMethod.getName());
     57     }
     58 
     59     if (benchmarkMethod.getReturnType() != double.class) {
     60       throw new InvalidBenchmarkException(
     61           "Arbitrary measurement methods must have a return type of double: "
     62               + benchmarkMethod.getName());
     63     }
     64 
     65     // Static technically doesn't hurt anything, but it's just the completely wrong idea
     66     if (Util.isStatic(benchmarkMethod)) {
     67       throw new InvalidBenchmarkException(
     68           "Arbitrary measurement methods must not be static: " + benchmarkMethod.getName());
     69     }
     70 
     71     if (!Util.isPublic(benchmarkMethod)) {
     72       throw new InvalidBenchmarkException(
     73           "Arbitrary measurement methods must be public: " + benchmarkMethod.getName());
     74     }
     75 
     76     return new ArbitraryMeasurementInstrumentation(benchmarkMethod);
     77   }
     78 
     79   @Override public TrialSchedulingPolicy schedulingPolicy() {
     80     // We could allow it here but in general it would depend on the particular measurement so it
     81     // should probably be configured by the user.  For now we just disable it.
     82     return TrialSchedulingPolicy.SERIAL;
     83   }
     84 
     85   private final class ArbitraryMeasurementInstrumentation extends Instrumentation {
     86     protected ArbitraryMeasurementInstrumentation(Method benchmarkMethod) {
     87       super(benchmarkMethod);
     88     }
     89 
     90     @Override
     91     public void dryRun(Object benchmark) throws InvalidBenchmarkException {
     92       try {
     93         benchmarkMethod.invoke(benchmark);
     94       } catch (IllegalAccessException impossible) {
     95         throw new AssertionError(impossible);
     96       } catch (InvocationTargetException e) {
     97         Throwable userException = e.getCause();
     98         propagateIfInstanceOf(userException, SkipThisScenarioException.class);
     99         throw new UserCodeException(userException);
    100       }
    101     }
    102 
    103     @Override
    104     public Class<? extends Worker> workerClass() {
    105       return ArbitraryMeasurementWorker.class;
    106     }
    107 
    108     @Override public ImmutableMap<String, String> workerOptions() {
    109       return ImmutableMap.of(GC_BEFORE_EACH_OPTION, options.get(GC_BEFORE_EACH_OPTION));
    110     }
    111 
    112     @Override
    113     MeasurementCollectingVisitor getMeasurementCollectingVisitor() {
    114       return new SingleMeasurementCollectingVisitor();
    115     }
    116   }
    117 
    118   @Override
    119   public ImmutableSet<String> instrumentOptions() {
    120     return ImmutableSet.of(GC_BEFORE_EACH_OPTION);
    121   }
    122 
    123   private static final class SingleMeasurementCollectingVisitor extends AbstractLogMessageVisitor
    124       implements MeasurementCollectingVisitor {
    125     Optional<Measurement> measurement = Optional.absent();
    126 
    127     @Override
    128     public boolean isDoneCollecting() {
    129       return measurement.isPresent();
    130     }
    131 
    132     @Override
    133     public boolean isWarmupComplete() {
    134       return true;
    135     }
    136 
    137     @Override
    138     public ImmutableList<Measurement> getMeasurements() {
    139       return ImmutableList.copyOf(measurement.asSet());
    140     }
    141 
    142     @Override
    143     public void visit(StopMeasurementLogMessage logMessage) {
    144       this.measurement = Optional.of(Iterables.getOnlyElement(logMessage.measurements()));
    145     }
    146 
    147     @Override
    148     public ImmutableList<String> getMessages() {
    149       return ImmutableList.of();
    150     }
    151   }
    152 }
    153