00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 package org.antlr.runtime.debug;
00029
00030 import org.antlr.runtime.*;
00031 import org.antlr.runtime.misc.DoubleKeyMap;
00032
00033 import java.util.*;
00034
00038 public class Profiler extends BlankDebugEventListener {
00039 public static final String DATA_SEP = "\t";
00040 public static final String newline = System.getProperty("line.separator");
00041
00042 static boolean dump = false;
00043
00044 public static class ProfileStats {
00045 public String Version;
00046 public String name;
00047 public int numRuleInvocations;
00048 public int numUniqueRulesInvoked;
00049 public int numDecisionEvents;
00050 public int numDecisionsCovered;
00051 public int numDecisionsThatPotentiallyBacktrack;
00052 public int numDecisionsThatDoBacktrack;
00053 public int maxRuleInvocationDepth;
00054 public float avgkPerDecisionEvent;
00055 public float avgkPerBacktrackingDecisionEvent;
00056 public float averageDecisionPercentBacktracks;
00057 public int numBacktrackOccurrences;
00058
00059 public int numFixedDecisions;
00060 public int minDecisionMaxFixedLookaheads;
00061 public int maxDecisionMaxFixedLookaheads;
00062 public int avgDecisionMaxFixedLookaheads;
00063 public int stddevDecisionMaxFixedLookaheads;
00064 public int numCyclicDecisions;
00065 public int minDecisionMaxCyclicLookaheads;
00066 public int maxDecisionMaxCyclicLookaheads;
00067 public int avgDecisionMaxCyclicLookaheads;
00068 public int stddevDecisionMaxCyclicLookaheads;
00069
00070
00071
00072
00073 public int numSemanticPredicates;
00074 public int numTokens;
00075 public int numHiddenTokens;
00076 public int numCharsMatched;
00077 public int numHiddenCharsMatched;
00078 public int numReportedErrors;
00079 public int numMemoizationCacheHits;
00080 public int numMemoizationCacheMisses;
00081 public int numGuessingRuleInvocations;
00082 public int numMemoizationCacheEntries;
00083 }
00084
00085 public static class DecisionDescriptor {
00086 public int decision;
00087 public String fileName;
00088 public String ruleName;
00089 public int line;
00090 public int pos;
00091 public boolean couldBacktrack;
00092
00093 public int n;
00094 public float avgk;
00095 public int maxk;
00096 public int numBacktrackOccurrences;
00097 public int numSemPredEvals;
00098 }
00099
00100
00101 public static class DecisionEvent {
00102 public DecisionDescriptor decision;
00103 public int startIndex;
00104 public int k;
00105 public boolean backtracks;
00106 public boolean evalSemPred;
00107 public long startTime;
00108 public long stopTime;
00109 public int numMemoizationCacheHits;
00110 public int numMemoizationCacheMisses;
00111 }
00112
00116 public static final String Version = "3";
00117 public static final String RUNTIME_STATS_FILENAME = "runtime.stats";
00118
00122 public DebugParser parser = null;
00123
00124
00125
00126 protected int ruleLevel = 0;
00127
00128 protected Token lastRealTokenTouchedInDecision;
00129 protected Set<String> uniqueRules = new HashSet<String>();
00130 protected Stack<String> currentGrammarFileName = new Stack();
00131 protected Stack<String> currentRuleName = new Stack();
00132 protected Stack<Integer> currentLine = new Stack();
00133 protected Stack<Integer> currentPos = new Stack();
00134
00135
00136
00137 protected DoubleKeyMap<String,Integer, DecisionDescriptor> decisions =
00138 new DoubleKeyMap<String,Integer, DecisionDescriptor>();
00139
00140
00141 protected List<DecisionEvent> decisionEvents = new ArrayList<DecisionEvent>();
00142 protected Stack<DecisionEvent> decisionStack = new Stack<DecisionEvent>();
00143
00144 protected int backtrackDepth;
00145
00146 ProfileStats stats = new ProfileStats();
00147
00148 public Profiler() {
00149 }
00150
00151 public Profiler(DebugParser parser) {
00152 this.parser = parser;
00153 }
00154
00155 public void enterRule(String grammarFileName, String ruleName) {
00156
00157 ruleLevel++;
00158 stats.numRuleInvocations++;
00159 uniqueRules.add(grammarFileName+":"+ruleName);
00160 stats.maxRuleInvocationDepth = Math.max(stats.maxRuleInvocationDepth, ruleLevel);
00161 currentGrammarFileName.push( grammarFileName );
00162 currentRuleName.push( ruleName );
00163 }
00164
00165 public void exitRule(String grammarFileName, String ruleName) {
00166 ruleLevel--;
00167 currentGrammarFileName.pop();
00168 currentRuleName.pop();
00169 }
00170
00176 public void examineRuleMemoization(IntStream input,
00177 int ruleIndex,
00178 int stopIndex,
00179 String ruleName)
00180 {
00181 if (dump) System.out.println("examine memo "+ruleName+" at "+input.index()+": "+stopIndex);
00182 if ( stopIndex==BaseRecognizer.MEMO_RULE_UNKNOWN ) {
00183
00184 stats.numMemoizationCacheMisses++;
00185 stats.numGuessingRuleInvocations++;
00186 currentDecision().numMemoizationCacheMisses++;
00187 }
00188 else {
00189
00190
00191 stats.numMemoizationCacheHits++;
00192 currentDecision().numMemoizationCacheHits++;
00193 }
00194 }
00195
00197 public void memoize(IntStream input,
00198 int ruleIndex,
00199 int ruleStartIndex,
00200 String ruleName)
00201 {
00202
00203 if (dump) System.out.println("memoize "+ruleName);
00204 stats.numMemoizationCacheEntries++;
00205 }
00206
00207 @Override
00208 public void location(int line, int pos) {
00209 currentLine.push(line);
00210 currentPos.push(pos);
00211 }
00212
00213 public void enterDecision(int decisionNumber, boolean couldBacktrack) {
00214 lastRealTokenTouchedInDecision = null;
00215 stats.numDecisionEvents++;
00216 int startingLookaheadIndex = parser.getTokenStream().index();
00217 TokenStream input = parser.getTokenStream();
00218 if ( dump ) System.out.println("enterDecision canBacktrack="+couldBacktrack+" "+ decisionNumber +
00219 " backtrack depth " + backtrackDepth +
00220 " @ " + input.get(input.index()) +
00221 " rule " +locationDescription());
00222 String g = (String) currentGrammarFileName.peek();
00223 DecisionDescriptor descriptor = decisions.get(g, decisionNumber);
00224 if ( descriptor == null ) {
00225 descriptor = new DecisionDescriptor();
00226 decisions.put(g, decisionNumber, descriptor);
00227 descriptor.decision = decisionNumber;
00228 descriptor.fileName = (String)currentGrammarFileName.peek();
00229 descriptor.ruleName = (String)currentRuleName.peek();
00230 descriptor.line = (Integer)currentLine.peek();
00231 descriptor.pos = (Integer)currentPos.peek();
00232 descriptor.couldBacktrack = couldBacktrack;
00233 }
00234 descriptor.n++;
00235
00236 DecisionEvent d = new DecisionEvent();
00237 decisionStack.push(d);
00238 d.decision = descriptor;
00239 d.startTime = System.currentTimeMillis();
00240 d.startIndex = startingLookaheadIndex;
00241 }
00242
00243 public void exitDecision(int decisionNumber) {
00244 DecisionEvent d = decisionStack.pop();
00245 d.stopTime = System.currentTimeMillis();
00246
00247 int lastTokenIndex = lastRealTokenTouchedInDecision.getTokenIndex();
00248 int numHidden = getNumberOfHiddenTokens(d.startIndex, lastTokenIndex);
00249 int depth = lastTokenIndex - d.startIndex - numHidden + 1;
00250 d.k = depth;
00251 d.decision.maxk = Math.max(d.decision.maxk, depth);
00252
00253 if (dump) System.out.println("exitDecision "+decisionNumber+" in "+d.decision.ruleName+
00254 " lookahead "+d.k +" max token "+lastRealTokenTouchedInDecision);
00255 decisionEvents.add(d);
00256 }
00257
00258 public void consumeToken(Token token) {
00259 if (dump) System.out.println("consume token "+token);
00260 if ( !inDecision() ) {
00261 stats.numTokens++;
00262 return;
00263 }
00264 if ( lastRealTokenTouchedInDecision==null ||
00265 lastRealTokenTouchedInDecision.getTokenIndex() < token.getTokenIndex() )
00266 {
00267 lastRealTokenTouchedInDecision = token;
00268 }
00269 DecisionEvent d = currentDecision();
00270
00271 int thisRefIndex = token.getTokenIndex();
00272 int numHidden = getNumberOfHiddenTokens(d.startIndex, thisRefIndex);
00273 int depth = thisRefIndex - d.startIndex - numHidden + 1;
00274
00275 if (dump) System.out.println("consume "+thisRefIndex+" "+depth+" tokens ahead in "+
00276 d.decision.ruleName+"-"+d.decision.decision+" start index "+d.startIndex);
00277 }
00278
00282 public boolean inDecision() {
00283 return decisionStack.size()>0;
00284 }
00285
00286 public void consumeHiddenToken(Token token) {
00287
00288 if ( !inDecision() ) stats.numHiddenTokens++;
00289 }
00290
00293 public void LT(int i, Token t) {
00294 if ( inDecision() && i>0 ) {
00295 DecisionEvent d = currentDecision();
00296 if (dump) System.out.println("LT("+i+")="+t+" index "+t.getTokenIndex()+" relative to "+d.decision.ruleName+"-"+
00297 d.decision.decision+" start index "+d.startIndex);
00298 if ( lastRealTokenTouchedInDecision==null ||
00299 lastRealTokenTouchedInDecision.getTokenIndex() < t.getTokenIndex() )
00300 {
00301 lastRealTokenTouchedInDecision = t;
00302 if (dump) System.out.println("set last token "+lastRealTokenTouchedInDecision);
00303 }
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320 }
00321 }
00322
00338 public void beginBacktrack(int level) {
00339 if (dump) System.out.println("enter backtrack "+level);
00340 backtrackDepth++;
00341 DecisionEvent e = currentDecision();
00342 if ( e.decision.couldBacktrack ) {
00343 stats.numBacktrackOccurrences++;
00344 e.decision.numBacktrackOccurrences++;
00345 e.backtracks = true;
00346 }
00347 }
00348
00350 public void endBacktrack(int level, boolean successful) {
00351 if (dump) System.out.println("exit backtrack "+level+": "+successful);
00352 backtrackDepth--;
00353 }
00354
00355 @Override
00356 public void mark(int i) {
00357 if (dump) System.out.println("mark "+i);
00358 }
00359
00360 @Override
00361 public void rewind(int i) {
00362 if (dump) System.out.println("rewind "+i);
00363 }
00364
00365 @Override
00366 public void rewind() {
00367 if (dump) System.out.println("rewind");
00368 }
00369
00370
00371
00372 protected DecisionEvent currentDecision() {
00373 return decisionStack.peek();
00374 }
00375
00376 public void recognitionException(RecognitionException e) {
00377 stats.numReportedErrors++;
00378 }
00379
00380 public void semanticPredicate(boolean result, String predicate) {
00381 stats.numSemanticPredicates++;
00382 if ( inDecision() ) {
00383 DecisionEvent d = currentDecision();
00384 d.evalSemPred = true;
00385 d.decision.numSemPredEvals++;
00386 if (dump) System.out.println("eval "+predicate+" in "+d.decision.ruleName+"-"+
00387 d.decision.decision);
00388 }
00389 }
00390
00391 public void terminate() {
00392 for (DecisionEvent e : decisionEvents) {
00393
00394 e.decision.avgk += e.k;
00395 stats.avgkPerDecisionEvent += e.k;
00396 if ( e.backtracks ) {
00397 stats.avgkPerBacktrackingDecisionEvent += e.k;
00398 }
00399 }
00400 stats.averageDecisionPercentBacktracks = 0.0f;
00401 for (DecisionDescriptor d : decisions.values()) {
00402 stats.numDecisionsCovered++;
00403 d.avgk /= (double)d.n;
00404 if ( d.couldBacktrack ) {
00405 stats.numDecisionsThatPotentiallyBacktrack++;
00406 float percentBacktracks = d.numBacktrackOccurrences / (float)d.n;
00407
00408 stats.averageDecisionPercentBacktracks += percentBacktracks;
00409 }
00410
00411 if ( d.numBacktrackOccurrences > 0 ) {
00412 stats.numDecisionsThatDoBacktrack++;
00413 }
00414 }
00415 stats.averageDecisionPercentBacktracks /= stats.numDecisionsThatPotentiallyBacktrack;
00416 stats.averageDecisionPercentBacktracks *= 100;
00417 stats.avgkPerDecisionEvent /= stats.numDecisionEvents;
00418 stats.avgkPerBacktrackingDecisionEvent /= (double)stats.numBacktrackOccurrences;
00419
00420 System.err.println(toString());
00421 System.err.println(getDecisionStatsDump());
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431 }
00432
00433 public void setParser(DebugParser parser) {
00434 this.parser = parser;
00435 }
00436
00437
00438
00439 public String toNotifyString() {
00440 StringBuffer buf = new StringBuffer();
00441 buf.append(Version);
00442 buf.append('\t');
00443 buf.append(parser.getClass().getName());
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498 return buf.toString();
00499 }
00500
00501 public String toString() {
00502 return toString(getReport());
00503 }
00504
00505 public ProfileStats getReport() {
00506
00507
00508
00509
00510
00511
00512
00513
00514 stats.Version = Version;
00515 stats.name = parser.getClass().getName();
00516 stats.numUniqueRulesInvoked = uniqueRules.size();
00517
00518 return stats;
00519 }
00520
00521 public DoubleKeyMap getDecisionStats() {
00522 return decisions;
00523 }
00524
00525 public List getDecisionEvents() {
00526 return decisionEvents;
00527 }
00528
00529 public static String toString(ProfileStats stats) {
00530 StringBuffer buf = new StringBuffer();
00531 buf.append("ANTLR Runtime Report; Profile Version ");
00532 buf.append(stats.Version);
00533 buf.append(newline);
00534 buf.append("parser name ");
00535 buf.append(stats.name);
00536 buf.append(newline);
00537 buf.append("Number of rule invocations ");
00538 buf.append(stats.numRuleInvocations);
00539 buf.append(newline);
00540 buf.append("Number of unique rules visited ");
00541 buf.append(stats.numUniqueRulesInvoked);
00542 buf.append(newline);
00543 buf.append("Number of decision events ");
00544 buf.append(stats.numDecisionEvents);
00545 buf.append(newline);
00546 buf.append("Overall average k per decision event ");
00547 buf.append(stats.avgkPerDecisionEvent);
00548 buf.append(newline);
00549 buf.append("Number of backtracking occurrences (can be multiple per decision) ");
00550 buf.append(stats.numBacktrackOccurrences);
00551 buf.append(newline);
00552 buf.append("Overall average k per decision event that backtracks ");
00553 buf.append(stats.avgkPerBacktrackingDecisionEvent);
00554 buf.append(newline);
00555 buf.append("Number of rule invocations while backtracking ");
00556 buf.append(stats.numGuessingRuleInvocations);
00557 buf.append(newline);
00558 buf.append("num decisions that potentially backtrack ");
00559 buf.append(stats.numDecisionsThatPotentiallyBacktrack);
00560 buf.append(newline);
00561 buf.append("num decisions that do backtrack ");
00562 buf.append(stats.numDecisionsThatDoBacktrack);
00563 buf.append(newline);
00564 buf.append("num decisions that potentially backtrack but don't ");
00565 buf.append(stats.numDecisionsThatPotentiallyBacktrack - stats.numDecisionsThatDoBacktrack);
00566 buf.append(newline);
00567 buf.append("average % of time a potentially backtracking decision backtracks ");
00568 buf.append(stats.averageDecisionPercentBacktracks);
00569 buf.append(newline);
00570 buf.append("num unique decisions covered ");
00571 buf.append(stats.numDecisionsCovered);
00572 buf.append(newline);
00573 buf.append("max rule invocation nesting depth ");
00574 buf.append(stats.maxRuleInvocationDepth);
00575 buf.append(newline);
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622 buf.append("rule memoization cache size ");
00623 buf.append(stats.numMemoizationCacheEntries);
00624 buf.append(newline);
00625 buf.append("number of rule memoization cache hits ");
00626 buf.append(stats.numMemoizationCacheHits);
00627 buf.append(newline);
00628 buf.append("number of rule memoization cache misses ");
00629 buf.append(stats.numMemoizationCacheMisses);
00630 buf.append(newline);
00631
00632
00633
00634 buf.append("number of tokens ");
00635 buf.append(stats.numTokens);
00636 buf.append(newline);
00637 buf.append("number of hidden tokens ");
00638 buf.append(stats.numHiddenTokens);
00639 buf.append(newline);
00640 buf.append("number of char ");
00641 buf.append(stats.numCharsMatched);
00642 buf.append(newline);
00643 buf.append("number of hidden char ");
00644 buf.append(stats.numHiddenCharsMatched);
00645 buf.append(newline);
00646 buf.append("number of syntax errors ");
00647 buf.append(stats.numReportedErrors);
00648 buf.append(newline);
00649 return buf.toString();
00650 }
00651
00652 public String getDecisionStatsDump() {
00653 StringBuffer buf = new StringBuffer();
00654 buf.append("location");
00655 buf.append(DATA_SEP);
00656 buf.append("n");
00657 buf.append(DATA_SEP);
00658 buf.append("avgk");
00659 buf.append(DATA_SEP);
00660 buf.append("maxk");
00661 buf.append(DATA_SEP);
00662 buf.append("synpred");
00663 buf.append(DATA_SEP);
00664 buf.append("sempred");
00665 buf.append(DATA_SEP);
00666 buf.append("canbacktrack");
00667 buf.append("\n");
00668 for (String fileName : decisions.keySet()) {
00669 for (int d : decisions.keySet(fileName)) {
00670 DecisionDescriptor s = decisions.get(fileName, d);
00671 buf.append(s.decision);
00672 buf.append("@");
00673 buf.append(locationDescription(s.fileName,s.ruleName,s.line,s.pos));
00674 buf.append(DATA_SEP);
00675 buf.append(s.n);
00676 buf.append(DATA_SEP);
00677 buf.append(String.format("%.2f",s.avgk));
00678 buf.append(DATA_SEP);
00679 buf.append(s.maxk);
00680 buf.append(DATA_SEP);
00681 buf.append(s.numBacktrackOccurrences);
00682 buf.append(DATA_SEP);
00683 buf.append(s.numSemPredEvals);
00684 buf.append(DATA_SEP);
00685 buf.append(s.couldBacktrack ?"1":"0");
00686 buf.append(newline);
00687 }
00688 }
00689 return buf.toString();
00690 }
00691
00692 protected int[] trim(int[] X, int n) {
00693 if ( n<X.length ) {
00694 int[] trimmed = new int[n];
00695 System.arraycopy(X,0,trimmed,0,n);
00696 X = trimmed;
00697 }
00698 return X;
00699 }
00700
00701 protected int[] toArray(List a) {
00702 int[] x = new int[a.size()];
00703 for (int i = 0; i < a.size(); i++) {
00704 Integer I = (Integer) a.get(i);
00705 x[i] = I.intValue();
00706 }
00707 return x;
00708 }
00709
00711 public int getNumberOfHiddenTokens(int i, int j) {
00712 int n = 0;
00713 TokenStream input = parser.getTokenStream();
00714 for (int ti = i; ti<input.size() && ti <= j; ti++) {
00715 Token t = input.get(ti);
00716 if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) {
00717 n++;
00718 }
00719 }
00720 return n;
00721 }
00722
00723 protected String locationDescription() {
00724 return locationDescription(
00725 currentGrammarFileName.peek(),
00726 currentRuleName.peek(),
00727 currentLine.peek(),
00728 currentPos.peek());
00729 }
00730
00731 protected String locationDescription(String file, String rule, int line, int pos) {
00732 return file+":"+line+":"+pos+"(" + rule + ")";
00733 }
00734 }