001: package xtc.lang.jeannie;
002:
003: import java.io.IOException;
004: import java.util.regex.Matcher;
005: import java.util.Comparator;
006: import java.util.HashMap;
007: import java.util.HashSet;
008: import java.util.TreeSet;
009:
010: import xtc.lang.jeannie.Debugger.DebugerControlStatus;
011: import xtc.tree.GNode;
012:
013: /**
014: * The Blink macro command implementation.
015: *
016: * @author Byeongcheol Lee
017: */
018: public final class DebuggerCommand {
019:
020: /**
021: * The debugger.
022: */
023: private final Debugger dbg;
024:
025: /**
026: * The Jeannie expression handler.
027: */
028: final DebuggerExpression dbgExpr;
029:
030: /**
031: * The blink break point list.
032: */
033: private final HashMap<Integer, BlinkBreakPoint> breakPoints = new HashMap<Integer, BlinkBreakPoint>();
034:
035: /**
036: * The deferred break point list.
037: */
038: private final HashMap<Integer, BlinkNativeBreakPoint> deferredBreakPoints = new HashMap<Integer, BlinkNativeBreakPoint>();
039:
040: /**
041: * @param dbg The debugger to work with.
042: */
043: DebuggerCommand(Debugger dbg) {
044: this .dbg = dbg;
045: this .dbgExpr = new DebuggerExpression(dbg);
046: }
047:
048: /**
049: * Extract the current source file and line by asking the current debugger
050: * (jdb or gdb).
051: *
052: * @return An object containing the source file and the line.
053: */
054: DebuggerSymbolMapper.SourceFileAndLine getCurrentSourceFileAndLine() {
055: // This would be pretty naive implementation.
056: switch (dbg.getControlStatus()) {
057: case JDB:
058: try {
059: Matcher m = dbg.runAndWaitJDB("where\n",
060: "\\[\\d+\\] [^ ]+ \\(([^:]+):(\\d+)\\)");
061: String file = m.group(1);
062: int line = Integer.parseInt(m.group(2));
063: return new DebuggerSymbolMapper.SourceFileAndLine(file,
064: line);
065: } catch (IOException e) {
066: dbg
067: .err("can not handle ensureCurrentSourceFileAndLine");
068: }
069: break;
070: case GDB:
071: try {
072: Matcher m = dbg.runAndWaitGDB("where 1\n",
073: "at ([^:]+):(\\d+)");
074: String file = m.group(1);
075: int line = Integer.parseInt(m.group(2));
076: return new DebuggerSymbolMapper.SourceFileAndLine(file,
077: line);
078: } catch (IOException e) {
079: dbg
080: .err("can not handle ensureCurrentSourceFileAndLine");
081: }
082: break;
083: default:
084: dbg.err("can not handle ensureCurrentSourceFileAndLine");
085: break;
086: }
087: return null;
088: }
089:
090: /**
091: * Pass a command to the jdb.
092: *
093: * @param jdbCommand The jdb command.
094: */
095: void executeJDBCommand(String jdbCommand) {
096: if (dbg.getControlStatus() != Debugger.DebugerControlStatus.JDB
097: && dbg.getControlStatus() != Debugger.DebugerControlStatus.JDB_IN_GDB) {
098: dbg
099: .err("jdb prefix command is allowed only in the jdb mode");
100: }
101: try {
102: dbg.sendUserMessage(jdbCommand + "\n");
103: } catch (IOException e) {
104: dbg.err("can not successfully jdb command");
105: }
106: }
107:
108: /**
109: * Pass a command to the gdb.
110: *
111: * @param gdbCommand THe gdb command.
112: */
113: void executeGDBCommand(String gdbCommand) {
114: if (dbg.getControlStatus() != Debugger.DebugerControlStatus.GDB
115: && dbg.getControlStatus() != Debugger.DebugerControlStatus.GDB_IN_JDB) {
116: dbg
117: .err("gdb prefix command is allowed only in the gdb mode");
118: }
119: try {
120: dbg.sendUserMessage(gdbCommand + "\n");
121: } catch (IOException e) {
122: dbg.err("can not successfully gdb command");
123: }
124: }
125:
126: /**
127: * Set a C break point.
128: *
129: * @param sourceFile The source file name.
130: * @param lineNumber The line number.
131: */
132: void executeBreak(String sourceFile, int lineNumber) {
133: BlinkNativeBreakPoint bp = addNativeBreakPoint(sourceFile,
134: lineNumber);
135: if (!dbg.IsDebuggerSwitchingInitialized()) {
136: deferredBreakPoints.put(bp.getID(), bp);
137: dbg
138: .out("the break point is delayed until the shared library loading\n");
139: return;
140: }
141: bp.set(dbg);
142:
143: }
144:
145: /**
146: * Set a Java break point.
147: *
148: * @param classID The class name.
149: * @param lineNumber The line number.
150: */
151: void executeStopAt(String classID, int lineNumber) {
152: BlinkJavaBreakPoint bp = addJavaBreakPoint(classID, lineNumber);
153: bp.set(dbg);
154: }
155:
156: /**
157: * Implement "info break" command.
158: */
159: void executeInfoBreak() {
160: TreeSet<Integer> breakPointIDList = new TreeSet<Integer>();
161: breakPointIDList.addAll(breakPoints.keySet());
162: StringBuilder sb = new StringBuilder();
163: for (final Integer id : breakPointIDList) {
164: BlinkBreakPoint bp = breakPoints.get(id);
165: sb.append(bp.toString()).append('\n');
166: }
167: dbg.out(sb.toString());
168: }
169:
170: /**
171: * Implement "delete [n]" command.
172: * @param id The identifier of break point or watch point.
173: */
174: void executeDelete(int id) {
175: if (deferredBreakPoints.containsKey(new Integer(id))) {
176: deferredBreakPoints.remove(new Integer(id));
177: breakPoints.remove(new Integer(id));
178: return;
179: } else if (!breakPoints.containsKey(new Integer(id))) {
180: dbg.err("not valid break point id -" + id);
181: return;
182: }
183:
184: BlinkBreakPoint bp = breakPoints.get(new Integer(id));
185: bp.reset(dbg);
186: breakPoints.remove(new Integer(id));
187: }
188:
189: /**
190: * Implement the access watch command.
191: *
192: * @param accessType The access type. Either "access" or "all"
193: * @param className The class name to watch.
194: * @param fieldName The field name to watch.
195: */
196: void executeWatch(String accessType, String className,
197: String fieldName) {
198: assert false : "not yet implemented";
199: }
200:
201: /**
202: * Implement the expression watch command.
203: * @param expr The expression
204: */
205: void executeWatch(String expr) {
206: assert false : "not yet implemented";
207: }
208:
209: /**
210: * Implement "info watch" command.
211: */
212: void executeInfoWatch() {
213: assert false : "not yet implemented";
214: }
215:
216: /**
217: * Implement Blink "where" command.
218: */
219: void executeWhere() {
220: switch (dbg.getControlStatus()) {
221: case NONE:
222: dbg.out("not available at this time.");
223: break;
224: case JDB:
225: case JDB_IN_GDB:
226: try {
227: dbg.sendUserMessage("where\n");
228: } catch (IOException e) {
229: dbg.err("could not correctly run where");
230: }
231: break;
232: case GDB:
233: case GDB_IN_JDB:
234: try {
235: dbg.sendUserMessage("where\n");
236: } catch (IOException e) {
237: dbg.err("could not correctly run where");
238: }
239: break;
240: default:
241: break;
242: }
243: }
244:
245: /**
246: * Implement "up [n]" command.
247: * @param step The number of stack walk-up.
248: */
249: void executeUp(int step) {
250: assert false : "not yet implemented";
251: }
252:
253: /**
254: * Implement "down [n]" command.
255: *
256: * @param step The number of stack walk-down.
257: */
258: void executeDown(int step) {
259: assert false : "not yet implemented";
260: }
261:
262: /**
263: * Implement "locals" command.
264: */
265: void executeLocals() {
266: assert false : "not yet implemented";
267: }
268:
269: /**
270: * Implement the step command.
271: */
272: void executeStep() {
273: assert false : "not yet implemented";
274: }
275:
276: /**
277: * Implement the next command.
278: */
279: void executeNext() {
280: assert false : "not yet implemented";
281: }
282:
283: /**
284: * Implement the print <expr> command.
285: *
286: * @param expr The expression.
287: */
288: void executePrint(final GNode n) {
289: DebuggerSymbolMapper.SourceFileAndLine loc = getCurrentSourceFileAndLine();
290: switch (dbg.getControlStatus()) {
291: case JDB:
292: String jdbExpr = dbgExpr.toJDBExpression(n, loc);
293: try {
294: Matcher m = dbg.runAndWaitJDB("print " + jdbExpr,
295: "(.+ = )");
296: dbg.out(m.group(1));
297: } catch (IOException e) {
298: dbg.err("can not correctly run print");
299: }
300: break;
301: case GDB:
302: String gdbExpr = dbgExpr.toGDBExpression(n, loc);
303: try {
304: Matcher m = dbg.runAndWaitJDB("print " + gdbExpr,
305: "(.+ = )");
306: dbg.out(m.group(1));
307: } catch (IOException e) {
308: dbg.err("can not correctly run print");
309: }
310: break;
311: default:
312: assert false : "not suitable debugger status";
313: break;
314: }
315:
316: }
317:
318: /**
319: * Implement Blink "list" command.
320: */
321: void executeList() {
322: try {
323: dbg.sendUserMessage("list\n");
324: } catch (IOException e) {
325: dbg.err("could not run the list ");
326: }
327: }
328:
329: /**
330: * @return true if there is blink deferred C break point.
331: */
332: boolean HasDeferredBreakPoint() {
333: return deferredBreakPoints.size() > 0;
334: }
335:
336: /**
337: * Handle deferred C break points when the shared library is loaded into the
338: * debuggee JVM.
339: */
340: void HandleDeferredBreakPoint() {
341: assert dbg.getControlStatus() == Debugger.DebugerControlStatus.GDB
342: || dbg.getControlStatus() == Debugger.DebugerControlStatus.JDB;
343: for (BlinkNativeBreakPoint p : deferredBreakPoints.values()) {
344: p.set(dbg);
345: }
346: deferredBreakPoints.clear();
347: }
348:
349: /**
350: *
351: * @param sourceFile The source file name.
352: * @param sourceLine The source line number.
353: */
354: private BlinkNativeBreakPoint addNativeBreakPoint(
355: String sourceFile, int sourceLine) {
356: BlinkNativeBreakPoint nbp = new BlinkNativeBreakPoint(
357: sourceFile, sourceLine);
358: breakPoints.put(new Integer(nbp.getID()), nbp);
359: return nbp;
360: }
361:
362: /**
363: *
364: * @param classFile The class name.
365: * @param sourceLine The source line number.
366: */
367: private BlinkJavaBreakPoint addJavaBreakPoint(String classFile,
368: int sourceLine) {
369: BlinkJavaBreakPoint jbp = new BlinkJavaBreakPoint(classFile,
370: sourceLine);
371: breakPoints.put(new Integer(jbp.getID()), jbp);
372: return jbp;
373: }
374:
375: /**
376: * A base class for the Blink break point.
377: */
378: private static abstract class BlinkBreakPoint {
379:
380: /**
381: * The next identification sequence number.
382: */
383: private static int nextID = 1;
384:
385: /**
386: * Allocate a new identification number.
387: *
388: * @return A new identification number.
389: */
390: private static synchronized int getNextID() {
391: return nextID++;
392: }
393:
394: /**
395: * The identification number.
396: */
397: private final int Id;
398:
399: protected BlinkBreakPoint() {
400: this .Id = getNextID();
401: }
402:
403: /**
404: * @return The identification number of the break point.
405: */
406: public int getID() {
407: return Id;
408: }
409:
410: abstract void set(Debugger dbg);
411:
412: abstract void reset(Debugger dbg);
413: }
414:
415: /**
416: * A deferred break point in the native code.
417: */
418: private static class BlinkNativeBreakPoint extends BlinkBreakPoint {
419:
420: /**
421: * The source file name.
422: */
423: final String sourceFileName;
424:
425: /**
426: * The line number in the source file.
427: */
428: final int sourceLineNumber;
429:
430: /**
431: * @param sname The source file name.
432: * @param lineno The line number.
433: */
434: BlinkNativeBreakPoint(String sname, int lineno) {
435: sourceFileName = sname;
436: sourceLineNumber = lineno;
437: }
438:
439: /**
440: * @return The source file name.
441: */
442: String getSourceFileName() {
443: return sourceFileName;
444: }
445:
446: /**
447: * @return The source line number.
448: */
449: int getSourceLineNumber() {
450: return sourceLineNumber;
451: }
452:
453: /**
454: * Check the equality of the native break point.
455: *
456: * @param o The compared object.
457: */
458: public boolean equals(Object o) {
459: if (o instanceof BlinkNativeBreakPoint == false)
460: return false;
461: BlinkNativeBreakPoint nbp = (BlinkNativeBreakPoint) o;
462: return (this == nbp)
463: || (this .sourceFileName.equals(nbp.sourceFileName) && this .sourceLineNumber == nbp.sourceLineNumber);
464: }
465:
466: /**
467: * @return The string representation.
468: */
469: public String toString() {
470: StringBuilder sb = new StringBuilder();
471: sb.append(getID()).append(" ").append("native").append(" ");
472: sb.append(sourceFileName).append(":").append(
473: sourceLineNumber);
474: return sb.toString();
475: }
476:
477: void set(Debugger dbg) {
478:
479: switch (dbg.getControlStatus()) {
480: case JDB:
481: try {
482: dbg.j2n();
483: // try to set break point
484: dbg.runAndWaitGDB("break " + sourceFileName + ":"
485: + sourceLineNumber + "\n",
486: "Breakpoint [0-9]+ at");
487: dbg.jret();
488: } catch (IOException e) {
489: dbg.err("could not set the break point.");
490: }
491: break;
492: case GDB:
493: try {
494: //try to set the break point
495: //try to set break point
496: dbg.runAndWaitGDB("break " + sourceFileName + ":"
497: + sourceLineNumber + "\n",
498: "Breakpoint [0-9]+ at");
499: } catch (IOException e) {
500: dbg.err("could not set the break point.");
501: }
502: break;
503: default:
504: break;
505: }
506: }
507:
508: /**
509: * Delete this break in the gdb.
510: * @param dbg The Blink debugger.
511: */
512: void reset(Debugger dbg) {
513: assert (dbg.getControlStatus() == Debugger.DebugerControlStatus.JDB)
514: || (dbg.getControlStatus() == Debugger.DebugerControlStatus.GDB);
515: assert (dbg.IsDebuggerSwitchingInitialized());
516: boolean needSwitching = dbg.getControlStatus() == Debugger.DebugerControlStatus.JDB;
517: if (needSwitching)
518: dbg.j2n();
519:
520: try {
521: dbg.runAndWaitGDB("clear " + sourceFileName + ":"
522: + sourceLineNumber + "\n",
523: "Deleted breakpoint [0-9]+");
524: } catch (IOException e) {
525: dbg.err("cound not resent the break point");
526: }
527:
528: if (needSwitching)
529: dbg.jret();
530: }
531: }
532:
533: /**
534: * A deferred break point in the native code.
535: */
536: private static class BlinkJavaBreakPoint extends BlinkBreakPoint {
537:
538: /**
539: * The source file name.
540: */
541: final String className;
542:
543: /**
544: * The line number in the source file.
545: */
546: final int sourceLineNumber;
547:
548: /**
549: * @param sname The source file name.
550: * @param lineno The line number.
551: */
552: BlinkJavaBreakPoint(String sname, int lineno) {
553: className = sname;
554: sourceLineNumber = lineno;
555: }
556:
557: /**
558: * @return The source file name.
559: */
560: String getClassName() {
561: return className;
562: }
563:
564: /**
565: * @return The source line number.
566: */
567: int getSourceLineNumber() {
568: return sourceLineNumber;
569: }
570:
571: /**
572: * Check the equality of the native break point.
573: *
574: * @param o The compared object.
575: */
576: public boolean equals(Object o) {
577: if (o instanceof BlinkNativeBreakPoint == false)
578: return false;
579: BlinkJavaBreakPoint nbp = (BlinkJavaBreakPoint) o;
580: return (this == nbp)
581: || (this .className.equals(nbp.className) && this .sourceLineNumber == nbp.sourceLineNumber);
582: }
583:
584: /**
585: * @return The string representation.
586: */
587: public String toString() {
588: StringBuilder sb = new StringBuilder();
589: sb.append(getID()).append(" ").append("java").append(" ");
590: sb.append(className).append(":").append(sourceLineNumber);
591: return sb.toString();
592: }
593:
594: void set(Debugger dbg) {
595: switch (dbg.getControlStatus()) {
596: case JDB:
597: try {
598: //try to set the break point
599: dbg.runAndWaitJDB("stop at " + className + ":"
600: + sourceLineNumber + "\n", new String[] {
601: "Deferring breakpoint [^ ]+:[0-9]+.",
602: "Set breakpoint [^ ]+:[0-9]+", });
603: } catch (IOException e) {
604: dbg.err("could not set the break point.");
605: }
606: break;
607: case GDB:
608: try {
609: dbg.n2j();
610: dbg.runAndWaitJDB("stop at " + className + ":"
611: + sourceLineNumber + "\n", new String[] {
612: "Deferring breakpoint [^ ]+:[0-9]+.",
613: "Set breakpoint [^ ]+:[0-9]+", });
614: dbg.jret();
615: } catch (IOException e) {
616: dbg.err("could not set the break point.");
617: }
618: break;
619: default:
620: break;
621: }
622: }
623:
624: /**
625: * Delete this break in the gdb.
626: * @param dbg The Blink debugger.
627: */
628: void reset(Debugger dbg) {
629: assert (dbg.getControlStatus() == Debugger.DebugerControlStatus.JDB)
630: || (dbg.getControlStatus() == Debugger.DebugerControlStatus.GDB);
631: assert (dbg.IsDebuggerSwitchingInitialized());
632: boolean needSwitching = dbg.getControlStatus() == Debugger.DebugerControlStatus.GDB;
633: if (needSwitching)
634: dbg.n2j();
635:
636: try {
637: dbg.runAndWaitJDB("clear " + className + ":"
638: + sourceLineNumber + "\n",
639: "Removed: breakpoint ([^ ]+):([0-9]+)");
640: } catch (IOException e) {
641: dbg.err("cound not resent the break point");
642: }
643:
644: if (needSwitching)
645: dbg.jret();
646: }
647: }
648: }
|