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