1/*
2 * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 *
26 */
27
28package bench;
29
30import java.io.InputStream;
31import java.io.InputStreamReader;
32import java.io.StreamTokenizer;
33import java.io.IOException;
34import java.util.Vector;
35
36
37/**
38 * Benchmark harness.  Responsible for parsing config file and running
39 * benchmarks.
40 */
41public class Harness {
42
43    BenchInfo[] binfo;
44
45    /**
46     * Create new benchmark harness with given configuration and reporter.
47     * Throws ConfigFormatException if there was an error parsing the config
48     * file.
49     * <p>
50     * <b>Config file syntax:</b>
51     * <p>
52     * '#' marks the beginning of a comment.  Blank lines are ignored.  All
53     * other lines should adhere to the following format:
54     * <pre>
55     *     &lt;weight&gt; &lt;name&gt; &lt;class&gt; [&lt;args&gt;]
56     * </pre>
57     * &lt;weight&gt; is a floating point value which is multiplied times the
58     * benchmark's execution time to determine its weighted score.  The
59     * total score of the benchmark suite is the sum of all weighted scores
60     * of its benchmarks.
61     * <p>
62     * &lt;name&gt; is a name used to identify the benchmark on the benchmark
63     * report.  If the name contains whitespace, the quote character '"' should
64     * be used as a delimiter.
65     * <p>
66     * &lt;class&gt; is the full name (including the package) of the class
67     * containing the benchmark implementation.  This class must implement
68     * bench.Benchmark.
69     * <p>
70     * [&lt;args&gt;] is a variable-length list of runtime arguments to pass to
71     * the benchmark.  Arguments containing whitespace should use the quote
72     * character '"' as a delimiter.
73     * <p>
74     * <b>Example:</b>
75     * <pre>
76     *      3.5 "My benchmark" bench.serial.Test first second "third arg"
77     * </pre>
78     */
79    public Harness(InputStream in) throws IOException, ConfigFormatException {
80        Vector bvec = new Vector();
81        StreamTokenizer tokens = new StreamTokenizer(new InputStreamReader(in));
82
83        tokens.resetSyntax();
84        tokens.wordChars(0, 255);
85        tokens.whitespaceChars(0, ' ');
86        tokens.commentChar('#');
87        tokens.quoteChar('"');
88        tokens.eolIsSignificant(true);
89
90        tokens.nextToken();
91        while (tokens.ttype != StreamTokenizer.TT_EOF) {
92            switch (tokens.ttype) {
93                case StreamTokenizer.TT_WORD:
94                case '"':                       // parse line
95                    bvec.add(parseBenchInfo(tokens));
96                    break;
97
98                default:                        // ignore
99                    tokens.nextToken();
100                    break;
101            }
102        }
103        binfo = (BenchInfo[]) bvec.toArray(new BenchInfo[bvec.size()]);
104    }
105
106    BenchInfo parseBenchInfo(StreamTokenizer tokens)
107        throws IOException, ConfigFormatException
108    {
109        float weight = parseBenchWeight(tokens);
110        String name = parseBenchName(tokens);
111        Benchmark bench = parseBenchClass(tokens);
112        String[] args = parseBenchArgs(tokens);
113        if (tokens.ttype == StreamTokenizer.TT_EOL)
114            tokens.nextToken();
115        return new BenchInfo(bench, name, weight, args);
116    }
117
118    float parseBenchWeight(StreamTokenizer tokens)
119        throws IOException, ConfigFormatException
120    {
121        float weight;
122        switch (tokens.ttype) {
123            case StreamTokenizer.TT_WORD:
124            case '"':
125                try {
126                    weight = Float.parseFloat(tokens.sval);
127                } catch (NumberFormatException e) {
128                    throw new ConfigFormatException("illegal weight value \"" +
129                            tokens.sval + "\" on line " + tokens.lineno());
130                }
131                tokens.nextToken();
132                return weight;
133
134            default:
135                throw new ConfigFormatException("missing weight value on line "
136                        + tokens.lineno());
137        }
138    }
139
140    String parseBenchName(StreamTokenizer tokens)
141        throws IOException, ConfigFormatException
142    {
143        String name;
144        switch (tokens.ttype) {
145            case StreamTokenizer.TT_WORD:
146            case '"':
147                name = tokens.sval;
148                tokens.nextToken();
149                return name;
150
151            default:
152                throw new ConfigFormatException("missing benchmark name on " +
153                        "line " + tokens.lineno());
154        }
155    }
156
157    Benchmark parseBenchClass(StreamTokenizer tokens)
158        throws IOException, ConfigFormatException
159    {
160        Benchmark bench;
161        switch (tokens.ttype) {
162            case StreamTokenizer.TT_WORD:
163            case '"':
164                try {
165                    Class cls = Class.forName(tokens.sval);
166                    bench = (Benchmark) cls.newInstance();
167                } catch (Exception e) {
168                    throw new ConfigFormatException("unable to instantiate " +
169                            "benchmark \"" + tokens.sval + "\" on line " +
170                            tokens.lineno());
171                }
172                tokens.nextToken();
173                return bench;
174
175            default:
176                throw new ConfigFormatException("missing benchmark class " +
177                        "name on line " + tokens.lineno());
178        }
179    }
180
181    String[] parseBenchArgs(StreamTokenizer tokens)
182        throws IOException, ConfigFormatException
183    {
184        Vector vec = new Vector();
185        for (;;) {
186            switch (tokens.ttype) {
187                case StreamTokenizer.TT_EOF:
188                case StreamTokenizer.TT_EOL:
189                    return (String[]) vec.toArray(new String[vec.size()]);
190
191                case StreamTokenizer.TT_WORD:
192                case '"':
193                    vec.add(tokens.sval);
194                    tokens.nextToken();
195                    break;
196
197                default:
198                    throw new ConfigFormatException("unrecognized arg token " +
199                            "on line " + tokens.lineno());
200            }
201        }
202    }
203
204    /**
205     * Run benchmarks, writing results to the given reporter.
206     */
207    public void runBenchmarks(Reporter reporter, boolean verbose) {
208        for (int i = 0; i < binfo.length; i++) {
209            if (verbose)
210                System.out.println("Running benchmark " + i + " (" +
211                        binfo[i].getName() + ")");
212            try {
213                binfo[i].runBenchmark();
214            } catch (Exception e) {
215                System.err.println("Error: benchmark " + i + " failed: " + e);
216                e.printStackTrace();
217            }
218            cleanup();
219        }
220        try {
221            reporter.writeReport(binfo, System.getProperties());
222        } catch (IOException e) {
223            System.err.println("Error: failed to write benchmark report");
224        }
225    }
226
227    /**
228     * Clean up method that is invoked after the completion of each benchmark.
229     * The default implementation calls System.gc(); subclasses may override
230     * this to perform additional cleanup measures.
231     */
232    protected void cleanup() {
233        System.gc();
234    }
235}
236