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 * <weight> <name> <class> [<args>] 56 * </pre> 57 * <weight> 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 * <name> 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 * <class> 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 * [<args>] 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