Home | History | Annotate | Download | only in debug
      1 /*
      2  [The "BSD license"]
      3  Copyright (c) 2005-2009 Terence Parr
      4  All rights reserved.
      5 
      6  Redistribution and use in source and binary forms, with or without
      7  modification, are permitted provided that the following conditions
      8  are met:
      9  1. Redistributions of source code must retain the above copyright
     10      notice, this list of conditions and the following disclaimer.
     11  2. Redistributions in binary form must reproduce the above copyright
     12      notice, this list of conditions and the following disclaimer in the
     13      documentation and/or other materials provided with the distribution.
     14  3. The name of the author may not be used to endorse or promote products
     15      derived from this software without specific prior written permission.
     16 
     17  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 package org.antlr.runtime.debug;
     29 
     30 import org.antlr.runtime.*;
     31 import org.antlr.runtime.misc.DoubleKeyMap;
     32 
     33 import java.util.*;
     34 
     35 /** Using the debug event interface, track what is happening in the parser
     36  *  and record statistics about the runtime.
     37  */
     38 public class Profiler extends BlankDebugEventListener {
     39 	public static final String DATA_SEP = "\t";
     40 	public static final String newline = System.getProperty("line.separator");
     41 
     42 	static boolean dump = false;
     43 
     44 	public static class ProfileStats {
     45 		public String Version;
     46 		public String name;
     47 		public int numRuleInvocations;
     48 		public int numUniqueRulesInvoked;
     49 		public int numDecisionEvents;
     50 		public int numDecisionsCovered;
     51 		public int numDecisionsThatPotentiallyBacktrack;
     52 		public int numDecisionsThatDoBacktrack;
     53 		public int maxRuleInvocationDepth;
     54 		public float avgkPerDecisionEvent;
     55 		public float avgkPerBacktrackingDecisionEvent;
     56 		public float averageDecisionPercentBacktracks;
     57 		public int numBacktrackOccurrences; // doesn't count gated DFA edges
     58 
     59 		public int numFixedDecisions;
     60 		public int minDecisionMaxFixedLookaheads;
     61 		public int maxDecisionMaxFixedLookaheads;
     62 		public int avgDecisionMaxFixedLookaheads;
     63 		public int stddevDecisionMaxFixedLookaheads;
     64 		public int numCyclicDecisions;
     65 		public int minDecisionMaxCyclicLookaheads;
     66 		public int maxDecisionMaxCyclicLookaheads;
     67 		public int avgDecisionMaxCyclicLookaheads;
     68 		public int stddevDecisionMaxCyclicLookaheads;
     69 //		int Stats.min(toArray(decisionMaxSynPredLookaheads);
     70 //		int Stats.max(toArray(decisionMaxSynPredLookaheads);
     71 //		int Stats.avg(toArray(decisionMaxSynPredLookaheads);
     72 //		int Stats.stddev(toArray(decisionMaxSynPredLookaheads);
     73 		public int numSemanticPredicates;
     74 		public int numTokens;
     75 		public int numHiddenTokens;
     76 		public int numCharsMatched;
     77 		public int numHiddenCharsMatched;
     78 		public int numReportedErrors;
     79 		public int numMemoizationCacheHits;
     80 		public int numMemoizationCacheMisses;
     81 		public int numGuessingRuleInvocations;
     82 		public int numMemoizationCacheEntries;
     83 	}
     84 
     85 	public static class DecisionDescriptor {
     86 		public int decision;
     87 		public String fileName;
     88 		public String ruleName;
     89 		public int line;
     90 		public int pos;
     91 		public boolean couldBacktrack;
     92 
     93 		public int n;
     94 		public float avgk; // avg across all decision events
     95 		public int maxk;
     96 		public int numBacktrackOccurrences;
     97 		public int numSemPredEvals;
     98 	}
     99 
    100 	// all about a specific exec of a single decision
    101 	public static class DecisionEvent {
    102 		public DecisionDescriptor decision;
    103 		public int startIndex;
    104 		public int k;
    105 		public boolean backtracks; // doesn't count gated DFA edges
    106 		public boolean evalSemPred;
    107 		public long startTime;
    108 		public long stopTime;
    109 		public int numMemoizationCacheHits;
    110 		public int numMemoizationCacheMisses;
    111 	}
    112 
    113 	/** Because I may change the stats, I need to track that for later
    114 	 *  computations to be consistent.
    115 	 */
    116 	public static final String Version = "3";
    117 	public static final String RUNTIME_STATS_FILENAME = "runtime.stats";
    118 
    119 	/** Ack, should not store parser; can't do remote stuff.  Well, we pass
    120 	 *  input stream around too so I guess it's ok.
    121 	 */
    122 	public DebugParser parser = null;
    123 
    124 	// working variables
    125 
    126 	protected int ruleLevel = 0;
    127 	//protected int decisionLevel = 0;
    128 	protected Token lastRealTokenTouchedInDecision;
    129 	protected Set<String> uniqueRules = new HashSet<String>();
    130 	protected Stack<String> currentGrammarFileName = new Stack();
    131 	protected Stack<String> currentRuleName = new Stack();
    132 	protected Stack<Integer> currentLine = new Stack();
    133 	protected Stack<Integer> currentPos = new Stack();
    134 
    135 	// Vector<DecisionStats>
    136 	//protected Vector decisions = new Vector(200); // need setSize
    137 	protected DoubleKeyMap<String,Integer, DecisionDescriptor> decisions =
    138 		new DoubleKeyMap<String,Integer, DecisionDescriptor>();
    139 
    140 	// Record a DecisionData for each decision we hit while parsing
    141 	protected List<DecisionEvent> decisionEvents = new ArrayList<DecisionEvent>();
    142 	protected Stack<DecisionEvent> decisionStack = new Stack<DecisionEvent>();
    143 
    144 	protected int backtrackDepth;
    145 
    146 	ProfileStats stats = new ProfileStats();
    147 
    148 	public Profiler() {
    149 	}
    150 
    151 	public Profiler(DebugParser parser) {
    152 		this.parser = parser;
    153 	}
    154 
    155 	public void enterRule(String grammarFileName, String ruleName) {
    156 //		System.out.println("enterRule "+grammarFileName+":"+ruleName);
    157 		ruleLevel++;
    158 		stats.numRuleInvocations++;
    159 		uniqueRules.add(grammarFileName+":"+ruleName);
    160 		stats.maxRuleInvocationDepth = Math.max(stats.maxRuleInvocationDepth, ruleLevel);
    161 		currentGrammarFileName.push( grammarFileName );
    162 		currentRuleName.push( ruleName );
    163 	}
    164 
    165 	public void exitRule(String grammarFileName, String ruleName) {
    166 		ruleLevel--;
    167 		currentGrammarFileName.pop();
    168 		currentRuleName.pop();
    169 	}
    170 
    171 	/** Track memoization; this is not part of standard debug interface
    172 	 *  but is triggered by profiling.  Code gen inserts an override
    173 	 *  for this method in the recognizer, which triggers this method.
    174 	 *  Called from alreadyParsedRule().
    175 	 */
    176 	public void examineRuleMemoization(IntStream input,
    177 									   int ruleIndex,
    178 									   int stopIndex, // index or MEMO_RULE_UNKNOWN...
    179 									   String ruleName)
    180 	{
    181 		if (dump) System.out.println("examine memo "+ruleName+" at "+input.index()+": "+stopIndex);
    182 		if ( stopIndex==BaseRecognizer.MEMO_RULE_UNKNOWN ) {
    183 			//System.out.println("rule "+ruleIndex+" missed @ "+input.index());
    184 			stats.numMemoizationCacheMisses++;
    185 			stats.numGuessingRuleInvocations++; // we'll have to enter
    186 			currentDecision().numMemoizationCacheMisses++;
    187 		}
    188 		else {
    189 			// regardless of rule success/failure, if in cache, we have a cache hit
    190 			//System.out.println("rule "+ruleIndex+" hit @ "+input.index());
    191 			stats.numMemoizationCacheHits++;
    192 			currentDecision().numMemoizationCacheHits++;
    193 		}
    194 	}
    195 
    196 	/** Warning: doesn't track success/failure, just unique recording event */
    197 	public void memoize(IntStream input,
    198 						int ruleIndex,
    199 						int ruleStartIndex,
    200 						String ruleName)
    201 	{
    202 		// count how many entries go into table
    203 		if (dump) System.out.println("memoize "+ruleName);
    204 		stats.numMemoizationCacheEntries++;
    205 	}
    206 
    207 	@Override
    208 	public void location(int line, int pos) {
    209 		currentLine.push(line);
    210 		currentPos.push(pos);
    211 	}
    212 
    213 	public void enterDecision(int decisionNumber, boolean couldBacktrack) {
    214 		lastRealTokenTouchedInDecision = null;
    215 		stats.numDecisionEvents++;
    216 		int startingLookaheadIndex = parser.getTokenStream().index();
    217 		TokenStream input = parser.getTokenStream();
    218 		if ( dump ) System.out.println("enterDecision canBacktrack="+couldBacktrack+" "+ decisionNumber +
    219 						   " backtrack depth " + backtrackDepth +
    220 						   " @ " + input.get(input.index()) +
    221 						   " rule " +locationDescription());
    222 		String g = (String) currentGrammarFileName.peek();
    223 		DecisionDescriptor descriptor = decisions.get(g, decisionNumber);
    224 		if ( descriptor == null ) {
    225 			descriptor = new DecisionDescriptor();
    226 			decisions.put(g, decisionNumber, descriptor);
    227 			descriptor.decision = decisionNumber;
    228 			descriptor.fileName = (String)currentGrammarFileName.peek();
    229 			descriptor.ruleName = (String)currentRuleName.peek();
    230 			descriptor.line = (Integer)currentLine.peek();
    231 			descriptor.pos = (Integer)currentPos.peek();
    232 			descriptor.couldBacktrack = couldBacktrack;
    233 		}
    234 		descriptor.n++;
    235 
    236 		DecisionEvent d = new DecisionEvent();
    237 		decisionStack.push(d);
    238 		d.decision = descriptor;
    239 		d.startTime = System.currentTimeMillis();
    240 		d.startIndex = startingLookaheadIndex;
    241 	}
    242 
    243 	public void exitDecision(int decisionNumber) {
    244 		DecisionEvent d = decisionStack.pop();
    245 		d.stopTime = System.currentTimeMillis();
    246 
    247 		int lastTokenIndex = lastRealTokenTouchedInDecision.getTokenIndex();
    248 		int numHidden = getNumberOfHiddenTokens(d.startIndex, lastTokenIndex);
    249 		int depth = lastTokenIndex - d.startIndex - numHidden + 1; // +1 counts consuming start token as 1
    250 		d.k = depth;
    251 		d.decision.maxk = Math.max(d.decision.maxk, depth);
    252 
    253 		if (dump) System.out.println("exitDecision "+decisionNumber+" in "+d.decision.ruleName+
    254 						   " lookahead "+d.k +" max token "+lastRealTokenTouchedInDecision);
    255 		decisionEvents.add(d); // done with decision; track all
    256 	}
    257 
    258 	public void consumeToken(Token token) {
    259 		if (dump) System.out.println("consume token "+token);
    260 		if ( !inDecision() ) {
    261 			stats.numTokens++;
    262 			return;
    263 		}
    264 		if ( lastRealTokenTouchedInDecision==null ||
    265 			 lastRealTokenTouchedInDecision.getTokenIndex() < token.getTokenIndex() )
    266 		{
    267 			lastRealTokenTouchedInDecision = token;
    268 		}
    269 		DecisionEvent d = currentDecision();
    270 		// compute lookahead depth
    271 		int thisRefIndex = token.getTokenIndex();
    272 		int numHidden = getNumberOfHiddenTokens(d.startIndex, thisRefIndex);
    273 		int depth = thisRefIndex - d.startIndex - numHidden + 1; // +1 counts consuming start token as 1
    274 		//d.maxk = Math.max(d.maxk, depth);
    275 		if (dump) System.out.println("consume "+thisRefIndex+" "+depth+" tokens ahead in "+
    276 						   d.decision.ruleName+"-"+d.decision.decision+" start index "+d.startIndex);
    277 	}
    278 
    279 	/** The parser is in a decision if the decision depth > 0.  This
    280 	 *  works for backtracking also, which can have nested decisions.
    281 	 */
    282 	public boolean inDecision() {
    283 		return decisionStack.size()>0;
    284 	}
    285 
    286 	public void consumeHiddenToken(Token token) {
    287 		//System.out.println("consume hidden token "+token);
    288 		if ( !inDecision() ) stats.numHiddenTokens++;
    289 	}
    290 
    291 	/** Track refs to lookahead if in a fixed/nonfixed decision.
    292 	 */
    293 	public void LT(int i, Token t) {
    294 		if ( inDecision() && i>0 ) {
    295 			DecisionEvent d = currentDecision();
    296 			if (dump) System.out.println("LT("+i+")="+t+" index "+t.getTokenIndex()+" relative to "+d.decision.ruleName+"-"+
    297 							   d.decision.decision+" start index "+d.startIndex);
    298 			if ( lastRealTokenTouchedInDecision==null ||
    299 				 lastRealTokenTouchedInDecision.getTokenIndex() < t.getTokenIndex() )
    300 			{
    301 				lastRealTokenTouchedInDecision = t;
    302 				if (dump) System.out.println("set last token "+lastRealTokenTouchedInDecision);
    303 			}
    304 			// get starting index off stack
    305 //			int stackTop = lookaheadStack.size()-1;
    306 //			Integer startingIndex = (Integer)lookaheadStack.get(stackTop);
    307 //			// compute lookahead depth
    308 //			int thisRefIndex = parser.getTokenStream().index();
    309 //			int numHidden =
    310 //				getNumberOfHiddenTokens(startingIndex.intValue(), thisRefIndex);
    311 //			int depth = i + thisRefIndex - startingIndex.intValue() - numHidden;
    312 //			/*
    313 //			System.out.println("LT("+i+") @ index "+thisRefIndex+" is depth "+depth+
    314 //				" max is "+maxLookaheadInCurrentDecision);
    315 //			*/
    316 //			if ( depth>maxLookaheadInCurrentDecision ) {
    317 //				maxLookaheadInCurrentDecision = depth;
    318 //			}
    319 //			d.maxk = currentDecision()/
    320 		}
    321 	}
    322 
    323 	/** Track backtracking decisions.  You'll see a fixed or cyclic decision
    324 	 *  and then a backtrack.
    325 	 *
    326 	 * 		enter rule
    327 	 * 		...
    328 	 * 		enter decision
    329 	 * 		LA and possibly consumes (for cyclic DFAs)
    330 	 * 		begin backtrack level
    331 	 * 		mark m
    332 	 * 		rewind m
    333 	 * 		end backtrack level, success
    334 	 * 		exit decision
    335 	 * 		...
    336 	 * 		exit rule
    337 	 */
    338 	public void beginBacktrack(int level) {
    339 		if (dump) System.out.println("enter backtrack "+level);
    340 		backtrackDepth++;
    341 		DecisionEvent e = currentDecision();
    342 		if ( e.decision.couldBacktrack ) {
    343 			stats.numBacktrackOccurrences++;
    344 			e.decision.numBacktrackOccurrences++;
    345 			e.backtracks = true;
    346 		}
    347 	}
    348 
    349 	/** Successful or not, track how much lookahead synpreds use */
    350 	public void endBacktrack(int level, boolean successful) {
    351 		if (dump) System.out.println("exit backtrack "+level+": "+successful);
    352 		backtrackDepth--;
    353 	}
    354 
    355 	@Override
    356 	public void mark(int i) {
    357 		if (dump) System.out.println("mark "+i);
    358 	}
    359 
    360 	@Override
    361 	public void rewind(int i) {
    362 		if (dump) System.out.println("rewind "+i);
    363 	}
    364 
    365 	@Override
    366 	public void rewind() {
    367 		if (dump) System.out.println("rewind");
    368 	}
    369 
    370 
    371 
    372 	protected DecisionEvent currentDecision() {
    373 		return decisionStack.peek();
    374 	}
    375 
    376 	public void recognitionException(RecognitionException e) {
    377 		stats.numReportedErrors++;
    378 	}
    379 
    380 	public void semanticPredicate(boolean result, String predicate) {
    381 		stats.numSemanticPredicates++;
    382 		if ( inDecision() ) {
    383 			DecisionEvent d = currentDecision();
    384 			d.evalSemPred = true;
    385 			d.decision.numSemPredEvals++;
    386 			if (dump) System.out.println("eval "+predicate+" in "+d.decision.ruleName+"-"+
    387 							   d.decision.decision);
    388 		}
    389 	}
    390 
    391 	public void terminate() {
    392 		for (DecisionEvent e : decisionEvents) {
    393 			//System.out.println("decision "+e.decision.decision+": k="+e.k);
    394 			e.decision.avgk += e.k;
    395 			stats.avgkPerDecisionEvent += e.k;
    396 			if ( e.backtracks ) { // doesn't count gated syn preds on DFA edges
    397 				stats.avgkPerBacktrackingDecisionEvent += e.k;
    398 			}
    399 		}
    400 		stats.averageDecisionPercentBacktracks = 0.0f;
    401 		for (DecisionDescriptor d : decisions.values()) {
    402 			stats.numDecisionsCovered++;
    403 			d.avgk /= (double)d.n;
    404 			if ( d.couldBacktrack ) {
    405 				stats.numDecisionsThatPotentiallyBacktrack++;
    406 				float percentBacktracks = d.numBacktrackOccurrences / (float)d.n;
    407 				//System.out.println("dec "+d.decision+" backtracks "+percentBacktracks*100+"%");
    408 				stats.averageDecisionPercentBacktracks += percentBacktracks;
    409 			}
    410 			// ignore rules that backtrack along gated DFA edges
    411 			if ( d.numBacktrackOccurrences > 0 ) {
    412 				stats.numDecisionsThatDoBacktrack++;
    413 			}
    414 		}
    415 		stats.averageDecisionPercentBacktracks /= stats.numDecisionsThatPotentiallyBacktrack;
    416 		stats.averageDecisionPercentBacktracks *= 100; // it's a percentage
    417 		stats.avgkPerDecisionEvent /= stats.numDecisionEvents;
    418 		stats.avgkPerBacktrackingDecisionEvent /= (double)stats.numBacktrackOccurrences;
    419 
    420 		System.err.println(toString());
    421 		System.err.println(getDecisionStatsDump());
    422 
    423 //		String stats = toNotifyString();
    424 //		try {
    425 //			Stats.writeReport(RUNTIME_STATS_FILENAME,stats);
    426 //		}
    427 //		catch (IOException ioe) {
    428 //			System.err.println(ioe);
    429 //			ioe.printStackTrace(System.err);
    430 //		}
    431 	}
    432 
    433 	public void setParser(DebugParser parser) {
    434 		this.parser = parser;
    435 	}
    436 
    437 	// R E P O R T I N G
    438 
    439 	public String toNotifyString() {
    440 		StringBuffer buf = new StringBuffer();
    441 		buf.append(Version);
    442 		buf.append('\t');
    443 		buf.append(parser.getClass().getName());
    444 //		buf.append('\t');
    445 //		buf.append(numRuleInvocations);
    446 //		buf.append('\t');
    447 //		buf.append(maxRuleInvocationDepth);
    448 //		buf.append('\t');
    449 //		buf.append(numFixedDecisions);
    450 //		buf.append('\t');
    451 //		buf.append(Stats.min(decisionMaxFixedLookaheads));
    452 //		buf.append('\t');
    453 //		buf.append(Stats.max(decisionMaxFixedLookaheads));
    454 //		buf.append('\t');
    455 //		buf.append(Stats.avg(decisionMaxFixedLookaheads));
    456 //		buf.append('\t');
    457 //		buf.append(Stats.stddev(decisionMaxFixedLookaheads));
    458 //		buf.append('\t');
    459 //		buf.append(numCyclicDecisions);
    460 //		buf.append('\t');
    461 //		buf.append(Stats.min(decisionMaxCyclicLookaheads));
    462 //		buf.append('\t');
    463 //		buf.append(Stats.max(decisionMaxCyclicLookaheads));
    464 //		buf.append('\t');
    465 //		buf.append(Stats.avg(decisionMaxCyclicLookaheads));
    466 //		buf.append('\t');
    467 //		buf.append(Stats.stddev(decisionMaxCyclicLookaheads));
    468 //		buf.append('\t');
    469 //		buf.append(numBacktrackDecisions);
    470 //		buf.append('\t');
    471 //		buf.append(Stats.min(toArray(decisionMaxSynPredLookaheads)));
    472 //		buf.append('\t');
    473 //		buf.append(Stats.max(toArray(decisionMaxSynPredLookaheads)));
    474 //		buf.append('\t');
    475 //		buf.append(Stats.avg(toArray(decisionMaxSynPredLookaheads)));
    476 //		buf.append('\t');
    477 //		buf.append(Stats.stddev(toArray(decisionMaxSynPredLookaheads)));
    478 //		buf.append('\t');
    479 //		buf.append(numSemanticPredicates);
    480 //		buf.append('\t');
    481 //		buf.append(parser.getTokenStream().size());
    482 //		buf.append('\t');
    483 //		buf.append(numHiddenTokens);
    484 //		buf.append('\t');
    485 //		buf.append(numCharsMatched);
    486 //		buf.append('\t');
    487 //		buf.append(numHiddenCharsMatched);
    488 //		buf.append('\t');
    489 //		buf.append(numberReportedErrors);
    490 //		buf.append('\t');
    491 //		buf.append(numMemoizationCacheHits);
    492 //		buf.append('\t');
    493 //		buf.append(numMemoizationCacheMisses);
    494 //		buf.append('\t');
    495 //		buf.append(numGuessingRuleInvocations);
    496 //		buf.append('\t');
    497 //		buf.append(numMemoizationCacheEntries);
    498 		return buf.toString();
    499 	}
    500 
    501 	public String toString() {
    502 		return toString(getReport());
    503 	}
    504 
    505 	public ProfileStats getReport() {
    506 //		TokenStream input = parser.getTokenStream();
    507 //		for (int i=0; i<input.size()&& lastRealTokenTouchedInDecision !=null&&i<= lastRealTokenTouchedInDecision.getTokenIndex(); i++) {
    508 //			Token t = input.get(i);
    509 //			if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) {
    510 //				stats.numHiddenTokens++;
    511 //				stats.numHiddenCharsMatched += t.getText().length();
    512 //			}
    513 //		}
    514 		stats.Version = Version;
    515 		stats.name = parser.getClass().getName();
    516 		stats.numUniqueRulesInvoked = uniqueRules.size();
    517 		//stats.numCharsMatched = lastTokenConsumed.getStopIndex() + 1;
    518 		return stats;
    519 	}
    520 
    521 	public DoubleKeyMap getDecisionStats() {
    522 		return decisions;
    523 	}
    524 
    525 	public List getDecisionEvents() {
    526 		return decisionEvents;
    527 	}
    528 
    529 	public static String toString(ProfileStats stats) {
    530 		StringBuffer buf = new StringBuffer();
    531 		buf.append("ANTLR Runtime Report; Profile Version ");
    532 		buf.append(stats.Version);
    533 		buf.append(newline);
    534 		buf.append("parser name ");
    535 		buf.append(stats.name);
    536 		buf.append(newline);
    537 		buf.append("Number of rule invocations ");
    538 		buf.append(stats.numRuleInvocations);
    539 		buf.append(newline);
    540 		buf.append("Number of unique rules visited ");
    541 		buf.append(stats.numUniqueRulesInvoked);
    542 		buf.append(newline);
    543 		buf.append("Number of decision events ");
    544 		buf.append(stats.numDecisionEvents);
    545 		buf.append(newline);
    546 		buf.append("Overall average k per decision event ");
    547 		buf.append(stats.avgkPerDecisionEvent);
    548 		buf.append(newline);
    549 		buf.append("Number of backtracking occurrences (can be multiple per decision) ");
    550 		buf.append(stats.numBacktrackOccurrences);
    551 		buf.append(newline);
    552 		buf.append("Overall average k per decision event that backtracks ");
    553 		buf.append(stats.avgkPerBacktrackingDecisionEvent);
    554 		buf.append(newline);
    555 		buf.append("Number of rule invocations while backtracking ");
    556 		buf.append(stats.numGuessingRuleInvocations);
    557 		buf.append(newline);
    558 		buf.append("num decisions that potentially backtrack ");
    559 		buf.append(stats.numDecisionsThatPotentiallyBacktrack);
    560 		buf.append(newline);
    561 		buf.append("num decisions that do backtrack ");
    562 		buf.append(stats.numDecisionsThatDoBacktrack);
    563 		buf.append(newline);
    564 		buf.append("num decisions that potentially backtrack but don't ");
    565 		buf.append(stats.numDecisionsThatPotentiallyBacktrack - stats.numDecisionsThatDoBacktrack);
    566 		buf.append(newline);
    567 		buf.append("average % of time a potentially backtracking decision backtracks ");
    568 		buf.append(stats.averageDecisionPercentBacktracks);
    569 		buf.append(newline);
    570 		buf.append("num unique decisions covered ");
    571 		buf.append(stats.numDecisionsCovered);
    572 		buf.append(newline);
    573 		buf.append("max rule invocation nesting depth ");
    574 		buf.append(stats.maxRuleInvocationDepth);
    575 		buf.append(newline);
    576 
    577 //		buf.append("number of fixed lookahead decisions ");
    578 //		buf.append();
    579 //		buf.append('\n');
    580 //		buf.append("min lookahead used in a fixed lookahead decision ");
    581 //		buf.append();
    582 //		buf.append('\n');
    583 //		buf.append("max lookahead used in a fixed lookahead decision ");
    584 //		buf.append();
    585 //		buf.append('\n');
    586 //		buf.append("average lookahead depth used in fixed lookahead decisions ");
    587 //		buf.append();
    588 //		buf.append('\n');
    589 //		buf.append("standard deviation of depth used in fixed lookahead decisions ");
    590 //		buf.append();
    591 //		buf.append('\n');
    592 //		buf.append("number of arbitrary lookahead decisions ");
    593 //		buf.append();
    594 //		buf.append('\n');
    595 //		buf.append("min lookahead used in an arbitrary lookahead decision ");
    596 //		buf.append();
    597 //		buf.append('\n');
    598 //		buf.append("max lookahead used in an arbitrary lookahead decision ");
    599 //		buf.append();
    600 //		buf.append('\n');
    601 //		buf.append("average lookahead depth used in arbitrary lookahead decisions ");
    602 //		buf.append();
    603 //		buf.append('\n');
    604 //		buf.append("standard deviation of depth used in arbitrary lookahead decisions ");
    605 //		buf.append();
    606 //		buf.append('\n');
    607 //		buf.append("number of evaluated syntactic predicates ");
    608 //		buf.append();
    609 //		buf.append('\n');
    610 //		buf.append("min lookahead used in a syntactic predicate ");
    611 //		buf.append();
    612 //		buf.append('\n');
    613 //		buf.append("max lookahead used in a syntactic predicate ");
    614 //		buf.append();
    615 //		buf.append('\n');
    616 //		buf.append("average lookahead depth used in syntactic predicates ");
    617 //		buf.append();
    618 //		buf.append('\n');
    619 //		buf.append("standard deviation of depth used in syntactic predicates ");
    620 //		buf.append();
    621 //		buf.append('\n');
    622 		buf.append("rule memoization cache size ");
    623 		buf.append(stats.numMemoizationCacheEntries);
    624 		buf.append(newline);
    625 		buf.append("number of rule memoization cache hits ");
    626 		buf.append(stats.numMemoizationCacheHits);
    627 		buf.append(newline);
    628 		buf.append("number of rule memoization cache misses ");
    629 		buf.append(stats.numMemoizationCacheMisses);
    630 		buf.append(newline);
    631 //		buf.append("number of evaluated semantic predicates ");
    632 //		buf.append();
    633 //		buf.append(newline);
    634 		buf.append("number of tokens ");
    635 		buf.append(stats.numTokens);
    636 		buf.append(newline);
    637 		buf.append("number of hidden tokens ");
    638 		buf.append(stats.numHiddenTokens);
    639 		buf.append(newline);
    640 		buf.append("number of char ");
    641 		buf.append(stats.numCharsMatched);
    642 		buf.append(newline);
    643 		buf.append("number of hidden char ");
    644 		buf.append(stats.numHiddenCharsMatched);
    645 		buf.append(newline);
    646 		buf.append("number of syntax errors ");
    647 		buf.append(stats.numReportedErrors);
    648 		buf.append(newline);
    649 		return buf.toString();
    650 	}
    651 
    652 	public String getDecisionStatsDump() {
    653 		StringBuffer buf = new StringBuffer();
    654 		buf.append("location");
    655 		buf.append(DATA_SEP);
    656 		buf.append("n");
    657 		buf.append(DATA_SEP);
    658 		buf.append("avgk");
    659 		buf.append(DATA_SEP);
    660 		buf.append("maxk");
    661 		buf.append(DATA_SEP);
    662 		buf.append("synpred");
    663 		buf.append(DATA_SEP);
    664 		buf.append("sempred");
    665 		buf.append(DATA_SEP);
    666 		buf.append("canbacktrack");
    667 		buf.append("\n");
    668 		for (String fileName : decisions.keySet()) {
    669 			for (int d : decisions.keySet(fileName)) {
    670 				DecisionDescriptor s = decisions.get(fileName, d);
    671 				buf.append(s.decision);
    672 				buf.append("@");
    673 				buf.append(locationDescription(s.fileName,s.ruleName,s.line,s.pos)); // decision number
    674 				buf.append(DATA_SEP);
    675 				buf.append(s.n);
    676 				buf.append(DATA_SEP);
    677 				buf.append(String.format("%.2f",s.avgk));
    678 				buf.append(DATA_SEP);
    679 				buf.append(s.maxk);
    680 				buf.append(DATA_SEP);
    681 				buf.append(s.numBacktrackOccurrences);
    682 				buf.append(DATA_SEP);
    683 				buf.append(s.numSemPredEvals);
    684 				buf.append(DATA_SEP);
    685 				buf.append(s.couldBacktrack ?"1":"0");
    686 				buf.append(newline);
    687 			}
    688 		}
    689 		return buf.toString();
    690 	}
    691 
    692 	protected int[] trim(int[] X, int n) {
    693 		if ( n<X.length ) {
    694 			int[] trimmed = new int[n];
    695 			System.arraycopy(X,0,trimmed,0,n);
    696 			X = trimmed;
    697 		}
    698 		return X;
    699 	}
    700 
    701 	protected int[] toArray(List a) {
    702 		int[] x = new int[a.size()];
    703 		for (int i = 0; i < a.size(); i++) {
    704 			Integer I = (Integer) a.get(i);
    705 			x[i] = I.intValue();
    706 		}
    707 		return x;
    708 	}
    709 
    710 	/** Get num hidden tokens between i..j inclusive */
    711 	public int getNumberOfHiddenTokens(int i, int j) {
    712 		int n = 0;
    713 		TokenStream input = parser.getTokenStream();
    714 		for (int ti = i; ti<input.size() && ti <= j; ti++) {
    715 			Token t = input.get(ti);
    716 			if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) {
    717 				n++;
    718 			}
    719 		}
    720 		return n;
    721 	}
    722 
    723 	protected String locationDescription() {
    724 		return locationDescription(
    725 			currentGrammarFileName.peek(),
    726 			currentRuleName.peek(),
    727 			currentLine.peek(),
    728 			currentPos.peek());
    729 	}
    730 
    731 	protected String locationDescription(String file, String rule, int line, int pos) {
    732 		return file+":"+line+":"+pos+"(" + rule + ")";
    733 	}
    734 }
    735