View Javadoc

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 }