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