• 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();
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 
Profiler()148 	public Profiler() {
149 	}
150 
Profiler(DebugParser parser)151 	public Profiler(DebugParser parser) {
152 		this.parser = parser;
153 	}
154 
enterRule(String grammarFileName, String ruleName)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 
exitRule(String grammarFileName, String ruleName)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 	 */
examineRuleMemoization(IntStream input, int ruleIndex, int stopIndex, String ruleName)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 */
memoize(IntStream input, int ruleIndex, int ruleStartIndex, String ruleName)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
location(int line, int pos)208 	public void location(int line, int pos) {
209 		currentLine.push(line);
210 		currentPos.push(pos);
211 	}
212 
enterDecision(int decisionNumber, boolean couldBacktrack)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 
exitDecision(int decisionNumber)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 
consumeToken(Token token)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 	 */
inDecision()282 	public boolean inDecision() {
283 		return decisionStack.size()>0;
284 	}
285 
consumeHiddenToken(Token token)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 	 */
LT(int i, Token t)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 	 */
beginBacktrack(int level)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 */
endBacktrack(int level, boolean successful)350 	public void endBacktrack(int level, boolean successful) {
351 		if (dump) System.out.println("exit backtrack "+level+": "+successful);
352 		backtrackDepth--;
353 	}
354 
355 	@Override
mark(int i)356 	public void mark(int i) {
357 		if (dump) System.out.println("mark "+i);
358 	}
359 
360 	@Override
rewind(int i)361 	public void rewind(int i) {
362 		if (dump) System.out.println("rewind "+i);
363 	}
364 
365 	@Override
rewind()366 	public void rewind() {
367 		if (dump) System.out.println("rewind");
368 	}
369 
370 
371 
currentDecision()372 	protected DecisionEvent currentDecision() {
373 		return decisionStack.peek();
374 	}
375 
recognitionException(RecognitionException e)376 	public void recognitionException(RecognitionException e) {
377 		stats.numReportedErrors++;
378 	}
379 
semanticPredicate(boolean result, String predicate)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 
terminate()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 
setParser(DebugParser parser)433 	public void setParser(DebugParser parser) {
434 		this.parser = parser;
435 	}
436 
437 	// R E P O R T I N G
438 
toNotifyString()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 
toString()501 	public String toString() {
502 		return toString(getReport());
503 	}
504 
getReport()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 
getDecisionStats()521 	public DoubleKeyMap getDecisionStats() {
522 		return decisions;
523 	}
524 
getDecisionEvents()525 	public List getDecisionEvents() {
526 		return decisionEvents;
527 	}
528 
toString(ProfileStats stats)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 
getDecisionStatsDump()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 
trim(int[] X, int n)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 
toArray(List a)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 */
getNumberOfHiddenTokens(int i, int j)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 
locationDescription()723 	protected String locationDescription() {
724 		return locationDescription(
725 			currentGrammarFileName.peek(),
726 			currentRuleName.peek(),
727 			currentLine.peek(),
728 			currentPos.peek());
729 	}
730 
locationDescription(String file, String rule, int line, int pos)731 	protected String locationDescription(String file, String rule, int line, int pos) {
732 		return file+":"+line+":"+pos+"(" + rule + ")";
733 	}
734 }
735