001 package net.sourceforge.pmd.util;
002
003 import java.io.IOException;
004 import java.io.InputStreamReader;
005 import java.io.Reader;
006 import java.text.MessageFormat;
007 import java.util.ArrayList;
008 import java.util.Collection;
009 import java.util.Collections;
010 import java.util.HashMap;
011 import java.util.Iterator;
012 import java.util.List;
013 import java.util.Map;
014 import java.util.Set;
015 import java.util.TreeSet;
016
017 import net.sourceforge.pmd.PMD;
018 import net.sourceforge.pmd.PMDException;
019 import net.sourceforge.pmd.Rule;
020 import net.sourceforge.pmd.RuleContext;
021 import net.sourceforge.pmd.RuleSet;
022 import net.sourceforge.pmd.RuleSetFactory;
023 import net.sourceforge.pmd.RuleSetNotFoundException;
024 import net.sourceforge.pmd.RuleSets;
025 import net.sourceforge.pmd.lang.Language;
026 import net.sourceforge.pmd.lang.LanguageFilenameFilter;
027 import net.sourceforge.pmd.lang.LanguageVersion;
028 import net.sourceforge.pmd.lang.LanguageVersionHandler;
029 import net.sourceforge.pmd.util.datasource.DataSource;
030
031 public class Benchmark {
032
033 private static class Result implements Comparable<Result> {
034 public Rule rule;
035 public long time;
036
037 public int compareTo(Result other) {
038 if (other.time < time) {
039 return -1;
040 } else if (other.time > time) {
041 return 1;
042 }
043
044 return rule.getName().compareTo(other.rule.getName());
045 }
046
047 public Result(long elapsed, Rule rule) {
048 this.rule = rule;
049 this.time = elapsed;
050 }
051 }
052
053 private static boolean findBooleanSwitch(String[] args, String name) {
054 for (int i = 0; i < args.length; i++) {
055 if (args[i].equals(name)) {
056 return true;
057 }
058 }
059 return false;
060 }
061
062 private static String findOptionalStringValue(String[] args, String name, String defaultValue) {
063 for (int i = 0; i < args.length; i++) {
064 if (args[i].equals(name)) {
065 return args[i + 1];
066 }
067 }
068 return defaultValue;
069 }
070
071 public static void main(String[] args) throws RuleSetNotFoundException, IOException, PMDException {
072
073 String targetjdk = findOptionalStringValue(args, "--targetjdk", "1.4");
074 Language language = Language.JAVA;
075 LanguageVersion languageVersion = language.getVersion(targetjdk);
076 if (languageVersion == null) {
077 languageVersion = language.getDefaultVersion();
078 }
079
080 String srcDir = findOptionalStringValue(args, "--source-directory", "/usr/local/java/src/java/lang/");
081 List<DataSource> dataSources = FileUtil.collectFiles(srcDir, new LanguageFilenameFilter(language));
082
083 boolean debug = findBooleanSwitch(args, "--debug");
084 boolean parseOnly = findBooleanSwitch(args, "--parse-only");
085
086 if (debug) {
087 System.out.println("Using " +language.getName() + " " + languageVersion.getVersion());
088 }
089 if (parseOnly) {
090 parseStress(languageVersion, dataSources);
091 } else {
092 String ruleset = findOptionalStringValue(args, "--ruleset", "");
093 if (debug) {
094 System.out.println("Checking directory " + srcDir);
095 }
096 Set<Result> results = new TreeSet<Result>();
097 RuleSetFactory factory = new RuleSetFactory();
098 if (ruleset.length() > 0) {
099 stress(languageVersion, factory.createRuleSet(ruleset), dataSources, results, debug);
100 } else {
101 Iterator<RuleSet> i = factory.getRegisteredRuleSets();
102 while (i.hasNext()) {
103 stress(languageVersion, i.next(), dataSources, results, debug);
104 }
105 }
106 System.out.println("=========================================================");
107 System.out.println("Rule\t\t\t\t\t\tTime in ms");
108 System.out.println("=========================================================");
109 for (Result result: results) {
110 StringBuffer out = new StringBuffer(result.rule.getName());
111 while (out.length() < 48) {
112 out.append(' ');
113 }
114 out.append(result.time);
115 System.out.println(out.toString());
116 }
117 }
118
119 System.out.println("=========================================================");
120 }
121
122 private static void parseStress(LanguageVersion languageVersion, List<DataSource> dataSources) throws IOException {
123 long start = System.currentTimeMillis();
124 for (DataSource dataSource: dataSources) {
125 LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler();
126 languageVersionHandler.getParser().parse(dataSource.getNiceFileName(false, null), new InputStreamReader(dataSource.getInputStream()));
127 }
128 long end = System.currentTimeMillis();
129 long elapsed = end - start;
130 System.out.println("That took " + elapsed + " ms");
131 }
132
133 private static void stress(LanguageVersion languageVersion, RuleSet ruleSet, List<DataSource> dataSources, Set<Result> results, boolean debug) throws PMDException, IOException {
134 Collection<Rule> rules = ruleSet.getRules();
135 for (Rule rule: rules) {
136 if (debug) {
137 System.out.println("Starting " + rule.getName());
138 }
139
140 RuleSet working = new RuleSet();
141 working.addRule(rule);
142 RuleSets ruleSets = new RuleSets();
143 ruleSets.addRuleSet(working);
144
145 PMD p = new PMD();
146 p.getConfiguration().setDefaultLanguageVersion(languageVersion);
147 RuleContext ctx = new RuleContext();
148 long start = System.currentTimeMillis();
149 for (DataSource dataSource: dataSources) {
150 Reader reader = new InputStreamReader(dataSource.getInputStream());
151 ctx.setSourceCodeFilename(dataSource.getNiceFileName(false, null));
152 p.processFile(reader, ruleSets, ctx);
153 reader.close();
154 }
155 long end = System.currentTimeMillis();
156 long elapsed = end - start;
157 results.add(new Result(elapsed, rule));
158 if (debug) {
159 System.out.println("Done timing " + rule.getName() + "; elapsed time was " + elapsed);
160 }
161 }
162 }
163
164 private static final Map<String, BenchmarkResult> NAME_TO_BENCHMARK_RESULT = new HashMap<String, BenchmarkResult>();
165
166 public static final int TYPE_RULE = 0;
167 public static final int TYPE_RULE_CHAIN_RULE = 1;
168 public static final int TYPE_COLLECT_FILES = 2;
169 public static final int TYPE_LOAD_RULES = 3;
170 public static final int TYPE_PARSER = 4;
171 public static final int TYPE_SYMBOL_TABLE = 5;
172 public static final int TYPE_DFA = 6;
173 public static final int TYPE_TYPE_RESOLUTION = 7;
174 public static final int TYPE_RULE_CHAIN_VISIT = 8;
175 public static final int TYPE_REPORTING = 9;
176 private static final int TYPE_RULE_TOTAL = 10;
177 private static final int TYPE_RULE_CHAIN_RULE_TOTAL = 11;
178 private static final int TYPE_MEASURED_TOTAL = 12;
179 private static final int TYPE_NON_MEASURED_TOTAL = 13;
180 public static final int TYPE_TOTAL_PMD = 14;
181
182 private static final String[] TYPE_NAMES = {
183 null,
184 null,
185 "Collect Files",
186 "Load Rules",
187 "Parser",
188 "Symbol Table",
189 "Data Flow Analysis",
190 "Type Resolution",
191 "RuleChain Visit",
192 "Reporting",
193 "Rule Total",
194 "RuleChain Rule Total",
195 "Measured",
196 "Non-measured",
197 "Total PMD",
198 };
199
200 private static final class BenchmarkResult implements Comparable<BenchmarkResult> {
201 private final int type;
202 private final String name;
203 private long time;
204 private long count;
205 public BenchmarkResult(int type, String name) {
206 this.type = type;
207 this.name = name;
208 }
209 public BenchmarkResult(int type, String name, long time, long count) {
210 this.type = type;
211 this.name = name;
212 this.time = time;
213 this.count = count;
214 }
215 public int getType() {
216 return type;
217 }
218 public String getName() {
219 return name;
220 }
221 public long getTime() {
222 return time;
223 }
224 public long getCount() {
225 return count;
226 }
227 public void update(long time, long count) {
228 this.time += time;
229 this.count += count;
230 }
231 public int compareTo(BenchmarkResult benchmarkResult) {
232 int cmp = this.type - benchmarkResult.type;
233 if (cmp == 0) {
234 long delta = this.time - benchmarkResult.time;
235 cmp = delta > 0 ? 1 : (delta < 0 ? -1 : 0);
236 }
237 return cmp;
238 }
239 }
240
241 public static void mark(int type, long time, long count) {
242 mark(type, null, time, count);
243 }
244
245 public synchronized static void mark(int type, String name, long time, long count) {
246 String typeName = TYPE_NAMES[type];
247 if (typeName != null && name != null) {
248 throw new IllegalArgumentException("Name cannot be given for type: " + type);
249 } else if (typeName == null && name == null) {
250 throw new IllegalArgumentException("Name is required for type: " + type);
251 } else if (typeName == null) {
252 typeName = name;
253 }
254 BenchmarkResult benchmarkResult = NAME_TO_BENCHMARK_RESULT.get(typeName);
255 if (benchmarkResult == null) {
256 benchmarkResult = new BenchmarkResult(type, typeName);
257 NAME_TO_BENCHMARK_RESULT.put(typeName, benchmarkResult);
258 }
259 benchmarkResult.update(time, count);
260 }
261
262 public static void reset() {
263 NAME_TO_BENCHMARK_RESULT.clear();
264 }
265
266 public static String report() {
267 List<BenchmarkResult> results = new ArrayList<BenchmarkResult>(NAME_TO_BENCHMARK_RESULT.values());
268
269 long totalTime[] = new long[TYPE_TOTAL_PMD + 1];
270 long totalCount[] = new long[TYPE_TOTAL_PMD + 1];
271 for (BenchmarkResult benchmarkResult: results) {
272 totalTime[benchmarkResult.getType()] += benchmarkResult.getTime();
273 totalCount[benchmarkResult.getType()] += benchmarkResult.getCount();
274 if (benchmarkResult.getType() < TYPE_MEASURED_TOTAL) {
275 totalTime[TYPE_MEASURED_TOTAL] += benchmarkResult.getTime();
276 }
277 }
278 results.add(new BenchmarkResult(TYPE_RULE_TOTAL, TYPE_NAMES[TYPE_RULE_TOTAL], totalTime[TYPE_RULE], 0));
279 results.add(new BenchmarkResult(TYPE_RULE_CHAIN_RULE_TOTAL, TYPE_NAMES[TYPE_RULE_CHAIN_RULE_TOTAL], totalTime[TYPE_RULE_CHAIN_RULE], 0));
280 results.add(new BenchmarkResult(TYPE_MEASURED_TOTAL, TYPE_NAMES[TYPE_MEASURED_TOTAL], totalTime[TYPE_MEASURED_TOTAL], 0));
281 results.add(new BenchmarkResult(TYPE_NON_MEASURED_TOTAL, TYPE_NAMES[TYPE_NON_MEASURED_TOTAL], totalTime[TYPE_TOTAL_PMD] - totalTime[TYPE_MEASURED_TOTAL], 0));
282 Collections.sort(results);
283
284 StringBuffer buf = new StringBuffer();
285 boolean writeRuleHeader = true;
286 boolean writeRuleChainRuleHeader = true;
287 long ruleCount = 0;
288 long ruleChainCount = 0;
289 for (BenchmarkResult benchmarkResult: results) {
290 StringBuffer buf2 = new StringBuffer();
291 buf2.append(benchmarkResult.getName());
292 buf2.append(':');
293 while (buf2.length() <= 50) {
294 buf2.append(' ');
295 }
296 buf2.append(StringUtil.lpad(MessageFormat.format("{0,number,0.000}", Double.valueOf(benchmarkResult.getTime()/1000000000.0)), 8));
297 if (benchmarkResult.getType() <= TYPE_RULE_CHAIN_RULE) {
298 buf2.append(StringUtil.lpad(MessageFormat.format("{0,number,###,###,###,###,###}", benchmarkResult.getCount()), 20));
299 }
300 switch (benchmarkResult.getType()) {
301 case TYPE_RULE:
302 if (writeRuleHeader) {
303 writeRuleHeader = false;
304 buf.append(PMD.EOL);
305 buf.append("---------------------------------<<< Rules >>>---------------------------------" + PMD.EOL);
306 buf.append("Rule name Time (secs) # of Evaluations" + PMD.EOL);
307 buf.append(PMD.EOL);
308 }
309 ruleCount++;
310 break;
311 case TYPE_RULE_CHAIN_RULE:
312 if (writeRuleChainRuleHeader) {
313 writeRuleChainRuleHeader = false;
314 buf.append(PMD.EOL);
315 buf.append("----------------------------<<< RuleChain Rules >>>----------------------------" + PMD.EOL);
316 buf.append("Rule name Time (secs) # of Visits" + PMD.EOL);
317 buf.append(PMD.EOL);
318 }
319 ruleChainCount++;
320 break;
321 case TYPE_COLLECT_FILES:
322 buf.append(PMD.EOL);
323 buf.append("--------------------------------<<< Summary >>>--------------------------------" + PMD.EOL);
324 buf.append("Segment Time (secs)" + PMD.EOL);
325 buf.append(PMD.EOL);
326 break;
327 case TYPE_MEASURED_TOTAL:
328 String s = MessageFormat.format("{0,number,###,###,###,###,###}", ruleCount);
329 buf.append("Rule Average (" + s + " rules):" + StringUtil.lpad(MessageFormat.format("{0,number,0.000}", ruleCount==0?0:totalTime[TYPE_RULE]/1000000000.0d/ruleCount), 37-s.length()) + PMD.EOL);
330 s = MessageFormat.format("{0,number,###,###,###,###,###}", ruleChainCount);
331 buf.append("RuleChain Average (" + s + " rules):" + StringUtil.lpad(MessageFormat.format("{0,number,0.000}", ruleChainCount==0?0:totalTime[TYPE_RULE_CHAIN_RULE]/1000000000.0d/ruleChainCount), 32-s.length()) + PMD.EOL);
332
333 buf.append(PMD.EOL);
334 buf.append("-----------------------------<<< Final Summary >>>-----------------------------" + PMD.EOL);
335 buf.append("Total Time (secs)" + PMD.EOL);
336 buf.append(PMD.EOL);
337 break;
338 default:
339 // Do nothing
340 break;
341 }
342 buf.append(buf2.toString());
343 buf.append(PMD.EOL);
344 }
345 return buf.toString();
346 }
347 }
|