• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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<String>();
131 	protected Stack<String> currentRuleName = new Stack<String>();
132 	protected Stack<Integer> currentLine = new Stack<Integer>();
133 	protected Stack<Integer> currentPos = new Stack<Integer>();
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 
Profiler()148 	public Profiler() {
149 	}
150 
Profiler(DebugParser parser)151 	public Profiler(DebugParser parser) {
152 		this.parser = parser;
153 	}
154 
155 	@Override
enterRule(String grammarFileName, String ruleName)156 	public void enterRule(String grammarFileName, String ruleName) {
157 //		System.out.println("enterRule "+grammarFileName+":"+ruleName);
158 		ruleLevel++;
159 		stats.numRuleInvocations++;
160 		uniqueRules.add(grammarFileName+":"+ruleName);
161 		stats.maxRuleInvocationDepth = Math.max(stats.maxRuleInvocationDepth, ruleLevel);
162 		currentGrammarFileName.push( grammarFileName );
163 		currentRuleName.push( ruleName );
164 	}
165 
166 	@Override
exitRule(String grammarFileName, String ruleName)167 	public void exitRule(String grammarFileName, String ruleName) {
168 		ruleLevel--;
169 		currentGrammarFileName.pop();
170 		currentRuleName.pop();
171 	}
172 
173 	/** Track memoization; this is not part of standard debug interface
174 	 *  but is triggered by profiling.  Code gen inserts an override
175 	 *  for this method in the recognizer, which triggers this method.
176 	 *  Called from alreadyParsedRule().
177 	 */
examineRuleMemoization(IntStream input, int ruleIndex, int stopIndex, String ruleName)178 	public void examineRuleMemoization(IntStream input,
179 									   int ruleIndex,
180 									   int stopIndex, // index or MEMO_RULE_UNKNOWN...
181 									   String ruleName)
182 	{
183 		if (dump) System.out.println("examine memo "+ruleName+" at "+input.index()+": "+stopIndex);
184 		if ( stopIndex==BaseRecognizer.MEMO_RULE_UNKNOWN ) {
185 			//System.out.println("rule "+ruleIndex+" missed @ "+input.index());
186 			stats.numMemoizationCacheMisses++;
187 			stats.numGuessingRuleInvocations++; // we'll have to enter
188 			currentDecision().numMemoizationCacheMisses++;
189 		}
190 		else {
191 			// regardless of rule success/failure, if in cache, we have a cache hit
192 			//System.out.println("rule "+ruleIndex+" hit @ "+input.index());
193 			stats.numMemoizationCacheHits++;
194 			currentDecision().numMemoizationCacheHits++;
195 		}
196 	}
197 
198 	/** Warning: doesn't track success/failure, just unique recording event */
memoize(IntStream input, int ruleIndex, int ruleStartIndex, String ruleName)199 	public void memoize(IntStream input,
200 						int ruleIndex,
201 						int ruleStartIndex,
202 						String ruleName)
203 	{
204 		// count how many entries go into table
205 		if (dump) System.out.println("memoize "+ruleName);
206 		stats.numMemoizationCacheEntries++;
207 	}
208 
209 	@Override
location(int line, int pos)210 	public void location(int line, int pos) {
211 		currentLine.push(line);
212 		currentPos.push(pos);
213 	}
214 
215 	@Override
enterDecision(int decisionNumber, boolean couldBacktrack)216 	public void enterDecision(int decisionNumber, boolean couldBacktrack) {
217 		lastRealTokenTouchedInDecision = null;
218 		stats.numDecisionEvents++;
219 		int startingLookaheadIndex = parser.getTokenStream().index();
220 		TokenStream input = parser.getTokenStream();
221 		if ( dump ) System.out.println("enterDecision canBacktrack="+couldBacktrack+" "+ decisionNumber +
222 						   " backtrack depth " + backtrackDepth +
223 						   " @ " + input.get(input.index()) +
224 						   " rule " +locationDescription());
225 		String g = currentGrammarFileName.peek();
226 		DecisionDescriptor descriptor = decisions.get(g, decisionNumber);
227 		if ( descriptor == null ) {
228 			descriptor = new DecisionDescriptor();
229 			decisions.put(g, decisionNumber, descriptor);
230 			descriptor.decision = decisionNumber;
231 			descriptor.fileName = currentGrammarFileName.peek();
232 			descriptor.ruleName = currentRuleName.peek();
233 			descriptor.line = currentLine.peek();
234 			descriptor.pos = currentPos.peek();
235 			descriptor.couldBacktrack = couldBacktrack;
236 		}
237 		descriptor.n++;
238 
239 		DecisionEvent d = new DecisionEvent();
240 		decisionStack.push(d);
241 		d.decision = descriptor;
242 		d.startTime = System.currentTimeMillis();
243 		d.startIndex = startingLookaheadIndex;
244 	}
245 
246 	@Override
exitDecision(int decisionNumber)247 	public void exitDecision(int decisionNumber) {
248 		DecisionEvent d = decisionStack.pop();
249 		d.stopTime = System.currentTimeMillis();
250 
251 		int lastTokenIndex = lastRealTokenTouchedInDecision.getTokenIndex();
252 		int numHidden = getNumberOfHiddenTokens(d.startIndex, lastTokenIndex);
253 		int depth = lastTokenIndex - d.startIndex - numHidden + 1; // +1 counts consuming start token as 1
254 		d.k = depth;
255 		d.decision.maxk = Math.max(d.decision.maxk, depth);
256 
257 		if (dump) System.out.println("exitDecision "+decisionNumber+" in "+d.decision.ruleName+
258 						   " lookahead "+d.k +" max token "+lastRealTokenTouchedInDecision);
259 		decisionEvents.add(d); // done with decision; track all
260 	}
261 
262 	@Override
consumeToken(Token token)263 	public void consumeToken(Token token) {
264 		if (dump) System.out.println("consume token "+token);
265 		if ( !inDecision() ) {
266 			stats.numTokens++;
267 			return;
268 		}
269 		if ( lastRealTokenTouchedInDecision==null ||
270 			 lastRealTokenTouchedInDecision.getTokenIndex() < token.getTokenIndex() )
271 		{
272 			lastRealTokenTouchedInDecision = token;
273 		}
274 		DecisionEvent d = currentDecision();
275 		// compute lookahead depth
276 		int thisRefIndex = token.getTokenIndex();
277 		int numHidden = getNumberOfHiddenTokens(d.startIndex, thisRefIndex);
278 		int depth = thisRefIndex - d.startIndex - numHidden + 1; // +1 counts consuming start token as 1
279 		//d.maxk = Math.max(d.maxk, depth);
280 		if (dump) System.out.println("consume "+thisRefIndex+" "+depth+" tokens ahead in "+
281 						   d.decision.ruleName+"-"+d.decision.decision+" start index "+d.startIndex);
282 	}
283 
284 	/** The parser is in a decision if the decision depth &gt; 0.  This
285 	 *  works for backtracking also, which can have nested decisions.
286 	 */
inDecision()287 	public boolean inDecision() {
288 		return decisionStack.size()>0;
289 	}
290 
291 	@Override
consumeHiddenToken(Token token)292 	public void consumeHiddenToken(Token token) {
293 		//System.out.println("consume hidden token "+token);
294 		if ( !inDecision() ) stats.numHiddenTokens++;
295 	}
296 
297 	/** Track refs to lookahead if in a fixed/nonfixed decision.
298 	 */
299 	@Override
LT(int i, Token t)300 	public void LT(int i, Token t) {
301 		if ( inDecision() && i>0 ) {
302 			DecisionEvent d = currentDecision();
303 			if (dump) System.out.println("LT("+i+")="+t+" index "+t.getTokenIndex()+" relative to "+d.decision.ruleName+"-"+
304 							   d.decision.decision+" start index "+d.startIndex);
305 			if ( lastRealTokenTouchedInDecision==null ||
306 				 lastRealTokenTouchedInDecision.getTokenIndex() < t.getTokenIndex() )
307 			{
308 				lastRealTokenTouchedInDecision = t;
309 				if (dump) System.out.println("set last token "+lastRealTokenTouchedInDecision);
310 			}
311 			// get starting index off stack
312 //			int stackTop = lookaheadStack.size()-1;
313 //			Integer startingIndex = (Integer)lookaheadStack.get(stackTop);
314 //			// compute lookahead depth
315 //			int thisRefIndex = parser.getTokenStream().index();
316 //			int numHidden =
317 //				getNumberOfHiddenTokens(startingIndex.intValue(), thisRefIndex);
318 //			int depth = i + thisRefIndex - startingIndex.intValue() - numHidden;
319 //			/*
320 //			System.out.println("LT("+i+") @ index "+thisRefIndex+" is depth "+depth+
321 //				" max is "+maxLookaheadInCurrentDecision);
322 //			*/
323 //			if ( depth>maxLookaheadInCurrentDecision ) {
324 //				maxLookaheadInCurrentDecision = depth;
325 //			}
326 //			d.maxk = currentDecision()/
327 		}
328 	}
329 
330 	/** Track backtracking decisions.  You'll see a fixed or cyclic decision
331 	 *  and then a backtrack.
332 	 *
333 	 * 		enter rule
334 	 * 		...
335 	 * 		enter decision
336 	 * 		LA and possibly consumes (for cyclic DFAs)
337 	 * 		begin backtrack level
338 	 * 		mark m
339 	 * 		rewind m
340 	 * 		end backtrack level, success
341 	 * 		exit decision
342 	 * 		...
343 	 * 		exit rule
344 	 */
345 	@Override
beginBacktrack(int level)346 	public void beginBacktrack(int level) {
347 		if (dump) System.out.println("enter backtrack "+level);
348 		backtrackDepth++;
349 		DecisionEvent e = currentDecision();
350 		if ( e.decision.couldBacktrack ) {
351 			stats.numBacktrackOccurrences++;
352 			e.decision.numBacktrackOccurrences++;
353 			e.backtracks = true;
354 		}
355 	}
356 
357 	/** Successful or not, track how much lookahead synpreds use */
358 	@Override
endBacktrack(int level, boolean successful)359 	public void endBacktrack(int level, boolean successful) {
360 		if (dump) System.out.println("exit backtrack "+level+": "+successful);
361 		backtrackDepth--;
362 	}
363 
364 	@Override
mark(int i)365 	public void mark(int i) {
366 		if (dump) System.out.println("mark "+i);
367 	}
368 
369 	@Override
rewind(int i)370 	public void rewind(int i) {
371 		if (dump) System.out.println("rewind "+i);
372 	}
373 
374 	@Override
rewind()375 	public void rewind() {
376 		if (dump) System.out.println("rewind");
377 	}
378 
379 
380 
currentDecision()381 	protected DecisionEvent currentDecision() {
382 		return decisionStack.peek();
383 	}
384 
385 	@Override
recognitionException(RecognitionException e)386 	public void recognitionException(RecognitionException e) {
387 		stats.numReportedErrors++;
388 	}
389 
390 	@Override
semanticPredicate(boolean result, String predicate)391 	public void semanticPredicate(boolean result, String predicate) {
392 		stats.numSemanticPredicates++;
393 		if ( inDecision() ) {
394 			DecisionEvent d = currentDecision();
395 			d.evalSemPred = true;
396 			d.decision.numSemPredEvals++;
397 			if (dump) System.out.println("eval "+predicate+" in "+d.decision.ruleName+"-"+
398 							   d.decision.decision);
399 		}
400 	}
401 
402 	@Override
terminate()403 	public void terminate() {
404 		for (DecisionEvent e : decisionEvents) {
405 			//System.out.println("decision "+e.decision.decision+": k="+e.k);
406 			e.decision.avgk += e.k;
407 			stats.avgkPerDecisionEvent += e.k;
408 			if ( e.backtracks ) { // doesn't count gated syn preds on DFA edges
409 				stats.avgkPerBacktrackingDecisionEvent += e.k;
410 			}
411 		}
412 		stats.averageDecisionPercentBacktracks = 0.0f;
413 		for (DecisionDescriptor d : decisions.values()) {
414 			stats.numDecisionsCovered++;
415 			d.avgk /= (double)d.n;
416 			if ( d.couldBacktrack ) {
417 				stats.numDecisionsThatPotentiallyBacktrack++;
418 				float percentBacktracks = d.numBacktrackOccurrences / (float)d.n;
419 				//System.out.println("dec "+d.decision+" backtracks "+percentBacktracks*100+"%");
420 				stats.averageDecisionPercentBacktracks += percentBacktracks;
421 			}
422 			// ignore rules that backtrack along gated DFA edges
423 			if ( d.numBacktrackOccurrences > 0 ) {
424 				stats.numDecisionsThatDoBacktrack++;
425 			}
426 		}
427 		stats.averageDecisionPercentBacktracks /= stats.numDecisionsThatPotentiallyBacktrack;
428 		stats.averageDecisionPercentBacktracks *= 100; // it's a percentage
429 		stats.avgkPerDecisionEvent /= stats.numDecisionEvents;
430 		stats.avgkPerBacktrackingDecisionEvent /= (double)stats.numBacktrackOccurrences;
431 
432 		System.err.println(toString());
433 		System.err.println(getDecisionStatsDump());
434 
435 //		String stats = toNotifyString();
436 //		try {
437 //			Stats.writeReport(RUNTIME_STATS_FILENAME,stats);
438 //		}
439 //		catch (IOException ioe) {
440 //			System.err.println(ioe);
441 //			ioe.printStackTrace(System.err);
442 //		}
443 	}
444 
setParser(DebugParser parser)445 	public void setParser(DebugParser parser) {
446 		this.parser = parser;
447 	}
448 
449 	// R E P O R T I N G
450 
toNotifyString()451 	public String toNotifyString() {
452 		StringBuilder buf = new StringBuilder();
453 		buf.append(Version);
454 		buf.append('\t');
455 		buf.append(parser.getClass().getName());
456 //		buf.append('\t');
457 //		buf.append(numRuleInvocations);
458 //		buf.append('\t');
459 //		buf.append(maxRuleInvocationDepth);
460 //		buf.append('\t');
461 //		buf.append(numFixedDecisions);
462 //		buf.append('\t');
463 //		buf.append(Stats.min(decisionMaxFixedLookaheads));
464 //		buf.append('\t');
465 //		buf.append(Stats.max(decisionMaxFixedLookaheads));
466 //		buf.append('\t');
467 //		buf.append(Stats.avg(decisionMaxFixedLookaheads));
468 //		buf.append('\t');
469 //		buf.append(Stats.stddev(decisionMaxFixedLookaheads));
470 //		buf.append('\t');
471 //		buf.append(numCyclicDecisions);
472 //		buf.append('\t');
473 //		buf.append(Stats.min(decisionMaxCyclicLookaheads));
474 //		buf.append('\t');
475 //		buf.append(Stats.max(decisionMaxCyclicLookaheads));
476 //		buf.append('\t');
477 //		buf.append(Stats.avg(decisionMaxCyclicLookaheads));
478 //		buf.append('\t');
479 //		buf.append(Stats.stddev(decisionMaxCyclicLookaheads));
480 //		buf.append('\t');
481 //		buf.append(numBacktrackDecisions);
482 //		buf.append('\t');
483 //		buf.append(Stats.min(toArray(decisionMaxSynPredLookaheads)));
484 //		buf.append('\t');
485 //		buf.append(Stats.max(toArray(decisionMaxSynPredLookaheads)));
486 //		buf.append('\t');
487 //		buf.append(Stats.avg(toArray(decisionMaxSynPredLookaheads)));
488 //		buf.append('\t');
489 //		buf.append(Stats.stddev(toArray(decisionMaxSynPredLookaheads)));
490 //		buf.append('\t');
491 //		buf.append(numSemanticPredicates);
492 //		buf.append('\t');
493 //		buf.append(parser.getTokenStream().size());
494 //		buf.append('\t');
495 //		buf.append(numHiddenTokens);
496 //		buf.append('\t');
497 //		buf.append(numCharsMatched);
498 //		buf.append('\t');
499 //		buf.append(numHiddenCharsMatched);
500 //		buf.append('\t');
501 //		buf.append(numberReportedErrors);
502 //		buf.append('\t');
503 //		buf.append(numMemoizationCacheHits);
504 //		buf.append('\t');
505 //		buf.append(numMemoizationCacheMisses);
506 //		buf.append('\t');
507 //		buf.append(numGuessingRuleInvocations);
508 //		buf.append('\t');
509 //		buf.append(numMemoizationCacheEntries);
510 		return buf.toString();
511 	}
512 
513 	@Override
toString()514 	public String toString() {
515 		return toString(getReport());
516 	}
517 
getReport()518 	public ProfileStats getReport() {
519 //		TokenStream input = parser.getTokenStream();
520 //		for (int i=0; i<input.size()&& lastRealTokenTouchedInDecision !=null&&i<= lastRealTokenTouchedInDecision.getTokenIndex(); i++) {
521 //			Token t = input.get(i);
522 //			if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) {
523 //				stats.numHiddenTokens++;
524 //				stats.numHiddenCharsMatched += t.getText().length();
525 //			}
526 //		}
527 		stats.Version = Version;
528 		stats.name = parser.getClass().getName();
529 		stats.numUniqueRulesInvoked = uniqueRules.size();
530 		//stats.numCharsMatched = lastTokenConsumed.getStopIndex() + 1;
531 		return stats;
532 	}
533 
getDecisionStats()534 	public DoubleKeyMap<String, Integer, DecisionDescriptor> getDecisionStats() {
535 		return decisions;
536 	}
537 
getDecisionEvents()538 	public List<DecisionEvent> getDecisionEvents() {
539 		return decisionEvents;
540 	}
541 
toString(ProfileStats stats)542 	public static String toString(ProfileStats stats) {
543 		StringBuilder buf = new StringBuilder();
544 		buf.append("ANTLR Runtime Report; Profile Version ");
545 		buf.append(stats.Version);
546 		buf.append(newline);
547 		buf.append("parser name ");
548 		buf.append(stats.name);
549 		buf.append(newline);
550 		buf.append("Number of rule invocations ");
551 		buf.append(stats.numRuleInvocations);
552 		buf.append(newline);
553 		buf.append("Number of unique rules visited ");
554 		buf.append(stats.numUniqueRulesInvoked);
555 		buf.append(newline);
556 		buf.append("Number of decision events ");
557 		buf.append(stats.numDecisionEvents);
558 		buf.append(newline);
559 		buf.append("Overall average k per decision event ");
560 		buf.append(stats.avgkPerDecisionEvent);
561 		buf.append(newline);
562 		buf.append("Number of backtracking occurrences (can be multiple per decision) ");
563 		buf.append(stats.numBacktrackOccurrences);
564 		buf.append(newline);
565 		buf.append("Overall average k per decision event that backtracks ");
566 		buf.append(stats.avgkPerBacktrackingDecisionEvent);
567 		buf.append(newline);
568 		buf.append("Number of rule invocations while backtracking ");
569 		buf.append(stats.numGuessingRuleInvocations);
570 		buf.append(newline);
571 		buf.append("num decisions that potentially backtrack ");
572 		buf.append(stats.numDecisionsThatPotentiallyBacktrack);
573 		buf.append(newline);
574 		buf.append("num decisions that do backtrack ");
575 		buf.append(stats.numDecisionsThatDoBacktrack);
576 		buf.append(newline);
577 		buf.append("num decisions that potentially backtrack but don't ");
578 		buf.append(stats.numDecisionsThatPotentiallyBacktrack - stats.numDecisionsThatDoBacktrack);
579 		buf.append(newline);
580 		buf.append("average % of time a potentially backtracking decision backtracks ");
581 		buf.append(stats.averageDecisionPercentBacktracks);
582 		buf.append(newline);
583 		buf.append("num unique decisions covered ");
584 		buf.append(stats.numDecisionsCovered);
585 		buf.append(newline);
586 		buf.append("max rule invocation nesting depth ");
587 		buf.append(stats.maxRuleInvocationDepth);
588 		buf.append(newline);
589 
590 //		buf.append("number of fixed lookahead decisions ");
591 //		buf.append();
592 //		buf.append('\n');
593 //		buf.append("min lookahead used in a fixed lookahead decision ");
594 //		buf.append();
595 //		buf.append('\n');
596 //		buf.append("max lookahead used in a fixed lookahead decision ");
597 //		buf.append();
598 //		buf.append('\n');
599 //		buf.append("average lookahead depth used in fixed lookahead decisions ");
600 //		buf.append();
601 //		buf.append('\n');
602 //		buf.append("standard deviation of depth used in fixed lookahead decisions ");
603 //		buf.append();
604 //		buf.append('\n');
605 //		buf.append("number of arbitrary lookahead decisions ");
606 //		buf.append();
607 //		buf.append('\n');
608 //		buf.append("min lookahead used in an arbitrary lookahead decision ");
609 //		buf.append();
610 //		buf.append('\n');
611 //		buf.append("max lookahead used in an arbitrary lookahead decision ");
612 //		buf.append();
613 //		buf.append('\n');
614 //		buf.append("average lookahead depth used in arbitrary lookahead decisions ");
615 //		buf.append();
616 //		buf.append('\n');
617 //		buf.append("standard deviation of depth used in arbitrary lookahead decisions ");
618 //		buf.append();
619 //		buf.append('\n');
620 //		buf.append("number of evaluated syntactic predicates ");
621 //		buf.append();
622 //		buf.append('\n');
623 //		buf.append("min lookahead used in a syntactic predicate ");
624 //		buf.append();
625 //		buf.append('\n');
626 //		buf.append("max lookahead used in a syntactic predicate ");
627 //		buf.append();
628 //		buf.append('\n');
629 //		buf.append("average lookahead depth used in syntactic predicates ");
630 //		buf.append();
631 //		buf.append('\n');
632 //		buf.append("standard deviation of depth used in syntactic predicates ");
633 //		buf.append();
634 //		buf.append('\n');
635 		buf.append("rule memoization cache size ");
636 		buf.append(stats.numMemoizationCacheEntries);
637 		buf.append(newline);
638 		buf.append("number of rule memoization cache hits ");
639 		buf.append(stats.numMemoizationCacheHits);
640 		buf.append(newline);
641 		buf.append("number of rule memoization cache misses ");
642 		buf.append(stats.numMemoizationCacheMisses);
643 		buf.append(newline);
644 //		buf.append("number of evaluated semantic predicates ");
645 //		buf.append();
646 //		buf.append(newline);
647 		buf.append("number of tokens ");
648 		buf.append(stats.numTokens);
649 		buf.append(newline);
650 		buf.append("number of hidden tokens ");
651 		buf.append(stats.numHiddenTokens);
652 		buf.append(newline);
653 		buf.append("number of char ");
654 		buf.append(stats.numCharsMatched);
655 		buf.append(newline);
656 		buf.append("number of hidden char ");
657 		buf.append(stats.numHiddenCharsMatched);
658 		buf.append(newline);
659 		buf.append("number of syntax errors ");
660 		buf.append(stats.numReportedErrors);
661 		buf.append(newline);
662 		return buf.toString();
663 	}
664 
getDecisionStatsDump()665 	public String getDecisionStatsDump() {
666 		StringBuilder buf = new StringBuilder();
667 		buf.append("location");
668 		buf.append(DATA_SEP);
669 		buf.append("n");
670 		buf.append(DATA_SEP);
671 		buf.append("avgk");
672 		buf.append(DATA_SEP);
673 		buf.append("maxk");
674 		buf.append(DATA_SEP);
675 		buf.append("synpred");
676 		buf.append(DATA_SEP);
677 		buf.append("sempred");
678 		buf.append(DATA_SEP);
679 		buf.append("canbacktrack");
680 		buf.append("\n");
681 		for (String fileName : decisions.keySet()) {
682 			for (int d : decisions.keySet(fileName)) {
683 				DecisionDescriptor s = decisions.get(fileName, d);
684 				buf.append(s.decision);
685 				buf.append("@");
686 				buf.append(locationDescription(s.fileName,s.ruleName,s.line,s.pos)); // decision number
687 				buf.append(DATA_SEP);
688 				buf.append(s.n);
689 				buf.append(DATA_SEP);
690 				buf.append(String.format("%.2f",s.avgk));
691 				buf.append(DATA_SEP);
692 				buf.append(s.maxk);
693 				buf.append(DATA_SEP);
694 				buf.append(s.numBacktrackOccurrences);
695 				buf.append(DATA_SEP);
696 				buf.append(s.numSemPredEvals);
697 				buf.append(DATA_SEP);
698 				buf.append(s.couldBacktrack ?"1":"0");
699 				buf.append(newline);
700 			}
701 		}
702 		return buf.toString();
703 	}
704 
trim(int[] X, int n)705 	protected int[] trim(int[] X, int n) {
706 		if ( n<X.length ) {
707 			int[] trimmed = new int[n];
708 			System.arraycopy(X,0,trimmed,0,n);
709 			X = trimmed;
710 		}
711 		return X;
712 	}
713 
toArray(List<Integer> a)714 	protected int[] toArray(List<Integer> a) {
715 		int[] x = new int[a.size()];
716 		for (int i = 0; i < a.size(); i++) {
717 			Integer I = a.get(i);
718 			x[i] = I;
719 		}
720 		return x;
721 	}
722 
723 	/** Get num hidden tokens between i..j inclusive */
getNumberOfHiddenTokens(int i, int j)724 	public int getNumberOfHiddenTokens(int i, int j) {
725 		int n = 0;
726 		TokenStream input = parser.getTokenStream();
727 		for (int ti = i; ti<input.size() && ti <= j; ti++) {
728 			Token t = input.get(ti);
729 			if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) {
730 				n++;
731 			}
732 		}
733 		return n;
734 	}
735 
locationDescription()736 	protected String locationDescription() {
737 		return locationDescription(
738 			currentGrammarFileName.peek(),
739 			currentRuleName.peek(),
740 			currentLine.peek(),
741 			currentPos.peek());
742 	}
743 
locationDescription(String file, String rule, int line, int pos)744 	protected String locationDescription(String file, String rule, int line, int pos) {
745 		return file+":"+line+":"+pos+"(" + rule + ")";
746 	}
747 }
748