1 package net.sourceforge.pmd.util; 2 3 import java.io.File; 4 import java.io.FileNotFoundException; 5 import java.io.FileReader; 6 import java.io.IOException; 7 import java.text.MessageFormat; 8 import java.util.ArrayList; 9 import java.util.Collection; 10 import java.util.Collections; 11 import java.util.HashMap; 12 import java.util.Iterator; 13 import java.util.List; 14 import java.util.Map; 15 import java.util.Set; 16 import java.util.TreeSet; 17 18 import net.sourceforge.pmd.PMD; 19 import net.sourceforge.pmd.PMDException; 20 import net.sourceforge.pmd.Rule; 21 import net.sourceforge.pmd.RuleContext; 22 import net.sourceforge.pmd.RuleSet; 23 import net.sourceforge.pmd.RuleSetFactory; 24 import net.sourceforge.pmd.RuleSetNotFoundException; 25 import net.sourceforge.pmd.SimpleRuleSetNameMapper; 26 import net.sourceforge.pmd.SourceFileSelector; 27 import net.sourceforge.pmd.SourceType; 28 import net.sourceforge.pmd.TargetJDK1_3; 29 import net.sourceforge.pmd.TargetJDK1_4; 30 import net.sourceforge.pmd.TargetJDK1_5; 31 import net.sourceforge.pmd.TargetJDK1_6; 32 import net.sourceforge.pmd.TargetJDK1_7; 33 import net.sourceforge.pmd.TargetJDKVersion; 34 import net.sourceforge.pmd.ast.JavaParser; 35 import net.sourceforge.pmd.cpd.SourceFileOrDirectoryFilter; 36 37 public class Benchmark { 38 39 private static class Result implements Comparable<Result> { 40 public Rule rule; 41 public long time; 42 43 public int compareTo(Result other) { 44 if (other.time < time) { 45 return -1; 46 } else if (other.time > time) { 47 return 1; 48 } 49 50 return rule.getName().compareTo(other.rule.getName()); 51 } 52 53 public Result(long elapsed, Rule rule) { 54 this.rule = rule; 55 this.time = elapsed; 56 } 57 } 58 59 private static boolean findBooleanSwitch(String[] args, String name) { 60 for (int i = 0; i < args.length; i++) { 61 if (args[i].equals(name)) { 62 return true; 63 } 64 } 65 return false; 66 } 67 68 private static String findOptionalStringValue(String[] args, String name, String defaultValue) { 69 for (int i = 0; i < args.length; i++) { 70 if (args[i].equals(name)) { 71 return args[i + 1]; 72 } 73 } 74 return defaultValue; 75 } 76 77 public static void main(String[] args) throws RuleSetNotFoundException, IOException, PMDException { 78 79 String srcDir = findOptionalStringValue(args, "--source-directory", "/usr/local/java/src/java/lang/"); 80 List<File> files = new FileFinder().findFilesFrom(srcDir, new SourceFileOrDirectoryFilter(new SourceFileSelector()), true); 81 82 SourceType jdk = SourceType.JAVA_14; 83 String targetjdk = findOptionalStringValue(args, "--targetjdk", "1.4"); 84 if (targetjdk.equals("1.3")) { 85 jdk = SourceType.JAVA_13; 86 } else if (targetjdk.equals("1.5")) { 87 jdk = SourceType.JAVA_15; 88 } else if (targetjdk.equals("1.6")) { 89 jdk = SourceType.JAVA_16; 90 } else if (targetjdk.equals("1.7")) { 91 jdk = SourceType.JAVA_17; 92 } 93 boolean debug = findBooleanSwitch(args, "--debug"); 94 boolean parseOnly = findBooleanSwitch(args, "--parse-only"); 95 96 if (debug) System.out.println("Using JDK " + jdk.getId()); 97 if (parseOnly) { 98 parseStress(jdk, files); 99 } else { 100 String ruleset = findOptionalStringValue(args, "--ruleset", ""); 101 if (debug) System.out.println("Checking directory " + srcDir); 102 Set<Result> results = new TreeSet<Result>(); 103 RuleSetFactory factory = new RuleSetFactory(); 104 if (ruleset.length() > 0) { 105 SimpleRuleSetNameMapper mapper = new SimpleRuleSetNameMapper(ruleset); 106 stress(jdk, factory.createSingleRuleSet(mapper.getRuleSets()), files, results, debug); 107 } else { 108 Iterator<RuleSet> i = factory.getRegisteredRuleSets(); 109 while (i.hasNext()) { 110 stress(jdk, i.next(), files, results, debug); 111 } 112 } 113 System.out.println("========================================================="); 114 System.out.println("Rule\t\t\t\t\t\tTime in ms"); 115 System.out.println("========================================================="); 116 for (Result result: results) { 117 StringBuffer out = new StringBuffer(result.rule.getName()); 118 while (out.length() < 48) { 119 out.append(' '); 120 } 121 out.append(result.time); 122 System.out.println(out.toString()); 123 } 124 } 125 126 System.out.println("========================================================="); 127 } 128 129 private static void parseStress(SourceType t, List<File> files) throws FileNotFoundException { 130 long start = System.currentTimeMillis(); 131 for (File file: files) { 132 TargetJDKVersion jdk; 133 if (t.equals(SourceType.JAVA_13)) { 134 jdk = new TargetJDK1_3(); 135 } else if (t.equals(SourceType.JAVA_14)) { 136 jdk = new TargetJDK1_4(); 137 } else if (t.equals(SourceType.JAVA_15)) { 138 jdk = new TargetJDK1_5(); 139 } else if (t.equals(SourceType.JAVA_16)) { 140 jdk = new TargetJDK1_6(); 141 } else { 142 jdk = new TargetJDK1_7(); 143 } 144 JavaParser parser = jdk.createParser(new FileReader(file)); 145 parser.CompilationUnit(); 146 } 147 long end = System.currentTimeMillis(); 148 long elapsed = end - start; 149 System.out.println("That took " + elapsed + " ms"); 150 } 151 152 private static void stress(SourceType t, RuleSet ruleSet, List<File> files, Set<Result> results, boolean debug) throws PMDException, IOException { 153 Collection<Rule> rules = ruleSet.getRules(); 154 for (Rule rule: rules) { 155 if (debug) System.out.println("Starting " + rule.getName()); 156 157 RuleSet working = new RuleSet(); 158 working.addRule(rule); 159 160 PMD p = new PMD(); 161 p.setJavaVersion(t); 162 RuleContext ctx = new RuleContext(); 163 long start = System.currentTimeMillis(); 164 for (File file: files) { 165 FileReader reader = new FileReader(file); 166 ctx.setSourceCodeFilename(file.getName()); 167 p.processFile(reader, working, ctx); 168 reader.close(); 169 } 170 long end = System.currentTimeMillis(); 171 long elapsed = end - start; 172 results.add(new Result(elapsed, rule)); 173 if (debug) System.out.println("Done timing " + rule.getName() + "; elapsed time was " + elapsed); 174 } 175 } 176 177 private static final Map<String, BenchmarkResult> nameToBenchmarkResult = new HashMap<String, BenchmarkResult>(); 178 179 public static final int TYPE_RULE = 0; 180 public static final int TYPE_RULE_CHAIN_RULE = 1; 181 public static final int TYPE_COLLECT_FILES = 2; 182 public static final int TYPE_LOAD_RULES = 3; 183 public static final int TYPE_PARSER = 4; 184 public static final int TYPE_SYMBOL_TABLE = 5; 185 public static final int TYPE_DFA = 6; 186 public static final int TYPE_TYPE_RESOLUTION = 7; 187 public static final int TYPE_RULE_CHAIN_VISIT = 8; 188 public static final int TYPE_REPORTING = 9; 189 private static final int TYPE_RULE_TOTAL = 10; 190 private static final int TYPE_RULE_CHAIN_RULE_TOTAL = 11; 191 private static final int TYPE_MEASURED_TOTAL = 12; 192 private static final int TYPE_NON_MEASURED_TOTAL = 13; 193 public static final int TYPE_TOTAL_PMD = 14; 194 195 private static final String[] TYPE_NAMES = { 196 null, 197 null, 198 "Collect Files", 199 "Load Rules", 200 "Parser", 201 "Symbol Table", 202 "Data Flow Analysis", 203 "Type Resolution", 204 "RuleChain Visit", 205 "Reporting", 206 "Rule Total", 207 "RuleChain Rule Total", 208 "Measured", 209 "Non-measured", 210 "Total PMD", 211 }; 212 213 private static final class BenchmarkResult implements Comparable<BenchmarkResult> { 214 private final int type; 215 private final String name; 216 private long time; 217 private long count; 218 public BenchmarkResult(int type, String name) { 219 this.type = type; 220 this.name = name; 221 } 222 public BenchmarkResult(int type, String name, long time, long count) { 223 this.type = type; 224 this.name = name; 225 this.time = time; 226 this.count = count; 227 } 228 public int getType() { 229 return type; 230 } 231 public String getName() { 232 return name; 233 } 234 public long getTime() { 235 return time; 236 } 237 public long getCount() { 238 return count; 239 } 240 public void update(long time, long count) { 241 this.time += time; 242 this.count += count; 243 } 244 public int compareTo(BenchmarkResult benchmarkResult) { 245 int cmp = this.type - benchmarkResult.type; 246 if (cmp == 0) { 247 long delta = this.time - benchmarkResult.time; 248 cmp = delta > 0 ? 1 : (delta < 0 ? -1 : 0); 249 } 250 return cmp; 251 } 252 } 253 254 public static void mark(int type, long time, long count) { 255 mark(type, null, time, count); 256 } 257 258 public synchronized static void mark(int type, String name, long time, long count) { 259 String typeName = TYPE_NAMES[type]; 260 if (typeName != null && name != null) { 261 throw new IllegalArgumentException("Name cannot be given for type: " + type); 262 } else if (typeName == null && name == null) { 263 throw new IllegalArgumentException("Name is required for type: " + type); 264 } else if (typeName == null) { 265 typeName = name; 266 } 267 BenchmarkResult benchmarkResult = nameToBenchmarkResult.get(typeName); 268 if (benchmarkResult == null) { 269 benchmarkResult = new BenchmarkResult(type, typeName); 270 nameToBenchmarkResult.put(typeName, benchmarkResult); 271 } 272 benchmarkResult.update(time, count); 273 } 274 275 public static void reset() { 276 nameToBenchmarkResult.clear(); 277 } 278 279 public static String report() { 280 List<BenchmarkResult> results = new ArrayList<BenchmarkResult>(nameToBenchmarkResult.values()); 281 282 long totalTime[] = new long[TYPE_TOTAL_PMD + 1]; 283 long totalCount[] = new long[TYPE_TOTAL_PMD + 1]; 284 for (BenchmarkResult benchmarkResult: results) { 285 totalTime[benchmarkResult.getType()] += benchmarkResult.getTime(); 286 totalCount[benchmarkResult.getType()] += benchmarkResult.getCount(); 287 if (benchmarkResult.getType() < TYPE_MEASURED_TOTAL) { 288 totalTime[TYPE_MEASURED_TOTAL] += benchmarkResult.getTime(); 289 } 290 } 291 results.add(new BenchmarkResult(TYPE_RULE_TOTAL, TYPE_NAMES[TYPE_RULE_TOTAL], totalTime[TYPE_RULE], 0)); 292 results.add(new BenchmarkResult(TYPE_RULE_CHAIN_RULE_TOTAL, TYPE_NAMES[TYPE_RULE_CHAIN_RULE_TOTAL], totalTime[TYPE_RULE_CHAIN_RULE], 0)); 293 results.add(new BenchmarkResult(TYPE_MEASURED_TOTAL, TYPE_NAMES[TYPE_MEASURED_TOTAL], totalTime[TYPE_MEASURED_TOTAL], 0)); 294 results.add(new BenchmarkResult(TYPE_NON_MEASURED_TOTAL, TYPE_NAMES[TYPE_NON_MEASURED_TOTAL], totalTime[TYPE_TOTAL_PMD] - totalTime[TYPE_MEASURED_TOTAL], 0)); 295 Collections.sort(results); 296 297 StringBuffer buf = new StringBuffer(); 298 boolean writeRuleHeader = true; 299 boolean writeRuleChainRuleHeader = true; 300 for (BenchmarkResult benchmarkResult: results) { 301 StringBuffer buf2 = new StringBuffer(); 302 buf2.append(benchmarkResult.getName()); 303 buf2.append(':'); 304 while (buf2.length() <= 50) { 305 buf2.append(' '); 306 } 307 buf2.append(StringUtil.lpad(MessageFormat.format("{0,number,0.000}", new Double(benchmarkResult.getTime()/1000000000.0)), 8)); 308 if (benchmarkResult.getType() <= TYPE_RULE_CHAIN_RULE) { 309 buf2.append(StringUtil.lpad(MessageFormat.format("{0,number,###,###,###,###,###}", benchmarkResult.getCount()), 20)); 310 } 311 switch (benchmarkResult.getType()) { 312 case TYPE_RULE: 313 if (writeRuleHeader) { 314 writeRuleHeader = false; 315 buf.append(PMD.EOL); 316 buf.append("---------------------------------<<< Rules >>>---------------------------------" + PMD.EOL); 317 buf.append("Rule name Time (secs) # of Evaluations" + PMD.EOL); 318 buf.append(PMD.EOL); 319 } 320 break; 321 case TYPE_RULE_CHAIN_RULE: 322 if (writeRuleChainRuleHeader) { 323 writeRuleChainRuleHeader = false; 324 buf.append(PMD.EOL); 325 buf.append("----------------------------<<< RuleChain Rules >>>----------------------------" + PMD.EOL); 326 buf.append("Rule name Time (secs) # of Visits" + PMD.EOL); 327 buf.append(PMD.EOL); 328 } 329 break; 330 case TYPE_COLLECT_FILES: 331 buf.append(PMD.EOL); 332 buf.append("--------------------------------<<< Summary >>>--------------------------------" + PMD.EOL); 333 buf.append("Segment Time (secs)" + PMD.EOL); 334 buf.append(PMD.EOL); 335 break; 336 case TYPE_MEASURED_TOTAL: 337 buf.append(PMD.EOL); 338 buf.append("-----------------------------<<< Final Summary >>>-----------------------------" + PMD.EOL); 339 buf.append("Total Time (secs)" + PMD.EOL); 340 buf.append(PMD.EOL); 341 break; 342 } 343 buf.append(buf2.toString()); 344 buf.append(PMD.EOL); 345 } 346 return buf.toString(); 347 } 348 }