0
# Debug Support
1
2
Debugging, profiling, and tracing tools for parser development and optimization. The debug package provides comprehensive instrumentation for understanding parser behavior and performance characteristics.
3
4
## Capabilities
5
6
### Debug Event Listener
7
8
Interface for receiving detailed parsing events during execution.
9
10
```java { .api }
11
/**
12
* Interface for debug event handling
13
*/
14
public interface DebugEventListener {
15
/** The parser has just entered rule ruleName */
16
public void enterRule(String grammarFileName, String ruleName);
17
18
/** Because rules can have lots of alternatives, we need an event for each.
19
* The default implementation does nothing. The decision number is the decision
20
* in the DFA for the decision state.
21
*/
22
public void enterAlt(int alt);
23
24
/** The parser has just exited rule ruleName */
25
public void exitRule(String grammarFileName, String ruleName);
26
27
/** The parser has just entered a subrule in the parser such as
28
* the () subrule.
29
*/
30
public void enterSubRule(int decisionNumber);
31
32
/** The parser has just exited a subrule in the parser such as
33
* the () subrule.
34
*/
35
public void exitSubRule(int decisionNumber);
36
37
/** A decision was made about which alt to enter. */
38
public void enterDecision(int decisionNumber, boolean couldBacktrack);
39
40
/** We are done deciding which alt to enter. */
41
public void exitDecision(int decisionNumber);
42
43
/** The parser consumed a token; tell me which one and by which rule
44
* that token matched or what rule invoked nextToken. This is perfect
45
* place to count consumed input or to build trees.
46
*/
47
public void consumeToken(Token token);
48
49
/** Track the progress of consuming tokens during error recovery.
50
* This is useful to highlight the input during debugging and
51
* in error messages.
52
*/
53
public void consumeHiddenToken(Token token);
54
55
/** Input stream is requesting a look ahead the k-th token.
56
* k==1 is the current token. k==0 is invalid.
57
* This event is fired before the next token is actually
58
* computed so you can see in the debugger what could happen
59
* next. This is not an indication that the token will be
60
* consumed next.
61
*/
62
public void LT(int i, Token t);
63
64
/** Announce that the parser has created a nil node */
65
public void nilNode(Object t);
66
67
/** Announce that the parser has created an error node in the tree
68
* such as when you find input that does not match the input expected
69
* via the grammar.
70
*/
71
public void errorNode(Object t);
72
73
/** Announce that the parser has just created node t for rule r. */
74
public void createNode(Object t, Token token);
75
76
/** Announce that the parser has just altered the tree -- usually by
77
* making rule node t the new root of old root r.
78
*/
79
public void createNode(Object t, int tokenType, String text);
80
81
/** Announce that the parser has just altered the tree -- usually by
82
* making rule node t the new root of old root r.
83
*/
84
public void becomeRoot(Object newRoot, Object oldRoot);
85
86
/** Announce that the parser has just added child t as a child of root r. */
87
public void addChild(Object root, Object child);
88
89
/** The parser has set the token boundaries for tree node t from
90
* tokens with indexes start..stop.
91
*/
92
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex);
93
94
/** Alter the type of tree node t to tokenType */
95
public void setType(Object node, int tokenType);
96
97
/** Alter the text of tree node t to tokenText */
98
public void setText(Object node, String tokenText);
99
100
/** We are done parsing; successfully or not. Track time and tell
101
* listeners we are done.
102
*/
103
public void terminate();
104
}
105
```
106
107
### Debug Parser
108
109
Parser wrapper that generates debug events during parsing.
110
111
```java { .api }
112
/**
113
* Parser with debugging capabilities
114
*/
115
public class DebugParser extends Parser {
116
/** Who to notify when events in the parser occur. */
117
protected DebugEventListener dbg = null;
118
119
/** Used to differentiate between fixed lookahead and cyclic DFA decisions
120
* while profiling.
121
*/
122
public boolean isCyclicDecision = false;
123
124
public DebugParser(TokenStream input, DebugEventListener dbg, RecognizerSharedState state);
125
public DebugParser(TokenStream input, RecognizerSharedState state);
126
public DebugParser(TokenStream input, DebugEventListener dbg);
127
128
/** Provide a new debug event listener for this parser. Notify the
129
* input stream too that it should send events to this listener.
130
*/
131
public void setDebugListener(DebugEventListener dbg);
132
133
public List<String> getRuleInvocationStack();
134
135
public void reportError(IOException e);
136
public void reportError(RecognitionException e);
137
138
public void beginResync();
139
public void endResync();
140
141
public void beginBacktrack(int level);
142
public void endBacktrack(int level, boolean successful);
143
}
144
```
145
146
**Usage Examples:**
147
148
```java
149
import org.antlr.runtime.debug.*;
150
import org.antlr.runtime.*;
151
152
// Create debug parser with custom listener
153
MyLexer lexer = new MyLexer(new ANTLRStringStream("x = 42;"));
154
DebugTokenStream tokens = new DebugTokenStream(new CommonTokenStream(lexer), null);
155
DebugEventListener listener = new TraceDebugEventListener();
156
DebugParser parser = new DebugParser(tokens, listener);
157
158
// Set up debugging
159
parser.setDebugListener(listener);
160
tokens.setDebugListener(listener);
161
162
// Parse with debugging enabled
163
try {
164
parser.program();
165
} catch (RecognitionException e) {
166
System.err.println("Parse failed: " + e.getMessage());
167
}
168
```
169
170
### Debug Token Stream
171
172
Token stream wrapper that generates debug events for token access.
173
174
```java { .api }
175
/**
176
* Token stream with debugging support
177
*/
178
public class DebugTokenStream implements TokenStream {
179
protected DebugEventListener dbg;
180
protected TokenStream input;
181
protected int lastMarker;
182
183
public DebugTokenStream(TokenStream input, DebugEventListener dbg);
184
185
public void setDebugListener(DebugEventListener dbg);
186
187
public void consume();
188
public Token get(int i);
189
public Token LT(int k);
190
public int mark();
191
public int index();
192
public void rewind(int marker);
193
public void rewind();
194
public void release(int marker);
195
public void seek(int index);
196
public int size();
197
public TokenSource getTokenSource();
198
public String getSourceName();
199
public String toString(int start, int stop);
200
public String toString(Token start, Token stop);
201
public String toString();
202
203
// Delegate to wrapped stream
204
public int LA(int i);
205
public int range();
206
}
207
```
208
209
**Usage Examples:**
210
211
```java
212
// Debug token stream with custom listener
213
CommonTokenStream baseStream = new CommonTokenStream(lexer);
214
DebugEventListener listener = new MyDebugListener();
215
DebugTokenStream debugStream = new DebugTokenStream(baseStream, listener);
216
217
// Token access generates debug events
218
Token token = debugStream.LT(1); // Fires LT event
219
debugStream.consume(); // Fires consumeToken event
220
221
// Use with debug parser
222
DebugParser parser = new DebugParser(debugStream, listener);
223
```
224
225
### Profiler
226
227
Performance profiler for analyzing parser efficiency and bottlenecks.
228
229
```java { .api }
230
/**
231
* Performance profiling for parsing
232
*/
233
public class Profiler extends BlankDebugEventListener {
234
public static final String DATA_SEP = "\t";
235
public static final String newline = System.getProperty("line.separator");
236
237
/** Because I may change the stats, I need to track that for later
238
* computations to be consistent.
239
*/
240
public static final String Version = "2";
241
public static final String RUNTIME_STATS_FILENAME = "runtime.stats";
242
243
protected Stats stats;
244
245
// all about a specific decision
246
public static class DecisionDescriptor {
247
public String fileName;
248
public String ruleName;
249
public int decision;
250
public int maxk;
251
public boolean couldBacktrack;
252
public boolean cyclic;
253
public boolean greedy;
254
public boolean blockLevel;
255
public boolean isFixed;
256
public int numAlts;
257
258
public String toString();
259
}
260
261
// Profile stats for decisions
262
public static class ProfileStats {
263
public String Version;
264
public String name;
265
public int numRuleInvocations;
266
public int numGuessedEarlyExits;
267
public int numMemoizationCacheMisses;
268
public int numMemoizationCacheHits;
269
public int numBacktrackOccurrences;
270
public List<DecisionEvent> events;
271
public List<DecisionDescriptor> decisions;
272
273
public String toString();
274
public String toNotifyString();
275
}
276
277
public Profiler();
278
public Profiler(Parser parser);
279
280
public void terminate();
281
public void enterDecision(int decisionNumber, boolean couldBacktrack);
282
public void exitDecision(int decisionNumber);
283
public void consumeToken(Token token);
284
public void consumeHiddenToken(Token token);
285
public void nextToken(Token t);
286
public void memoize(IntStream input, int ruleIndex, int ruleStartIndex, boolean success);
287
public void mark(int i);
288
public void rewind(int i);
289
public void rewind();
290
public void beginBacktrack(int level);
291
public void endBacktrack(int level, boolean successful);
292
public void location(int line, int pos);
293
public void recognitionException(RecognitionException e);
294
public void beginResync();
295
public void endResync();
296
public void semanticPredicate(boolean result, String predicate);
297
298
public void setParser(Parser parser);
299
public Parser getParser();
300
301
// Report generation
302
public String getReport();
303
public String getReport(String name);
304
305
public ProfileStats getDecisionProfile();
306
public String toNotifyString();
307
public String toString();
308
}
309
```
310
311
**Usage Examples:**
312
313
```java
314
// Profile parser performance
315
MyLexer lexer = new MyLexer(new ANTLRStringStream(input));
316
CommonTokenStream tokens = new CommonTokenStream(lexer);
317
MyParser parser = new MyParser(tokens);
318
319
// Add profiler
320
Profiler profiler = new Profiler(parser);
321
parser.setDebugListener(profiler);
322
323
// Parse and collect statistics
324
parser.program();
325
326
// Get performance report
327
String report = profiler.getReport();
328
System.out.println("Performance Report:");
329
System.out.println(report);
330
331
// Get decision profile
332
ProfileStats stats = profiler.getDecisionProfile();
333
System.out.println("Decision stats: " + stats.toString());
334
```
335
336
### Tracer
337
338
Simple trace listener that prints parsing events to console.
339
340
```java { .api }
341
/**
342
* Tracing debug event listener
343
*/
344
public class Tracer extends BlankDebugEventListener {
345
public Parser recognizer;
346
347
public Tracer(Parser recognizer);
348
349
public void enterRule(String ruleName);
350
public void exitRule(String ruleName);
351
}
352
353
/**
354
* Debug listener that prints trace information
355
*/
356
public class TraceDebugEventListener extends BlankDebugEventListener {
357
public void enterRule(String grammarFileName, String ruleName);
358
public void exitRule(String grammarFileName, String ruleName);
359
public void enterAlt(int alt);
360
public void enterSubRule(int decisionNumber);
361
public void exitSubRule(int decisionNumber);
362
public void enterDecision(int decisionNumber, boolean couldBacktrack);
363
public void exitDecision(int decisionNumber);
364
public void consumeToken(Token token);
365
public void consumeHiddenToken(Token token);
366
public void LT(int i, Token t);
367
public void nilNode(Object t);
368
public void errorNode(Object t);
369
public void createNode(Object t, Token token);
370
public void createNode(Object t, int tokenType, String text);
371
public void becomeRoot(Object newRoot, Object oldRoot);
372
public void addChild(Object root, Object child);
373
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex);
374
}
375
```
376
377
**Usage Examples:**
378
379
```java
380
// Simple rule tracing
381
MyParser parser = new MyParser(tokens);
382
Tracer tracer = new Tracer(parser);
383
parser.setDebugListener(tracer);
384
385
// This will print rule entry/exit during parsing
386
parser.program();
387
388
// Full event tracing
389
TraceDebugEventListener trace = new TraceDebugEventListener();
390
DebugParser debugParser = new DebugParser(tokens, trace);
391
debugParser.program(); // Prints all parse events
392
```
393
394
### Remote Debugging
395
396
Support for remote debugging sessions via socket communication.
397
398
```java { .api }
399
/**
400
* Socket proxy for debug events
401
*/
402
public class DebugEventSocketProxy extends BlankDebugEventListener {
403
public static final int DEFAULT_DEBUGGER_PORT = 0xBFBF;
404
405
protected int port;
406
protected ServerSocket serverSocket;
407
protected Socket socket;
408
protected PrintWriter out;
409
protected BufferedReader in;
410
411
public DebugEventSocketProxy(Parser recognizer, int port, TreeAdaptor adaptor)
412
throws IOException;
413
414
public void handshake() throws IOException;
415
public void commence();
416
public void terminate();
417
418
protected void transmit(String event);
419
420
public void enterRule(String grammarFileName, String ruleName);
421
public void exitRule(String grammarFileName, String ruleName);
422
public void enterAlt(int alt);
423
public void enterSubRule(int decisionNumber);
424
public void exitSubRule(int decisionNumber);
425
public void enterDecision(int decisionNumber, boolean couldBacktrack);
426
public void exitDecision(int decisionNumber);
427
public void consumeToken(Token token);
428
public void consumeHiddenToken(Token token);
429
public void LT(int i, Token t);
430
public void mark(int marker);
431
public void rewind(int marker);
432
public void rewind();
433
public void beginBacktrack(int level);
434
public void endBacktrack(int level, boolean successful);
435
public void location(int line, int pos);
436
public void recognitionException(RecognitionException e);
437
public void beginResync();
438
public void endResync();
439
public void semanticPredicate(boolean result, String predicate);
440
}
441
442
/**
443
* Remote socket listener for debug events
444
*/
445
public class RemoteDebugEventSocketListener {
446
public static final int MAX_EVENT_ELEMENTS = 8;
447
448
protected String grammarFileName;
449
protected String[] ruleNames;
450
protected ServerSocket serverSocket;
451
protected Socket clientSocket;
452
protected PrintWriter out;
453
protected BufferedReader in;
454
protected boolean sessionStarted = false;
455
456
public RemoteDebugEventSocketListener(String grammarFileName, String[] ruleNames)
457
throws IOException;
458
459
public void start();
460
public void waitForDebuggerToAttach() throws IOException;
461
public void handshake() throws IOException;
462
public void commence();
463
public void terminate();
464
}
465
```
466
467
**Usage Examples:**
468
469
```java
470
// Remote debugging setup
471
try {
472
MyParser parser = new MyParser(tokens);
473
DebugEventSocketProxy proxy = new DebugEventSocketProxy(parser, 0xBFBF, new CommonTreeAdaptor());
474
parser.setDebugListener(proxy);
475
476
System.out.println("Waiting for debugger to attach...");
477
proxy.handshake(); // Wait for debugger connection
478
479
// Parse with remote debugging
480
parser.program();
481
482
} catch (IOException e) {
483
System.err.println("Remote debugging failed: " + e.getMessage());
484
}
485
```
486
487
## Utility Classes
488
489
### Blank Debug Event Listener
490
491
No-op implementation providing a base for selective event handling.
492
493
```java { .api }
494
/**
495
* No-op debug event listener implementation
496
*/
497
public class BlankDebugEventListener implements DebugEventListener {
498
public void enterRule(String grammarFileName, String ruleName) {}
499
public void exitRule(String grammarFileName, String ruleName) {}
500
public void enterAlt(int alt) {}
501
public void enterSubRule(int decisionNumber) {}
502
public void exitSubRule(int decisionNumber) {}
503
public void enterDecision(int decisionNumber, boolean couldBacktrack) {}
504
public void exitDecision(int decisionNumber) {}
505
public void consumeToken(Token token) {}
506
public void consumeHiddenToken(Token token) {}
507
public void LT(int i, Token t) {}
508
public void nilNode(Object t) {}
509
public void errorNode(Object t) {}
510
public void createNode(Object t, Token token) {}
511
public void createNode(Object t, int tokenType, String text) {}
512
public void becomeRoot(Object newRoot, Object oldRoot) {}
513
public void addChild(Object root, Object child) {}
514
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) {}
515
public void setType(Object node, int tokenType) {}
516
public void setText(Object node, String tokenText) {}
517
public void terminate() {}
518
}
519
```
520
521
### Debug Event Hub
522
523
Multiplexer for sending debug events to multiple listeners.
524
525
```java { .api }
526
/**
527
* Hub for managing multiple debug listeners
528
*/
529
public class DebugEventHub implements DebugEventListener {
530
protected List<DebugEventListener> listeners;
531
532
public DebugEventHub(DebugEventListener listener, DebugEventListener... others);
533
534
public void addListener(DebugEventListener listener);
535
public void removeListener(DebugEventListener listener);
536
537
/* All methods delegate to all listeners */
538
public void enterRule(String grammarFileName, String ruleName);
539
public void exitRule(String grammarFileName, String ruleName);
540
// ... (implements all DebugEventListener methods)
541
}
542
```
543
544
## Common Patterns
545
546
### Custom Debug Listener
547
548
```java
549
public class MyDebugListener extends BlankDebugEventListener {
550
private int ruleDepth = 0;
551
private long startTime;
552
553
@Override
554
public void enterRule(String grammarFileName, String ruleName) {
555
System.out.println(indent() + "-> " + ruleName);
556
ruleDepth++;
557
startTime = System.currentTimeMillis();
558
}
559
560
@Override
561
public void exitRule(String grammarFileName, String ruleName) {
562
ruleDepth--;
563
long elapsed = System.currentTimeMillis() - startTime;
564
System.out.println(indent() + "<- " + ruleName + " (" + elapsed + "ms)");
565
}
566
567
@Override
568
public void consumeToken(Token token) {
569
System.out.println(indent() + "consume: " + token.getText());
570
}
571
572
private String indent() {
573
return " ".repeat(ruleDepth);
574
}
575
}
576
```
577
578
### Performance Monitoring
579
580
```java
581
public class PerformanceMonitor extends BlankDebugEventListener {
582
private Map<String, RuleStats> ruleStats = new HashMap<>();
583
private Stack<RuleEntry> callStack = new Stack<>();
584
585
private static class RuleEntry {
586
String name;
587
long startTime;
588
589
RuleEntry(String name) {
590
this.name = name;
591
this.startTime = System.nanoTime();
592
}
593
}
594
595
private static class RuleStats {
596
int callCount;
597
long totalTime;
598
long maxTime;
599
600
void addCall(long time) {
601
callCount++;
602
totalTime += time;
603
maxTime = Math.max(maxTime, time);
604
}
605
}
606
607
@Override
608
public void enterRule(String grammarFileName, String ruleName) {
609
callStack.push(new RuleEntry(ruleName));
610
}
611
612
@Override
613
public void exitRule(String grammarFileName, String ruleName) {
614
if (!callStack.isEmpty()) {
615
RuleEntry entry = callStack.pop();
616
long elapsed = System.nanoTime() - entry.startTime;
617
618
ruleStats.computeIfAbsent(ruleName, k -> new RuleStats())
619
.addCall(elapsed);
620
}
621
}
622
623
public void printReport() {
624
System.out.println("Performance Report:");
625
System.out.println("Rule\t\tCalls\tTotal(ms)\tAvg(ms)\t\tMax(ms)");
626
627
for (Map.Entry<String, RuleStats> entry : ruleStats.entrySet()) {
628
RuleStats stats = entry.getValue();
629
double totalMs = stats.totalTime / 1_000_000.0;
630
double avgMs = totalMs / stats.callCount;
631
double maxMs = stats.maxTime / 1_000_000.0;
632
633
System.out.printf("%s\t\t%d\t%.2f\t\t%.2f\t\t%.2f%n",
634
entry.getKey(), stats.callCount, totalMs, avgMs, maxMs);
635
}
636
}
637
}
638
```
639
640
### Conditional Debugging
641
642
```java
643
public class ConditionalDebugger extends BlankDebugEventListener {
644
private final Set<String> debugRules;
645
private final boolean debugTokens;
646
private boolean inDebugRule = false;
647
648
public ConditionalDebugger(Set<String> debugRules, boolean debugTokens) {
649
this.debugRules = debugRules;
650
this.debugTokens = debugTokens;
651
}
652
653
@Override
654
public void enterRule(String grammarFileName, String ruleName) {
655
if (debugRules.contains(ruleName)) {
656
inDebugRule = true;
657
System.out.println("DEBUG: Entering " + ruleName);
658
}
659
}
660
661
@Override
662
public void exitRule(String grammarFileName, String ruleName) {
663
if (debugRules.contains(ruleName)) {
664
System.out.println("DEBUG: Exiting " + ruleName);
665
inDebugRule = false;
666
}
667
}
668
669
@Override
670
public void consumeToken(Token token) {
671
if (debugTokens && inDebugRule) {
672
System.out.println("DEBUG: Token: " + token.getText() +
673
" at " + token.getLine() + ":" + token.getCharPositionInLine());
674
}
675
}
676
}
677
```