| /* |
| [The "BSD licence"] |
| Copyright (c) 2005-2008 Terence Parr |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| 1. Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| 2. Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| 3. The name of the author may not be used to endorse or promote products |
| derived from this software without specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package org.eclipse.persistence.internal.libraries.antlr.runtime.debug; |
| |
| import org.eclipse.persistence.internal.libraries.antlr.runtime.*; |
| import org.eclipse.persistence.internal.libraries.antlr.runtime.misc.Stats; |
| |
| import java.util.*; |
| import java.io.IOException; |
| |
| /** Using the debug event interface, track what is happening in the parser |
| * and record statistics about the runtime. |
| */ |
| public class Profiler extends BlankDebugEventListener { |
| /** Because I may change the stats, I need to track that for later |
| * computations to be consistent. |
| */ |
| public static final String Version = "2"; |
| public static final String RUNTIME_STATS_FILENAME = "runtime.stats"; |
| public static final int NUM_RUNTIME_STATS = 29; |
| |
| public DebugParser parser = null; |
| |
| // working variables |
| |
| protected int ruleLevel = 0; |
| protected int decisionLevel = 0; |
| protected int maxLookaheadInCurrentDecision = 0; |
| protected CommonToken lastTokenConsumed=null; |
| |
| protected List lookaheadStack = new ArrayList(); |
| |
| // stats variables |
| |
| public int numRuleInvocations = 0; |
| public int numGuessingRuleInvocations = 0; |
| public int maxRuleInvocationDepth = 0; |
| public int numFixedDecisions = 0; |
| public int numCyclicDecisions = 0; |
| public int numBacktrackDecisions = 0; |
| public int[] decisionMaxFixedLookaheads = new int[200]; // TODO: make List |
| public int[] decisionMaxCyclicLookaheads = new int[200]; |
| public List decisionMaxSynPredLookaheads = new ArrayList(); |
| public int numHiddenTokens = 0; |
| public int numCharsMatched = 0; |
| public int numHiddenCharsMatched = 0; |
| public int numSemanticPredicates = 0; |
| public int numSyntacticPredicates = 0; |
| protected int numberReportedErrors = 0; |
| public int numMemoizationCacheMisses = 0; |
| public int numMemoizationCacheHits = 0; |
| public int numMemoizationCacheEntries = 0; |
| |
| public Profiler() { |
| } |
| |
| public Profiler(DebugParser parser) { |
| this.parser = parser; |
| } |
| |
| public void enterRule(String grammarFileName, String ruleName) { |
| //System.out.println("enterRule "+ruleName); |
| ruleLevel++; |
| numRuleInvocations++; |
| if ( ruleLevel >maxRuleInvocationDepth ) { |
| maxRuleInvocationDepth = ruleLevel; |
| } |
| |
| } |
| |
| /** Track memoization; this is not part of standard debug interface |
| * but is triggered by profiling. Code gen inserts an override |
| * for this method in the recognizer, which triggers this method. |
| */ |
| public void examineRuleMemoization(IntStream input, |
| int ruleIndex, |
| String ruleName) |
| { |
| //System.out.println("examine memo "+ruleName); |
| int stopIndex = parser.getRuleMemoization(ruleIndex, input.index()); |
| if ( stopIndex==BaseRecognizer.MEMO_RULE_UNKNOWN ) { |
| //System.out.println("rule "+ruleIndex+" missed @ "+input.index()); |
| numMemoizationCacheMisses++; |
| numGuessingRuleInvocations++; // we'll have to enter |
| } |
| else { |
| // regardless of rule success/failure, if in cache, we have a cache hit |
| //System.out.println("rule "+ruleIndex+" hit @ "+input.index()); |
| numMemoizationCacheHits++; |
| } |
| } |
| |
| public void memoize(IntStream input, |
| int ruleIndex, |
| int ruleStartIndex, |
| String ruleName) |
| { |
| // count how many entries go into table |
| //System.out.println("memoize "+ruleName); |
| numMemoizationCacheEntries++; |
| } |
| |
| public void exitRule(String grammarFileName, String ruleName) { |
| ruleLevel--; |
| } |
| |
| public void enterDecision(int decisionNumber) { |
| decisionLevel++; |
| int startingLookaheadIndex = parser.getTokenStream().index(); |
| //System.out.println("enterDecision "+decisionNumber+" @ index "+startingLookaheadIndex); |
| lookaheadStack.add(new Integer(startingLookaheadIndex)); |
| } |
| |
| public void exitDecision(int decisionNumber) { |
| //System.out.println("exitDecision "+decisionNumber); |
| // track how many of acyclic, cyclic here as we don't know what kind |
| // yet in enterDecision event. |
| if ( parser.isCyclicDecision ) { |
| numCyclicDecisions++; |
| } |
| else { |
| numFixedDecisions++; |
| } |
| lookaheadStack.remove(lookaheadStack.size()-1); // pop lookahead depth counter |
| decisionLevel--; |
| if ( parser.isCyclicDecision ) { |
| if ( numCyclicDecisions>=decisionMaxCyclicLookaheads.length ) { |
| int[] bigger = new int[decisionMaxCyclicLookaheads.length*2]; |
| System.arraycopy(decisionMaxCyclicLookaheads,0,bigger,0,decisionMaxCyclicLookaheads.length); |
| decisionMaxCyclicLookaheads = bigger; |
| } |
| decisionMaxCyclicLookaheads[numCyclicDecisions-1] = maxLookaheadInCurrentDecision; |
| } |
| else { |
| if ( numFixedDecisions>=decisionMaxFixedLookaheads.length ) { |
| int[] bigger = new int[decisionMaxFixedLookaheads.length*2]; |
| System.arraycopy(decisionMaxFixedLookaheads,0,bigger,0,decisionMaxFixedLookaheads.length); |
| decisionMaxFixedLookaheads = bigger; |
| } |
| decisionMaxFixedLookaheads[numFixedDecisions-1] = maxLookaheadInCurrentDecision; |
| } |
| parser.isCyclicDecision = false; // can't nest so just reset to false |
| maxLookaheadInCurrentDecision = 0; |
| } |
| |
| public void consumeToken(Token token) { |
| //System.out.println("consume token "+token); |
| lastTokenConsumed = (CommonToken)token; |
| } |
| |
| /** The parser is in a decision if the decision depth > 0. This |
| * works for backtracking also, which can have nested decisions. |
| */ |
| public boolean inDecision() { |
| return decisionLevel>0; |
| } |
| |
| public void consumeHiddenToken(Token token) { |
| //System.out.println("consume hidden token "+token); |
| lastTokenConsumed = (CommonToken)token; |
| } |
| |
| /** Track refs to lookahead if in a fixed/nonfixed decision. |
| */ |
| public void LT(int i, Token t) { |
| if ( inDecision() ) { |
| // get starting index off stack |
| int stackTop = lookaheadStack.size()-1; |
| Integer startingIndex = (Integer)lookaheadStack.get(stackTop); |
| // compute lookahead depth |
| int thisRefIndex = parser.getTokenStream().index(); |
| int numHidden = |
| getNumberOfHiddenTokens(startingIndex.intValue(), thisRefIndex); |
| int depth = i + thisRefIndex - startingIndex.intValue() - numHidden; |
| /* |
| System.out.println("LT("+i+") @ index "+thisRefIndex+" is depth "+depth+ |
| " max is "+maxLookaheadInCurrentDecision); |
| */ |
| if ( depth>maxLookaheadInCurrentDecision ) { |
| maxLookaheadInCurrentDecision = depth; |
| } |
| } |
| } |
| |
| /** Track backtracking decisions. You'll see a fixed or cyclic decision |
| * and then a backtrack. |
| * |
| * enter rule |
| * ... |
| * enter decision |
| * LA and possibly consumes (for cyclic DFAs) |
| * begin backtrack level |
| * mark m |
| * rewind m |
| * end backtrack level, success |
| * exit decision |
| * ... |
| * exit rule |
| */ |
| public void beginBacktrack(int level) { |
| //System.out.println("enter backtrack "+level); |
| numBacktrackDecisions++; |
| } |
| |
| /** Successful or not, track how much lookahead synpreds use */ |
| public void endBacktrack(int level, boolean successful) { |
| //System.out.println("exit backtrack "+level+": "+successful); |
| decisionMaxSynPredLookaheads.add( |
| new Integer(maxLookaheadInCurrentDecision) |
| ); |
| } |
| |
| /* |
| public void mark(int marker) { |
| int i = parser.getTokenStream().index(); |
| System.out.println("mark @ index "+i); |
| synPredLookaheadStack.add(new Integer(i)); |
| } |
| |
| public void rewind(int marker) { |
| // pop starting index off stack |
| int stackTop = synPredLookaheadStack.size()-1; |
| Integer startingIndex = (Integer)synPredLookaheadStack.get(stackTop); |
| synPredLookaheadStack.remove(synPredLookaheadStack.size()-1); |
| // compute lookahead depth |
| int stopIndex = parser.getTokenStream().index(); |
| System.out.println("rewind @ index "+stopIndex); |
| int depth = stopIndex - startingIndex.intValue(); |
| System.out.println("depth of lookahead for synpred: "+depth); |
| decisionMaxSynPredLookaheads.add( |
| new Integer(depth) |
| ); |
| } |
| */ |
| |
| public void recognitionException(RecognitionException e) { |
| numberReportedErrors++; |
| } |
| |
| public void semanticPredicate(boolean result, String predicate) { |
| if ( inDecision() ) { |
| numSemanticPredicates++; |
| } |
| } |
| |
| public void terminate() { |
| String stats = toNotifyString(); |
| try { |
| Stats.writeReport(RUNTIME_STATS_FILENAME,stats); |
| } |
| catch (IOException ioe) { |
| System.err.println(ioe); |
| ioe.printStackTrace(System.err); |
| } |
| System.out.println(toString(stats)); |
| } |
| |
| public void setParser(DebugParser parser) { |
| this.parser = parser; |
| } |
| |
| // R E P O R T I N G |
| |
| public String toNotifyString() { |
| TokenStream input = parser.getTokenStream(); |
| for (int i=0; i<input.size()&&lastTokenConsumed!=null&&i<=lastTokenConsumed.getTokenIndex(); i++) { |
| Token t = input.get(i); |
| if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) { |
| numHiddenTokens++; |
| numHiddenCharsMatched += t.getText().length(); |
| } |
| } |
| numCharsMatched = lastTokenConsumed.getStopIndex() + 1; |
| decisionMaxFixedLookaheads = trim(decisionMaxFixedLookaheads, numFixedDecisions); |
| decisionMaxCyclicLookaheads = trim(decisionMaxCyclicLookaheads, numCyclicDecisions); |
| StringBuffer buf = new StringBuffer(); |
| buf.append(Version); |
| buf.append('\t'); |
| buf.append(parser.getClass().getName()); |
| buf.append('\t'); |
| buf.append(numRuleInvocations); |
| buf.append('\t'); |
| buf.append(maxRuleInvocationDepth); |
| buf.append('\t'); |
| buf.append(numFixedDecisions); |
| buf.append('\t'); |
| buf.append(Stats.min(decisionMaxFixedLookaheads)); |
| buf.append('\t'); |
| buf.append(Stats.max(decisionMaxFixedLookaheads)); |
| buf.append('\t'); |
| buf.append(Stats.avg(decisionMaxFixedLookaheads)); |
| buf.append('\t'); |
| buf.append(Stats.stddev(decisionMaxFixedLookaheads)); |
| buf.append('\t'); |
| buf.append(numCyclicDecisions); |
| buf.append('\t'); |
| buf.append(Stats.min(decisionMaxCyclicLookaheads)); |
| buf.append('\t'); |
| buf.append(Stats.max(decisionMaxCyclicLookaheads)); |
| buf.append('\t'); |
| buf.append(Stats.avg(decisionMaxCyclicLookaheads)); |
| buf.append('\t'); |
| buf.append(Stats.stddev(decisionMaxCyclicLookaheads)); |
| buf.append('\t'); |
| buf.append(numBacktrackDecisions); |
| buf.append('\t'); |
| buf.append(Stats.min(toArray(decisionMaxSynPredLookaheads))); |
| buf.append('\t'); |
| buf.append(Stats.max(toArray(decisionMaxSynPredLookaheads))); |
| buf.append('\t'); |
| buf.append(Stats.avg(toArray(decisionMaxSynPredLookaheads))); |
| buf.append('\t'); |
| buf.append(Stats.stddev(toArray(decisionMaxSynPredLookaheads))); |
| buf.append('\t'); |
| buf.append(numSemanticPredicates); |
| buf.append('\t'); |
| buf.append(parser.getTokenStream().size()); |
| buf.append('\t'); |
| buf.append(numHiddenTokens); |
| buf.append('\t'); |
| buf.append(numCharsMatched); |
| buf.append('\t'); |
| buf.append(numHiddenCharsMatched); |
| buf.append('\t'); |
| buf.append(numberReportedErrors); |
| buf.append('\t'); |
| buf.append(numMemoizationCacheHits); |
| buf.append('\t'); |
| buf.append(numMemoizationCacheMisses); |
| buf.append('\t'); |
| buf.append(numGuessingRuleInvocations); |
| buf.append('\t'); |
| buf.append(numMemoizationCacheEntries); |
| return buf.toString(); |
| } |
| |
| public String toString() { |
| return toString(toNotifyString()); |
| } |
| |
| protected static String[] decodeReportData(String data) { |
| String[] fields = new String[NUM_RUNTIME_STATS]; |
| StringTokenizer st = new StringTokenizer(data, "\t"); |
| int i = 0; |
| while ( st.hasMoreTokens() ) { |
| fields[i] = st.nextToken(); |
| i++; |
| } |
| if ( i!=NUM_RUNTIME_STATS ) { |
| return null; |
| } |
| return fields; |
| } |
| |
| public static String toString(String notifyDataLine) { |
| String[] fields = decodeReportData(notifyDataLine); |
| if ( fields==null ) { |
| return null; |
| } |
| StringBuffer buf = new StringBuffer(); |
| buf.append("ANTLR Runtime Report; Profile Version "); |
| buf.append(fields[0]); |
| buf.append('\n'); |
| buf.append("parser name "); |
| buf.append(fields[1]); |
| buf.append('\n'); |
| buf.append("Number of rule invocations "); |
| buf.append(fields[2]); |
| buf.append('\n'); |
| buf.append("Number of rule invocations in \"guessing\" mode "); |
| buf.append(fields[27]); |
| buf.append('\n'); |
| buf.append("max rule invocation nesting depth "); |
| buf.append(fields[3]); |
| buf.append('\n'); |
| buf.append("number of fixed lookahead decisions "); |
| buf.append(fields[4]); |
| buf.append('\n'); |
| buf.append("min lookahead used in a fixed lookahead decision "); |
| buf.append(fields[5]); |
| buf.append('\n'); |
| buf.append("max lookahead used in a fixed lookahead decision "); |
| buf.append(fields[6]); |
| buf.append('\n'); |
| buf.append("average lookahead depth used in fixed lookahead decisions "); |
| buf.append(fields[7]); |
| buf.append('\n'); |
| buf.append("standard deviation of depth used in fixed lookahead decisions "); |
| buf.append(fields[8]); |
| buf.append('\n'); |
| buf.append("number of arbitrary lookahead decisions "); |
| buf.append(fields[9]); |
| buf.append('\n'); |
| buf.append("min lookahead used in an arbitrary lookahead decision "); |
| buf.append(fields[10]); |
| buf.append('\n'); |
| buf.append("max lookahead used in an arbitrary lookahead decision "); |
| buf.append(fields[11]); |
| buf.append('\n'); |
| buf.append("average lookahead depth used in arbitrary lookahead decisions "); |
| buf.append(fields[12]); |
| buf.append('\n'); |
| buf.append("standard deviation of depth used in arbitrary lookahead decisions "); |
| buf.append(fields[13]); |
| buf.append('\n'); |
| buf.append("number of evaluated syntactic predicates "); |
| buf.append(fields[14]); |
| buf.append('\n'); |
| buf.append("min lookahead used in a syntactic predicate "); |
| buf.append(fields[15]); |
| buf.append('\n'); |
| buf.append("max lookahead used in a syntactic predicate "); |
| buf.append(fields[16]); |
| buf.append('\n'); |
| buf.append("average lookahead depth used in syntactic predicates "); |
| buf.append(fields[17]); |
| buf.append('\n'); |
| buf.append("standard deviation of depth used in syntactic predicates "); |
| buf.append(fields[18]); |
| buf.append('\n'); |
| buf.append("rule memoization cache size "); |
| buf.append(fields[28]); |
| buf.append('\n'); |
| buf.append("number of rule memoization cache hits "); |
| buf.append(fields[25]); |
| buf.append('\n'); |
| buf.append("number of rule memoization cache misses "); |
| buf.append(fields[26]); |
| buf.append('\n'); |
| buf.append("number of evaluated semantic predicates "); |
| buf.append(fields[19]); |
| buf.append('\n'); |
| buf.append("number of tokens "); |
| buf.append(fields[20]); |
| buf.append('\n'); |
| buf.append("number of hidden tokens "); |
| buf.append(fields[21]); |
| buf.append('\n'); |
| buf.append("number of char "); |
| buf.append(fields[22]); |
| buf.append('\n'); |
| buf.append("number of hidden char "); |
| buf.append(fields[23]); |
| buf.append('\n'); |
| buf.append("number of syntax errors "); |
| buf.append(fields[24]); |
| buf.append('\n'); |
| return buf.toString(); |
| } |
| |
| protected int[] trim(int[] X, int n) { |
| if ( n<X.length ) { |
| int[] trimmed = new int[n]; |
| System.arraycopy(X,0,trimmed,0,n); |
| X = trimmed; |
| } |
| return X; |
| } |
| |
| protected int[] toArray(List a) { |
| int[] x = new int[a.size()]; |
| for (int i = 0; i < a.size(); i++) { |
| Integer I = (Integer) a.get(i); |
| x[i] = I.intValue(); |
| } |
| return x; |
| } |
| |
| /** Get num hidden tokens between i..j inclusive */ |
| public int getNumberOfHiddenTokens(int i, int j) { |
| int n = 0; |
| TokenStream input = parser.getTokenStream(); |
| for (int ti = i; ti<input.size() && ti <= j; ti++) { |
| Token t = input.get(ti); |
| if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) { |
| n++; |
| } |
| } |
| return n; |
| } |
| } |