org.dacapo.harness.CommandLineArgs.java Source code

Java tutorial

Introduction

Here is the source code for org.dacapo.harness.CommandLineArgs.java

Source

/*
 * Copyright (c) 2006, 2009 The Australian National University.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License v2.0.
 * You may obtain the license at
 * 
 *    http://www.opensource.org/licenses/apache2.0.php
 */
package org.dacapo.harness;

import java.io.File;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.jar.JarEntry;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.dacapo.parser.Config;

/**
 * Command line arguments for a dacapo benchmark run.
 * 
 * Encapsulated in an object so that it can be passed to user-written callbacks.
 * 
 * @date $Date$
 * @id $Id$
 */
public class CommandLineArgs {

    public enum Methodology {
        ITERATE, CONVERGE;
    }

    public final static int EXIT_OK = 0;
    public final static int EXIT_MISSING_CALLBACK = 2;
    public final static int EXIT_BAD_CALLBACK = 3;
    public final static int EXIT_BAD_COMMANDLINE = 4;
    public final static int EXIT_UNKNOWN_BENCHMARK = 9;
    public final static int EXIT_MISSING_BENCHMARKS = 10;

    public static final String RELEASE_NOTES = "RELEASE_NOTES.txt";
    public static final String DEFAULT_SIZE = "default";
    public static final String DEFAULT_SCRATCH_DIRECTORY = "./scratch";
    public static final String DEFAULT_MAX_ITERATIONS = "20";
    public static final String DEFAULT_WINDOW_SIZE = "3";
    public static final String DEFAULT_VARIANCE = "3.0";
    public static final String DEFAULT_ITERATIONS = "1";
    public static final String DEFAULT_THREAD_COUNT = "0"; // 0 represents
                                                           // unspecified
    public static final String DEFAULT_THREAD_FACTOR = "0"; // 0 represents
                                                            // unspecified

    private static final String OPT_CALLBACK = "callback";
    private static final String OPT_HELP = "help";
    private static final String OPT_RELEASE_NOTES = "release-notes";
    private static final String OPT_LIST_BENCHMARKS = "list-benchmarks";
    private static final String OPT_INFORMATION = "information";
    private static final String OPT_SIZE = "size";
    private static final String OPT_SCRATCH_DIRECTORY = "scratch-directory";
    private static final String OPT_CONVERGE = "converge";
    private static final String OPT_MAX_ITERATIONS = "max-iterations";
    private static final String OPT_VARIANCE = "variance";
    private static final String OPT_WINDOW = "window";
    private static final String OPT_ITERATIONS = "iterations";
    private static final String OPT_DEBUG = "debug";
    private static final String OPT_IGNORE_VALIDATION = "ignore-validation";
    private static final String OPT_NO_DIGEST_OUTPUT = "no-digest-output";
    private static final String OPT_NO_VALIDATION = "no-validation";
    private static final String OPT_PRESERVE = "preserve";
    private static final String OPT_VALIDATION_REPORT = "validation-report";
    private static final String OPT_CONFIG = "config";
    private static final String OPT_VERBOSE = "verbose";
    private static final String OPT_NO_PRE_ITERATION_GC = "no-pre-iteration-gc";
    private static final String OPT_THREAD_COUNT = "thread-count";
    private static final String OPT_THREAD_FACTOR = "thread-factor";

    private static final Option[] OPTIONS = {
            makeOption("c", OPT_CALLBACK, "Use class <callback> to bracket benchmark runs", "callback"),
            makeOption("h", OPT_HELP, "Print this help", null),
            makeOption("r", OPT_RELEASE_NOTES, "Print the release notes", null),
            makeOption("l", OPT_LIST_BENCHMARKS, "List available benchmarks", null),
            makeOption("i", OPT_INFORMATION, "Display benchmark information", null),
            makeOption("s", OPT_SIZE, "Size of input data", "SIZE"),
            makeOption(null, OPT_SCRATCH_DIRECTORY, "Specify an alternate scratch directory <dir>", "dir"),
            makeOption("C", OPT_CONVERGE, "Allow benchmark times to converge before timing", null),
            makeOption(null, OPT_MAX_ITERATIONS, "Run a max of <max_iterations> iterations (default 20)",
                    "max_iterations"),
            makeOption(null, OPT_VARIANCE, "Target coefficient of variation <pct> (default 3.0)", "pct"),
            makeOption(null, OPT_WINDOW, "Measure variance over <window> runs (default 3)", "window"),
            makeOption("n", OPT_ITERATIONS, "Run the benchmark <iter> times", "iter"),
            makeOption("d", OPT_DEBUG, "Verbose debugging information", null),
            makeOption(null, OPT_IGNORE_VALIDATION, "Don't halt on validation failure", null),
            makeOption(null, OPT_NO_DIGEST_OUTPUT, "Turn off SHA1 digest of stdout/stderr", null),
            makeOption(null, OPT_NO_VALIDATION, "Don't validate at all", null),
            makeOption(null, OPT_PRESERVE, "Preserve output files (debug)", null),
            makeOption(null, OPT_VALIDATION_REPORT, "Report digests, line counts etc", "report_file"),
            makeOption(null, OPT_CONFIG, null, "config_file"),
            makeOption(null, OPT_NO_PRE_ITERATION_GC,
                    "Skip performing System.gc() before the start of each iteration", null),
            makeOption("t", OPT_THREAD_COUNT, "Set the thread count to drive the workload (mutually exclusive -k)",
                    "thread_count"),
            makeOption("k", OPT_THREAD_FACTOR,
                    "Set the number of threads per CPU to drive the workload (mutually exclusive with -t)",
                    "thread_per_cpu"),
            makeOption("v", OPT_VERBOSE, "Verbose output", null) };

    private static CommandLineParser parser = new PosixParser();
    private static Options options = new Options();
    private static Options visibleOptions = new Options();

    {
        // Construct the option list and the visibleOption list.
        // The option list is used for parsing the command line,
        // where as the visibleOption is a subset of the option list
        // and is used for producing the usage help.
        for (int i = 0; i < OPTIONS.length; i++) {
            options.addOption(OPTIONS[i]);
            if (OPTIONS[i].getDescription() != null)
                visibleOptions.addOption(OPTIONS[i]);
        }
    }

    private CommandLine line;
    private Callback callback = null;
    private List<String> benchmarks = new ArrayList<String>();

    CommandLineArgs(String[] args) throws Exception {
        try {
            boolean reportAndExitOk = false;
            line = parser.parse(options, args);

            // report general benchmark information if requested and terminate
            if (line.hasOption(OPT_LIST_BENCHMARKS)) {
                printBenchmarks();
                reportAndExitOk = true;
            }
            if (line.hasOption(OPT_RELEASE_NOTES)) {
                printReleaseNotes();
                reportAndExitOk = true;
            }
            if (line.hasOption(OPT_HELP)) {
                printUsage();
                reportAndExitOk = true;
            }
            if (line.hasOption(OPT_THREAD_FACTOR) && line.hasOption(OPT_THREAD_COUNT)) {
                throw new ParseException("Cannot specify -t and -k options.");
            }
            if (reportAndExitOk)
                System.exit(EXIT_OK);
        } catch (ParseException e) {
            System.err.println("Command line exception: " + e.getMessage());
            System.exit(EXIT_BAD_COMMANDLINE);
        } catch (Exception e) {
            System.err.println("Exception processing command line values: " + e.getMessage());
            System.exit(EXIT_BAD_COMMANDLINE);
        }

        // configure the callback
        defineCallback();

        // check that at least one or more benchmarks are specified or a
        // config file is specified but not both
        if (line.getArgList().isEmpty() && !line.hasOption(OPT_CONFIG)) {
            printUsage();
            System.exit(EXIT_MISSING_BENCHMARKS);
        } else if (line.hasOption(OPT_CONFIG) && !line.getArgList().isEmpty()) {
            System.err.println("You may only specify a cnf or a list of benchmarks, but not both");
            System.exit(EXIT_BAD_COMMANDLINE);
        }

        List<String> benchmarkSet = extractBenchmarkSet();

        if (!line.getArgList().isEmpty()) {
            // now get the benchmark names and check against the available benchmarks
            for (Object bm : line.getArgList()) {
                if (benchmarkSet.contains(bm))
                    benchmarks.add((String) bm);
                else {
                    System.err.println("Unknown benchmark: " + bm);
                    System.exit(EXIT_UNKNOWN_BENCHMARK);
                }
            }
        } else {
            // determine benchmark from the cnf file and add it to the benchmarks
            File cnfFile = new File(line.getOptionValue(OPT_CONFIG));

            if (!cnfFile.canRead()) {
                System.err.println("Unknown config file: " + cnfFile.getAbsolutePath());
                System.exit(EXIT_UNKNOWN_BENCHMARK);
            }

            Config config = Config.parse(cnfFile);

            if (benchmarkSet.contains(config.name))
                benchmarks.add(config.name);
            else {
                System.err.println("Unknown benchmark specified in cnf file: " + config.name);
                System.exit(EXIT_UNKNOWN_BENCHMARK);
            }
        }
    }

    /**
     * Print a usage message to stdout
     */
    static void printUsage() {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("DaCapo Benchmark suite", visibleOptions);
    }

    /**
     * Print the release notes to System.out
     */
    static void printReleaseNotes() throws IOException {
        BufferedReader releaseNotes = new BufferedReader(
                new InputStreamReader(CommandLineArgs.class.getClassLoader().getResourceAsStream(RELEASE_NOTES)));

        String line;
        while ((line = releaseNotes.readLine()) != null) {
            System.out.println(line);
        }
    }

    /**
     * List all the benchmarks supported by this release
     */
    static void printBenchmarks() throws IOException {
        Iterator<String> iter = new TreeSet(extractBenchmarkSet()).iterator();

        for (; iter.hasNext();) {
            System.out.print(iter.next());
            if (iter.hasNext()) {
                System.out.print(" ");
            }
        }
        System.out.println();
    }

    static List<String> extractBenchmarkSet() throws IOException {
        List<String> benchmarks = new ArrayList<String>();
        URL url = CommandLineArgs.class.getClassLoader().getResource("cnf");
        String protocol = url.getProtocol();
        if (protocol.equals("jar")) {
            JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
            for (Enumeration<?> entries = jarConnection.getJarFile().entries(); entries.hasMoreElements();) {
                String entry = ((JarEntry) entries.nextElement()).getName();
                if (entry.endsWith(".cnf")) {
                    entry = entry.replace("cnf/", "").replace(".cnf", "");
                    benchmarks.add(entry);
                }
            }
        } else if (protocol.equals("file")) {
            File dir = new File(url.getFile());
            if (dir.isDirectory()) {
                File[] files = dir.listFiles();
                for (int i = 0; i < files.length; i++) {
                    String entry = files[i].toString();
                    entry = entry.substring(entry.lastIndexOf('/') + 1, entry.length());
                    entry = entry.replace(".cnf", "");
                    benchmarks.add(entry);
                }
            }
        }
        return benchmarks;
    }

    public Iterable<String> benchmarks() {
        return benchmarks;
    }

    // Getter methods
    public boolean getVerbose() {
        return line.hasOption(OPT_VERBOSE);
    }

    public Methodology getMethodology() {
        return line.hasOption(OPT_CONVERGE) ? Methodology.CONVERGE : Methodology.ITERATE;
    }

    public double getTargetVar() {
        return Double.parseDouble(line.getOptionValue(OPT_VARIANCE, DEFAULT_VARIANCE)) / 100.0;
    }

    public int getWindow() {
        return Integer.parseInt(line.getOptionValue(OPT_WINDOW, DEFAULT_WINDOW_SIZE));
    }

    public int getMaxIterations() {
        return Integer.parseInt(line.getOptionValue(OPT_MAX_ITERATIONS, DEFAULT_MAX_ITERATIONS));
    }

    public boolean getIgnoreValidation() {
        return line.hasOption(OPT_IGNORE_VALIDATION);
    }

    public int getIterations() {
        return Integer.parseInt(line.getOptionValue(OPT_ITERATIONS, DEFAULT_ITERATIONS));
    }

    public String getSize() {
        return line.getOptionValue(OPT_SIZE, DEFAULT_SIZE);
    }

    public String getScratchDir() {
        return line.getOptionValue(OPT_SCRATCH_DIRECTORY, DEFAULT_SCRATCH_DIRECTORY);
    }

    public Callback getCallback() {
        return callback;
    }

    public String getCnfOverride() {
        return line.getOptionValue(OPT_CONFIG, null);
    }

    public boolean getInformation() {
        return line.hasOption(OPT_INFORMATION);
    }

    public boolean getSilent() {
        return !getVerbose();
    }

    public boolean getDebug() {
        return line.hasOption(OPT_DEBUG);
    }

    public boolean getPreserve() {
        return line.hasOption(OPT_PRESERVE);
    }

    public boolean getValidateOutput() {
        return !line.hasOption(OPT_NO_DIGEST_OUTPUT); // validateOutput;
    }

    public boolean getValidate() {
        return !line.hasOption(OPT_NO_VALIDATION);
    }

    public String getValidationReport() {
        return line.getOptionValue(OPT_VALIDATION_REPORT, null);
    }

    public boolean getPreIterationGC() {
        return !line.hasOption(OPT_NO_PRE_ITERATION_GC);
    }

    public String getThreadCount() {
        return line.getOptionValue(OPT_THREAD_COUNT, DEFAULT_THREAD_COUNT);
    }

    public String getThreadFactor() {
        return line.getOptionValue(OPT_THREAD_FACTOR, DEFAULT_THREAD_FACTOR);
    }

    public List<String> getArgList() {
        return line.getArgList();
    }

    public String[] getArgs() {
        return line.getArgs();
    }

    // *****************************************************************
    private void defineCallback() throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            InstantiationException, IllegalAccessException, InvocationTargetException {
        // define the callback class (or set the default if none specified
        if (line.hasOption(OPT_CALLBACK)) {
            // use a callback
            Class<?> cls = null;
            try {
                cls = Class.forName(line.getOptionValue(OPT_CALLBACK));
            } catch (Exception e) {
                System.err.println(e);
                System.err.println("Could not find callback class " + line.getOptionValue(OPT_CALLBACK));
                System.exit(EXIT_MISSING_CALLBACK);
            }
            if (!(Callback.class.isAssignableFrom(cls))) {
                System.err.println(line.getOptionValue(OPT_CALLBACK) + " is not an instance of Callback");
                System.exit(EXIT_BAD_CALLBACK);
            } else {
                Constructor<?> cons = cls.getConstructor(new Class[] { CommandLineArgs.class });
                this.callback = ((Callback) cons.newInstance(new Object[] { this }));
            }
        }

        // set the default callback class if no callback is defined
        if (getCallback() == null) {
            callback = new Callback(this);
        }
    }

    /*
     * Define a commandline option.
     * 
     * @param shortName An optional short form name for the command line option.
     * 
     * @param longname A commandline option must have a long form name.
     * 
     * @param description All commandline options that are visible options must
     * have a description, commandline options that are for internal development
     * usage must not have a description and must instead be documented in the
     * code.
     * 
     * @param argName A commandline option that requires has an argument must
     * specify an argument name.
     */
    private static Option makeOption(String shortName, String longName, String description, String argName) {
        assert longName != null;

        Option option = new Option(shortName, longName, argName != null, description);

        if (argName != null) {
            option.setValueSeparator('=');
            option.setArgName(argName);
        }

        return option;
    }
}