All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.caliper.Runner Maven / Gradle / Ivy

There is a newer version: 1.0-beta-3
Show newest version
/*
 * Copyright (C) 2009 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;

import com.google.caliper.UserException.DisplayUsageException;
import com.google.caliper.UserException.ExceptionFromUserCodeException;
import com.google.caliper.util.InterleavedReader;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ObjectArrays;
import com.google.common.io.Closeables;
import com.google.gson.JsonObject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import java.util.regex.Pattern;

/**
 * Creates, executes and reports benchmark runs.
 */
public final class Runner {

  private static final FileFilter UPLOAD_FILE_FILTER = new FileFilter() {
    @Override public boolean accept(File file) {
      return file.getName().endsWith(".xml") || file.getName().endsWith(".json");
    }
  };

  private static final String FILE_NAME_DATE_FORMAT = "yyyy-MM-dd'T'HH-mm-ssZ";

  private static final Splitter ARGUMENT_SPLITTER
      = Splitter.on(Pattern.compile("\\s+")).omitEmptyStrings();

  /** Command line arguments to the process */
  private Arguments arguments;
  private ScenarioSelection scenarioSelection;

  private String createFileName(Result result) {
    String timestamp = createTimestamp();
    return String.format("%s.%s.json", result.getRun().getBenchmarkName(), timestamp);
  }

  private String createTimestamp() {
    SimpleDateFormat dateFormat = new SimpleDateFormat(FILE_NAME_DATE_FORMAT, Locale.US);
    dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
    dateFormat.setLenient(true);
    return dateFormat.format(new Date());
  }

  public void run(String... args) {
    this.arguments = Arguments.parse(args);
    File resultsUploadFile = arguments.getUploadResultsFile();
    if (resultsUploadFile != null) {
      uploadResultsFileOrDir(resultsUploadFile);
      return;
    }
    this.scenarioSelection = new ScenarioSelection(arguments);
    if (arguments.getDebug()) {
      debug();
      return;
    }
    Result result = runOutOfProcess();
    new ConsoleReport(result.getRun(), arguments).displayResults();
    boolean saveResultsLocally = arguments.getSaveResultsFile() != null;
    try {
      postResults(result);
    } catch (Exception e) {
      System.out.println();
      System.out.println(e);
      saveResultsLocally = true;
    }

    if (saveResultsLocally) {
      saveResults(result);
    }
  }

  void uploadResultsFileOrDir(File resultsFileOrDir) {
    try {
      if (resultsFileOrDir.isDirectory()) {
        for (File resultsFile : resultsFileOrDir.listFiles(UPLOAD_FILE_FILTER)) {
          uploadResults(resultsFile);
        }
      } else {
        uploadResults(resultsFileOrDir);
      }
    } catch (Exception e) {
      throw new RuntimeException("uploading XML file failed", e);
    }
  }

  private void uploadResults(File resultsUploadFile) throws IOException {
    System.out.println();
    System.out.println("Uploading " + resultsUploadFile.getCanonicalPath());
    InputStream inputStream = new FileInputStream(resultsUploadFile);
    try {
      Result result = new ResultsReader().getResult(inputStream);
      postResults(result);
    } finally {
      inputStream.close();
    }
  }

  private void saveResults(Result result) {
    File resultsFile = arguments.getSaveResultsFile();
    File destinationFile;
    if (resultsFile == null) {
      File dir = new File("./caliper-results");
      dir.mkdirs();
      destinationFile = new File(dir, createFileName(result));
    } else if (resultsFile.exists() && resultsFile.isDirectory()) {
      destinationFile = new File(resultsFile, createFileName(result));
    } else {
      // assume this is a file
      File parent = resultsFile.getParentFile();
      if (parent != null) {
        parent.mkdirs();
      }
      destinationFile = resultsFile;
    }

    PrintStream filePrintStream;
    try {
      filePrintStream = new PrintStream(new FileOutputStream(destinationFile));
    } catch (FileNotFoundException e) {
      throw new RuntimeException("can't open " + destinationFile, e);
    }
    String resultJson = Json.getGsonInstance().toJson(result);
    try {
      System.out.println();
      System.out.println("Writing results to " + destinationFile.getCanonicalPath());
      filePrintStream.print(resultJson);
    } catch (Exception e) {
      System.out.println(e);
      System.out.println("Failed to write results to file, writing to standard out instead:");
      System.out.println(resultJson);
      System.out.flush();
    } finally {
      filePrintStream.close();
    }
  }

  private void postResults(Result result) {
    CaliperRc caliperrc = CaliperRc.INSTANCE;
    String postUrl = caliperrc.getPostUrl();
    String apiKey = caliperrc.getApiKey();
    if (postUrl == null || apiKey == null) {
      // TODO: probably nicer to show a message if only one is null
      return;
    }

    try {
      URL url = new URL(postUrl + apiKey + "/" + result.getRun().getBenchmarkName());
      HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(getProxy());
      urlConnection.setDoOutput(true);
      String resultJson = Json.getGsonInstance().toJson(result);
      urlConnection.getOutputStream().write(resultJson.getBytes());
      if (urlConnection.getResponseCode() == 200) {
        System.out.println("");
        System.out.println("View current and previous benchmark results online:");
        BufferedReader in = new BufferedReader(
            new InputStreamReader(urlConnection.getInputStream()));
        System.out.println("  " + in.readLine());
        in.close();
        return;
      }

      System.out.println("Posting to " + postUrl + " failed: "
          + urlConnection.getResponseMessage());
      BufferedReader reader = new BufferedReader(
          new InputStreamReader(urlConnection.getInputStream()));
      String line;
      while ((line = reader.readLine()) != null) {
        System.out.println(line);
      }
      reader.close();
    } catch (IOException e) {
      throw new RuntimeException("Posting to " + postUrl + " failed.", e);
    }
  }

  private Proxy getProxy() {
    String proxyAddress = CaliperRc.INSTANCE.getProxy();
    if (proxyAddress == null) {
      return Proxy.NO_PROXY;
    }

    String[] proxyHostAndPort = proxyAddress.trim().split(":");
    return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(
        proxyHostAndPort[0], Integer.parseInt(proxyHostAndPort[1])));
  }

  private ScenarioResult runScenario(Scenario scenario) {
    MeasurementResult timeMeasurementResult = measure(scenario, MeasurementType.TIME);
    MeasurementSet allocationMeasurements = null;
    String allocationEventLog = null;
    MeasurementSet memoryMeasurements = null;
    String memoryEventLog = null;
    if (arguments.getMeasureMemory()) {
      MeasurementResult allocationsMeasurementResult =
          measure(scenario, MeasurementType.INSTANCE);
      allocationMeasurements = allocationsMeasurementResult.getMeasurements();
      allocationEventLog = allocationsMeasurementResult.getEventLog();
      MeasurementResult memoryMeasurementResult =
          measure(scenario, MeasurementType.MEMORY);
      memoryMeasurements = memoryMeasurementResult.getMeasurements();
      memoryEventLog = memoryMeasurementResult.getEventLog();
    }

    return new ScenarioResult(timeMeasurementResult.getMeasurements(),
        timeMeasurementResult.getEventLog(),
        allocationMeasurements, allocationEventLog,
        memoryMeasurements, memoryEventLog);
  }

  private static class MeasurementResult {
    private final MeasurementSet measurements;
    private final String eventLog;

    MeasurementResult(MeasurementSet measurements, String eventLog) {
      this.measurements = measurements;
      this.eventLog = eventLog;
    }

    public MeasurementSet getMeasurements() {
      return measurements;
    }

    public String getEventLog() {
      return eventLog;
    }
  }

  private MeasurementResult measure(Scenario scenario, MeasurementType type) {
    Vm vm = new VmFactory().createVm(scenario);
    // this must be done before starting the forked process on certain VMs
    ProcessBuilder processBuilder = createCommand(scenario, vm, type)
        .redirectErrorStream(true);
    Process timeProcess;
    try {
      timeProcess = processBuilder.start();
    } catch (IOException e) {
      throw new RuntimeException("failed to start subprocess", e);
    }

    MeasurementSet measurementSet = null;
    StringBuilder eventLog = new StringBuilder();
    InterleavedReader reader = null;
    try {
      reader = new InterleavedReader(arguments.getMarker(),
          new InputStreamReader(timeProcess.getInputStream()));
      Object o;
      while ((o = reader.read()) != null) {
        if (o instanceof String) {
          eventLog.append(o);
        } else if (measurementSet == null) {
          JsonObject jsonObject = (JsonObject) o;
          measurementSet = Json.measurementSetFromJson(jsonObject);
        } else {
          throw new RuntimeException("Unexpected value: " + o);
        }
      }
    } catch (IOException e) {
      throw new RuntimeException(e);
    } finally {
      Closeables.closeQuietly(reader);
      timeProcess.destroy();
    }

    if (measurementSet == null) {
      String message = "Failed to execute " + Joiner.on(" ").join(processBuilder.command());
      System.err.println("  " + message);
      System.err.println(eventLog.toString());
      throw new ConfigurationException(message);
    }

    return new MeasurementResult(measurementSet, eventLog.toString());
  }

  private ProcessBuilder createCommand(Scenario scenario, Vm vm, MeasurementType type) {
    File workingDirectory = new File(System.getProperty("user.dir"));

    String classPath = System.getProperty("java.class.path");
    if (classPath == null || classPath.length() == 0) {
      throw new IllegalStateException("java.class.path is undefined in " + System.getProperties());
    }

    ImmutableList.Builder vmArgs = ImmutableList.builder();
    vmArgs.addAll(ARGUMENT_SPLITTER.split(scenario.getVariables().get(Scenario.VM_KEY)));
    if (type == MeasurementType.INSTANCE || type == MeasurementType.MEMORY) {
      String allocationJarFile = System.getenv("ALLOCATION_JAR");
      vmArgs.add("-javaagent:" + allocationJarFile);
    }
    vmArgs.addAll(vm.getVmSpecificOptions(type, arguments));

    Map vmParameters = scenario.getVariables(
        scenarioSelection.getVmParameterNames());
    for (String vmParameter : vmParameters.values()) {
      vmArgs.addAll(ARGUMENT_SPLITTER.split(vmParameter));
    }

    ImmutableList.Builder caliperArgs = ImmutableList.builder();
    caliperArgs.add("--warmupMillis").add(Long.toString(arguments.getWarmupMillis()));
    caliperArgs.add("--runMillis").add(Long.toString(arguments.getRunMillis()));
    caliperArgs.add("--measurementType").add(type.toString());
    caliperArgs.add("--marker").add(arguments.getMarker());

    Map userParameters = scenario.getVariables(
        scenarioSelection.getUserParameterNames());
    for (Entry entry : userParameters.entrySet()) {
      caliperArgs.add("-D" + entry.getKey() + "=" + entry.getValue());
    }
    caliperArgs.add(arguments.getSuiteClassName());

    return vm.newProcessBuilder(workingDirectory, classPath,
        vmArgs.build(), InProcessRunner.class.getName(), caliperArgs.build());
  }

  private void debug() {
    try {
      int debugReps = arguments.getDebugReps();
      InProcessRunner runner = new InProcessRunner();
      DebugMeasurer measurer = new DebugMeasurer(debugReps);
      for (Scenario scenario : scenarioSelection.select()) {
        System.out.println("running " + debugReps + " debug reps of " + scenario);
        runner.run(scenarioSelection, scenario, measurer);
      }
    } catch (Exception e) {
      throw new ExceptionFromUserCodeException(e);
    }
  }

  private Result runOutOfProcess() {
    Date executedDate = new Date();
    ImmutableMap.Builder resultsBuilder = ImmutableMap.builder();

    try {
      List scenarios = scenarioSelection.select();

      int i = 0;
      for (Scenario scenario : scenarios) {
        beforeMeasurement(i++, scenarios.size(), scenario);
        ScenarioResult scenarioResult = runScenario(scenario);
        afterMeasurement(arguments.getMeasureMemory(), scenarioResult);
        resultsBuilder.put(scenario, scenarioResult);
      }
      System.out.println();

      Environment environment = new EnvironmentGetter().getEnvironmentSnapshot();
      return new Result(
          new Run(resultsBuilder.build(), arguments.getSuiteClassName(), executedDate),
          environment);
    } catch (Exception e) {
      throw new ExceptionFromUserCodeException(e);
    }
  }

  private void beforeMeasurement(int index, int total, Scenario scenario) {
    double percentDone = (double) index / total;
    System.out.printf("%2.0f%% %s", percentDone * 100, scenario);
  }

  private void afterMeasurement(boolean memoryMeasured, ScenarioResult scenarioResult) {
    String memoryMeasurements = "";
    if (memoryMeasured) {
      MeasurementSet instanceMeasurementSet =
          scenarioResult.getMeasurementSet(MeasurementType.INSTANCE);
      String instanceUnit =
        ConsoleReport.UNIT_ORDERING.min(instanceMeasurementSet.getUnitNames().entrySet()).getKey();
      MeasurementSet memoryMeasurementSet = scenarioResult.getMeasurementSet(MeasurementType.MEMORY);
      String memoryUnit =
        ConsoleReport.UNIT_ORDERING.min(memoryMeasurementSet.getUnitNames().entrySet()).getKey();
      memoryMeasurements = String.format(", allocated %s%s for a total of %s%s",
          Math.round(instanceMeasurementSet.medianUnits()), instanceUnit,
          Math.round(memoryMeasurementSet.medianUnits()), memoryUnit);
    }

    MeasurementSet timeMeasurementSet = scenarioResult.getMeasurementSet(MeasurementType.TIME);
    String unit =
        ConsoleReport.UNIT_ORDERING.min(timeMeasurementSet.getUnitNames().entrySet()).getKey();
    System.out.printf(" %.2f %s; \u03C3=%.2f %s @ %d trials%s%n", timeMeasurementSet.medianUnits(),
        unit, timeMeasurementSet.standardDeviationUnits(), unit,
        timeMeasurementSet.getMeasurements().size(), memoryMeasurements);
  }

  public static void main(String[] args) {
    try {
      new Runner().run(args);
      System.exit(0); // user code may have leave non-daemon threads behind!
    } catch (DisplayUsageException e) {
      e.display();
      System.exit(0);
    } catch (UserException e) {
      e.display();
      System.exit(1);
    }
  }

  @SuppressWarnings("unchecked") // temporary fakery
  public static void main(Class suite, String[] args) {
    main(ObjectArrays.concat(args, suite.getName()));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy