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 { isBenchmarkMethod(Method method)47 @Override public boolean isBenchmarkMethod(Method method) { 48 return method.isAnnotationPresent(ArbitraryMeasurement.class); 49 } 50 51 @Override createInstrumentation(Method benchmarkMethod)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 schedulingPolicy()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 { ArbitraryMeasurementInstrumentation(Method benchmarkMethod)86 protected ArbitraryMeasurementInstrumentation(Method benchmarkMethod) { 87 super(benchmarkMethod); 88 } 89 90 @Override dryRun(Object benchmark)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 workerClass()104 public Class<? extends Worker> workerClass() { 105 return ArbitraryMeasurementWorker.class; 106 } 107 workerOptions()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 getMeasurementCollectingVisitor()113 MeasurementCollectingVisitor getMeasurementCollectingVisitor() { 114 return new SingleMeasurementCollectingVisitor(); 115 } 116 } 117 118 @Override instrumentOptions()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 isDoneCollecting()128 public boolean isDoneCollecting() { 129 return measurement.isPresent(); 130 } 131 132 @Override isWarmupComplete()133 public boolean isWarmupComplete() { 134 return true; 135 } 136 137 @Override getMeasurements()138 public ImmutableList<Measurement> getMeasurements() { 139 return ImmutableList.copyOf(measurement.asSet()); 140 } 141 142 @Override visit(StopMeasurementLogMessage logMessage)143 public void visit(StopMeasurementLogMessage logMessage) { 144 this.measurement = Optional.of(Iterables.getOnlyElement(logMessage.measurements())); 145 } 146 147 @Override getMessages()148 public ImmutableList<String> getMessages() { 149 return ImmutableList.of(); 150 } 151 } 152 } 153