com.google.caliper.runner.instrument.InstrumentModule.java Source code

Java tutorial

Introduction

Here is the source code for com.google.caliper.runner.instrument.InstrumentModule.java

Source

/*
 * Copyright (C) 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.caliper.runner.instrument;

import com.google.caliper.core.BenchmarkClassModel;
import com.google.caliper.core.BenchmarkClassModel.MethodModel;
import com.google.caliper.core.InvalidBenchmarkException;
import com.google.caliper.core.InvalidInstrumentException;
import com.google.caliper.runner.RunScoped;
import com.google.caliper.runner.config.CaliperConfig;
import com.google.caliper.runner.config.InstrumentConfig;
import com.google.caliper.runner.config.VmType;
import com.google.caliper.runner.instrument.Instrument.InstrumentedMethod;
import com.google.caliper.runner.options.CaliperOptions;
import com.google.caliper.util.InvalidCommandException;
import com.google.caliper.util.ShortDuration;
import com.google.caliper.util.Stderr;
import com.google.caliper.util.Util;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import dagger.MapKey;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.inject.Provider;

/** Configures the {@link Instrument}s for a Caliper run. */
@Module(includes = NanoTimeGranularityModule.class)
public abstract class InstrumentModule {
    private InstrumentModule() {
    }

    /**
     * Specifies the {@link Class} object to use as a key in the map of available {@link Instrument
     * instruments} passed to {@link #provideInstruments},
     */
    @MapKey
    public @interface InstrumentClassKey {
        Class<? extends Instrument> value();
    }

    @Provides
    @IntoMap
    @InstrumentClassKey(ArbitraryMeasurementInstrument.class)
    static Instrument provideArbitraryMeasurementInstrument() {
        return new ArbitraryMeasurementInstrument();
    }

    @Provides
    @IntoMap
    @InstrumentClassKey(RuntimeInstrument.class)
    static Instrument provideRuntimeInstrument(@NanoTimeGranularity ShortDuration nanoTimeGranularity) {
        return new RuntimeInstrument(nanoTimeGranularity);
    }

    @RunScoped
    @Provides
    static ImmutableSet<Instrument> provideInstruments(CaliperOptions options, final CaliperConfig config,
            Map<Class<? extends Instrument>, Provider<Instrument>> availableInstruments,
            ImmutableSet<VmType> vmTypes, @Stderr PrintWriter stderr) throws InvalidCommandException {

        ImmutableSet.Builder<Instrument> builder = ImmutableSet.builder();
        ImmutableSet<String> configuredInstruments = config.getConfiguredInstruments();
        ImmutableSet<String> selectedInstruments = options.instrumentNames();

        if (selectedInstruments.isEmpty()) {
            selectedInstruments = config.getDefaultInstruments();
        }

        for (final String instrumentName : selectedInstruments) {
            if (!configuredInstruments.contains(instrumentName)) {
                throw new InvalidCommandException(
                        "%s is not a configured instrument (%s). "
                                + "use --print-config to see the configured instruments.",
                        instrumentName, configuredInstruments);
            }
            final InstrumentConfig instrumentConfig = config.getInstrumentConfig(instrumentName);
            String className = instrumentConfig.className();
            try {
                Class<? extends Instrument> clazz = Util.lenientClassForName(className)
                        .asSubclass(Instrument.class);
                Provider<Instrument> instrumentProvider = availableInstruments.get(clazz);
                if (instrumentProvider == null) {
                    throw new InvalidInstrumentException("Instrument %s not supported", className);
                }

                if (isSupportedByAllVms(clazz, vmTypes)) {
                    Instrument instrument = instrumentProvider.get();
                    InstrumentInjectorModule injectorModule = new InstrumentInjectorModule(instrumentConfig,
                            instrumentName);
                    InstrumentComponent instrumentComponent = DaggerInstrumentComponent.builder()
                            .instrumentInjectorModule(injectorModule).build();
                    instrumentComponent.injectInstrument(instrument);
                    builder.add(instrument);
                } else {
                    stderr.format("Instrument %s not supported on at least one target VM; ignoring\n", className);
                }
            } catch (ClassNotFoundException e) {
                throw new InvalidCommandException("Cannot find instrument class '%s'", className);
            }
        }
        return builder.build();
    }

    /**
     * If the user is running the benchmark against multiple VMs, we can only use instruments that all
     * of those VMs support.
     */
    private static boolean isSupportedByAllVms(Class<?> instrumentClass, ImmutableSet<VmType> vmTypes) {
        for (VmType vmType : vmTypes) {
            if (!vmType.supports(instrumentClass)) {
                return false;
            }
        }
        return true;
    }

    @Provides
    static ImmutableSet<InstrumentedMethod> provideInstrumentedMethods(CaliperOptions options,
            BenchmarkClassModel benchmarkClass, ImmutableSet<Instrument> instruments)
            throws InvalidBenchmarkException {
        ImmutableSet.Builder<InstrumentedMethod> builder = ImmutableSet.builder();
        ImmutableSet<String> benchmarkMethodNames = options.benchmarkMethodNames();
        Set<String> unusedBenchmarkNames = new HashSet<String>(benchmarkMethodNames);
        for (Instrument instrument : instruments) {
            for (MethodModel method : findAllBenchmarkMethods(benchmarkClass, instrument)) {
                if (benchmarkMethodNames.isEmpty() || benchmarkMethodNames.contains(method.name())) {
                    builder.add(instrument.createInstrumentedMethod(method));
                    unusedBenchmarkNames.remove(method.name());
                }
            }
        }
        if (!unusedBenchmarkNames.isEmpty()) {
            throw new InvalidBenchmarkException("Invalid benchmark method(s) specified in options: %s",
                    unusedBenchmarkNames);
        }
        return builder.build();
    }

    private static ImmutableSortedSet<MethodModel> findAllBenchmarkMethods(BenchmarkClassModel benchmarkClass,
            Instrument instrument) throws InvalidBenchmarkException {
        ImmutableSortedSet.Builder<MethodModel> result = ImmutableSortedSet
                .orderedBy(Ordering.natural().onResultOf(new Function<MethodModel, String>() {
                    @Override
                    public String apply(MethodModel method) {
                        return method.name();
                    }
                }));
        Set<String> benchmarkMethodNames = new HashSet<String>();
        Set<String> overloadedMethodNames = new TreeSet<String>();
        for (MethodModel method : benchmarkClass.methods()) {
            if (instrument.isBenchmarkMethod(method)) {
                result.add(method);
                if (!benchmarkMethodNames.add(method.name())) {
                    overloadedMethodNames.add(method.name());
                }
            }
        }
        if (!overloadedMethodNames.isEmpty()) {
            throw new InvalidBenchmarkException(
                    "Overloads are disallowed for benchmark methods, found overloads of %s in benchmark %s",
                    overloadedMethodNames, benchmarkClass.simpleName());
        }
        return result.build();
    }
}