/******************************************************************************* * Copyright (c) 2005, 2018 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bjorn Freeman-Benson - initial API and implementation * Pawel Piech (Wind River) - ported PDA Virtual Machine to Java (Bug 261400) * IBM Coporation - bug fixing *******************************************************************************/ package org.eclipse.debug.examples.pdavm; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.StringWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.StringTokenizer; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Push Down Automata interpreter. * * @since 3.5 */ public class PDAVirtualMachine { static class Stack extends LinkedList { private static final long serialVersionUID = 1L; @Override public Object pop() { return isEmpty() ? Integer.valueOf(0) : remove(size() - 1); } @Override public void push(Object value) { add(value); } } static class Register { Register(String name) { fName = name; } String fName; String fGroup = ""; //$NON-NLS-1$ boolean fIsWriteable = true; Map fBitFields = new LinkedHashMap<>(0); int fValue; } static class BitField { BitField(String name) { fName = name; } String fName; int fBitOffset; int fBitCount; Map fMnemonics = new LinkedHashMap<>(0); } Map fRegisters = new LinkedHashMap<>(0); class Args { final String[] fArgs; int next = 0; Args(String[] args) { fArgs = args; } boolean hasNextArg() { return fArgs.length > next; } String getNextStringArg() { if (fArgs.length > next) { return fArgs[next++]; } return ""; //$NON-NLS-1$ } int getNextIntArg() { String arg = getNextStringArg(); try { return Integer.parseInt(arg); } catch (NumberFormatException e) { } return 0; } boolean getNextBooleanArg() { String arg = getNextStringArg(); try { return Boolean.getBoolean(arg); } catch (NumberFormatException e) { } return false; } Object getNextIntOrStringArg() { String arg = getNextStringArg(); try { return Integer.valueOf(arg); } catch (NumberFormatException e) { } return arg; } PDAThread getThreadArg() { int id = getNextIntArg(); return fThreads.get( Integer.valueOf(id) ); } } class PDAThread { final int fID; /** The push down automata data stack (the data stack). */ final Stack fStack = new Stack(); /** * PDAThread copy of the code. It can differ from the program if * performing an evaluation. */ String[] fThreadCode; /** PDAThread copy of the labels. */ Map fThreadLabels; /** The stack of stack frames (the control stack) */ final List fFrames = new LinkedList<>(); /** Current stack frame (not includced in fFrames) */ Frame fCurrentFrame; /** * The run flag is true if the thread is running. If the run flag is * false, the thread exits the next time the main instruction loop runs. */ boolean fRun = true; String fSuspend = null; boolean fStep = false; boolean fStepReturn = false; int fSavedPC; boolean fPerformingEval = false; PDAThread(int id, String function, int pc) { fID = id; fCurrentFrame = new Frame(function, pc); fThreadCode = fCode; fThreadLabels = fLabels; } } final Map fThreads = new LinkedHashMap<>(); int fNextThreadId = 1; boolean fStarted = true; /** * The code is stored as an array of strings, each line of the source file * being one entry in the array. */ final String[] fCode; /** A mapping of labels to indicies in the code array */ final Map fLabels; /** Each stack frame is a mapping of variable names to values. */ class Frame { final Map fLocalVariables = new LinkedHashMap<>(); /** * The name of the function in this frame */ final String fFunction; /** * The current program counter in the frame the pc points to the next * instruction to be executed */ int fPC; Frame(String function, int pc) { fFunction = function; fPC = pc; } void set(String name, Object value) { if (name.startsWith("$")) { //$NON-NLS-1$ setRegisterValue(name, value); } else { fLocalVariables.put(name, value); } } Object get(String name) { if (name.startsWith("$")) { //$NON-NLS-1$ return getRegisterValue(name); } else { return fLocalVariables.get(name); } } } void setRegisterValue(String name, Object value) { Register reg = fRegisters.get(getRegisterPartOfName(name)); if (reg == null) { return; } String bitFieldName = getBitFieldPartOfName(name); if (bitFieldName != null) { BitField bitField = reg.fBitFields.get(bitFieldName); if (bitField == null) { return; } Integer intValue = null; if (value instanceof Integer) { intValue = (Integer)value; } else if (value instanceof String) { intValue = bitField.fMnemonics.get(value); } if (intValue != null) { int bitFieldMask = 2^(bitField.fBitCount - 1); int registerMask = ~(bitFieldMask << bitField.fBitOffset); int bitFieldValue = intValue.intValue() & bitFieldMask; reg.fValue = (reg.fValue & registerMask) | (bitFieldValue << bitField.fBitOffset); } } else if (value instanceof Integer) { reg.fValue = ((Integer)value).intValue(); } } Object getRegisterValue(String name) { Register reg = fRegisters.get(getRegisterPartOfName(name)); if (reg == null) { return null; } String bitFieldName = getBitFieldPartOfName(name); if (bitFieldName != null) { BitField bitField = reg.fBitFields.get(bitFieldName); if (bitField == null) { return null; } int bitFieldMask = 2^(bitField.fBitCount - 1); int registerMask = bitFieldMask << bitField.fBitOffset; return Integer.valueOf( (reg.fValue & registerMask) >> bitField.fBitOffset ); } else { return Integer.valueOf(reg.fValue); } } /** * Breakpoints are stored per each each line of code. The boolean indicates * whether the whole VM should suspend or just the triggering thread. */ final Map fBreakpoints = new HashMap<>(); /** * The suspend flag is true if the VM should suspend running the program and * just listen for debug commands. */ String fSuspendVM; /** Flag indicating whether the debugger is performing a step. */ boolean fStepVM = false; /** Flag indicating whether the debugger is performing a step return */ boolean fStepReturnVM = false; int fSteppingThread = 0; /** Name of the pda program being debugged */ final String fFilename; /** The command line argument to start a debug session. */ final boolean fDebug; /** The port to listen for debug commands on */ final int fCommandPort; /** * Command socket for receiving debug commands and sending command responses */ Socket fCommandSocket; /** Command socket reader */ BufferedReader fCommandReceiveStream; /** Command socket write stream. */ OutputStream fCommandResponseStream; /** The port to send debug events to */ final int fEventPort; /** Event socket */ Socket fEventSocket; /** Event socket and write stream. */ OutputStream fEventStream; /** The eventstops table holds which events cause suspends and which do not. */ final Map fEventStops = new HashMap<>(); { fEventStops.put("unimpinstr", Boolean.FALSE); //$NON-NLS-1$ fEventStops.put("nosuchlabel", Boolean.FALSE); //$NON-NLS-1$ } /** * The watchpoints table holds watchpoint information. *

* variablename_stackframedepth => N *

    *
  • N = 0 is no watch
  • *
  • N = 1 is read watch
  • *
  • N = 2 is write watch
  • *
  • N = 3 is both, etc.
  • */ final Map fWatchpoints = new HashMap<>(); public static void main(String[] args) { String programFile = args.length >= 1 ? args[0] : null; if (programFile == null) { System.err.println("Error: No program specified"); //$NON-NLS-1$ return; } String debugFlag = args.length >= 2 ? args[1] : ""; //$NON-NLS-1$ boolean debug = "-debug".equals(debugFlag); //$NON-NLS-1$ int commandPort = 0; int eventPort = 0; if (debug) { String commandPortStr = args.length >= 3 ? args[2] : ""; //$NON-NLS-1$ try { commandPort = Integer.parseInt(commandPortStr); } catch (NumberFormatException e) { System.err.println("Error: Invalid command port"); //$NON-NLS-1$ return; } String eventPortStr = args.length >= 4 ? args[3] : ""; //$NON-NLS-1$ try { eventPort = Integer.parseInt(eventPortStr); } catch (NumberFormatException e) { System.err.println("Error: Invalid event port"); //$NON-NLS-1$ return; } } PDAVirtualMachine pdaVM = null; try { pdaVM = new PDAVirtualMachine(programFile, debug, commandPort, eventPort); pdaVM.startDebugger(); } catch (IOException e) { System.err.println("Error: " + e.toString()); //$NON-NLS-1$ return; } pdaVM.run(); } PDAVirtualMachine(String inputFile, boolean debug, int commandPort, int eventPort) throws IOException { fFilename = inputFile; // Load all the code into memory StringWriter stringWriter = new StringWriter(); List code = new LinkedList<>(); try (FileReader fileReader = new FileReader(inputFile)) { int c = fileReader.read(); while (c != -1) { if (c == '\n') { code.add(stringWriter.toString().trim()); stringWriter = new StringWriter(); } else { stringWriter.write(c); } c = fileReader.read(); } } code.add(stringWriter.toString().trim()); fCode = code.toArray(new String[code.size()]); fLabels = mapLabels(fCode); fDebug = debug; fCommandPort = commandPort; fEventPort = eventPort; } /** * Initializes the labels map */ Map mapLabels(String[] code) { Map labels = new HashMap<>(); for (int i = 0; i < code.length; i++) { if (code[i].length() != 0 && code[i].charAt(0) == ':') { labels.put(code[i].substring(1), Integer.valueOf(i)); } } return labels; } void sendCommandResponse(String response) { try { fCommandResponseStream.write(response.getBytes()); fCommandResponseStream.flush(); } catch (IOException e) { } } void sendDebugEvent(String event, boolean error) { if (fDebug) { try { fEventStream.write(event.getBytes()); fEventStream.write('\n'); fEventStream.flush(); } catch (IOException e) { System.err.println("Error: " + e); //$NON-NLS-1$ System.exit(1); } } else if (error) { System.err.println("Error: " + event); //$NON-NLS-1$ } } void startDebugger() throws IOException { if (fDebug) { System.out.println("-debug " + fCommandPort + " " + fEventPort); //$NON-NLS-1$ //$NON-NLS-2$ } try (ServerSocket commandServerSocket = new ServerSocket(fCommandPort)) { fCommandSocket = commandServerSocket.accept(); fCommandReceiveStream = new BufferedReader(new InputStreamReader(fCommandSocket.getInputStream())); fCommandResponseStream = new PrintStream(fCommandSocket.getOutputStream()); } try (ServerSocket eventServerSocket = new ServerSocket(fEventPort)) { fEventSocket = eventServerSocket.accept(); fEventStream = new PrintStream(fEventSocket.getOutputStream()); } System.out.println("debug connection accepted"); //$NON-NLS-1$ fSuspendVM = "client"; //$NON-NLS-1$ } void run() { int id = fNextThreadId++; sendDebugEvent("vmstarted", false); //$NON-NLS-1$ fThreads.put(Integer.valueOf(id), new PDAThread(id, "main", 0)); //$NON-NLS-1$ if (fDebug) { sendDebugEvent("started " + id, false); //$NON-NLS-1$ } boolean allThreadsSuspended = false; while (!fThreads.isEmpty()) { checkForBreakpoint(); if (fSuspendVM != null) { debugUI(); } else { yieldToDebug(allThreadsSuspended); if (fSuspendVM != null) { // Received a command to suspend VM, skip executing threads. continue; } } PDAThread[] threadsCopy = fThreads.values().toArray(new PDAThread[fThreads.size()]); allThreadsSuspended = true; for (int i = 0; i < threadsCopy.length; i++) { PDAThread thread = threadsCopy[i]; if (thread.fSuspend == null) { allThreadsSuspended = false; String instruction = thread.fThreadCode[thread.fCurrentFrame.fPC]; thread.fCurrentFrame.fPC++; doOneInstruction(thread, instruction); if (thread.fCurrentFrame.fPC >= thread.fThreadCode.length) { // Thread reached end of code, exit from the thread. thread.fRun = false; } else if (thread.fStepReturn) { // If this thread is in a step-return operation, check // if we've returned from a call. instruction = thread.fThreadCode[thread.fCurrentFrame.fPC]; if ("return".equals(instruction)) { //$NON-NLS-1$ // Note: this will only be triggered if the current // thread also has the fStepReturn flag set. if (fStepReturnVM) { fSuspendVM = thread.fID + " step"; //$NON-NLS-1$ } else { thread.fSuspend = "step"; //$NON-NLS-1$ } } } if (!thread.fRun) { sendDebugEvent("exited " + thread.fID, false); //$NON-NLS-1$ fThreads.remove(Integer.valueOf(thread.fID)); } else if (thread.fSuspend != null) { sendDebugEvent("suspended " + thread.fID + " " + thread.fSuspend, false); //$NON-NLS-1$ //$NON-NLS-2$ thread.fStep = thread.fStepReturn = thread.fPerformingEval = false; } } } // Force thread context switch to avoid starving out other // processes in the system. Thread.yield(); } sendDebugEvent("vmterminated", false); //$NON-NLS-1$ if (fDebug) { try { fCommandReceiveStream.close(); fCommandResponseStream.close(); fCommandSocket.close(); fEventStream.close(); fEventSocket.close(); } catch (IOException e) { System.out.println("Error: " + e); //$NON-NLS-1$ } } } void doOneInstruction(PDAThread thread, String instr) { StringTokenizer tokenizer = new StringTokenizer(instr); String op = tokenizer.nextToken(); List tokens = new LinkedList<>(); while (tokenizer.hasMoreTokens()) { tokens.add(tokenizer.nextToken()); } Args args = new Args( tokens.toArray(new String[tokens.size()]) ); boolean opValid = true; if (op.equals("add")) { //$NON-NLS-1$ iAdd(thread, args); } else if (op.equals("branch_not_zero")) { //$NON-NLS-1$ iBranchNotZero(thread, args); } else if (op.equals("call")) { //$NON-NLS-1$ iCall(thread, args); } else if (op.equals("dec")) { //$NON-NLS-1$ iDec(thread, args); } else if (op.equals("def")) { //$NON-NLS-1$ iDef(thread, args); } else if (op.equals("dup")) { //$NON-NLS-1$ iDup(thread, args); } else if (op.equals("exec")) { //$NON-NLS-1$ iExec(thread, args); } else if (op.equals("halt")) { //$NON-NLS-1$ iHalt(thread, args); } else if (op.equals("output")) { //$NON-NLS-1$ iOutput(thread, args); } else if (op.equals("pop")) { //$NON-NLS-1$ iPop(thread, args); } else if (op.equals("push")) { //$NON-NLS-1$ iPush(thread, args); } else if (op.equals("return")) { //$NON-NLS-1$ iReturn(thread, args); } else if (op.equals("var")) { //$NON-NLS-1$ iVar(thread, args); } else if (op.equals("xyzzy")) { //$NON-NLS-1$ iInternalEndEval(thread, args); } else if (op.startsWith(":")) {} // label //$NON-NLS-1$ else if (op.startsWith("#")) {} // comment //$NON-NLS-1$ else { opValid = false; } if (!opValid) { sendDebugEvent("unimplemented instruction " + op, true); //$NON-NLS-1$ if ( fEventStops.get("unimpinstr").booleanValue() ) { //$NON-NLS-1$ fSuspendVM = thread.fID + " event unimpinstr"; //$NON-NLS-1$ thread.fCurrentFrame.fPC--; } } else if (thread.fStep) { if (fStepVM) { fSuspendVM = thread.fID + " step"; //$NON-NLS-1$ fStepVM = false; } else { thread.fSuspend = "step"; //$NON-NLS-1$ } thread.fStep = false; } } void checkForBreakpoint() { if (fDebug) { for (Iterator itr = fThreads.values().iterator(); itr.hasNext();) { PDAThread thread = itr.next(); Integer pc = Integer.valueOf(thread.fCurrentFrame.fPC); // Suspend for breakpoint if: // - the VM is not yet set to suspend, for e.g. as a result of step end, // - the thread is not yet suspended and is not performing an evaluation // - the breakpoints table contains a breakpoint for the given line. if (fSuspendVM == null && thread.fSuspend == null && !thread.fPerformingEval && fBreakpoints.containsKey(pc)) { if ( fBreakpoints.get(pc).booleanValue() ) { fSuspendVM = thread.fID + " breakpoint " + pc; //$NON-NLS-1$ } else { thread.fSuspend = "breakpoint " + pc; //$NON-NLS-1$ thread.fStep = thread.fStepReturn = false; sendDebugEvent("suspended " + thread.fID + " " + thread.fSuspend, false); //$NON-NLS-1$ //$NON-NLS-2$ } } } } } /** * After each instruction, we check the debug command channel for control input. If * there are commands, process them. */ void yieldToDebug(boolean allThreadsSuspended) { if (fDebug) { String line = ""; //$NON-NLS-1$ try { if (allThreadsSuspended || fCommandReceiveStream.ready()) { line = fCommandReceiveStream.readLine(); processDebugCommand(line); } } catch (IOException e) { System.err.println("Error: " + e); //$NON-NLS-1$ System.exit(1); } } } /** * Service the debugger commands while the VM is suspended */ void debugUI() { if (!fStarted) { sendDebugEvent("vmsuspended " + fSuspendVM, false); //$NON-NLS-1$ } else { fStarted = false; } // Clear all stepping flags. In case the VM suspended while // a step operation was being performed for the VM or some thread. fStepVM = fStepReturnVM = false; for (Iterator itr = fThreads.values().iterator(); itr.hasNext();) { PDAThread thread = itr.next(); thread.fSuspend = null; thread.fStep = thread.fStepReturn = thread.fPerformingEval = false; } while (fSuspendVM != null) { String line = ""; //$NON-NLS-1$ try { line = fCommandReceiveStream.readLine(); } catch (IOException e) { System.err.println("Error: " + e); //$NON-NLS-1$ System.exit(1); return; } processDebugCommand(line); } if (fStepVM || fStepReturnVM) { sendDebugEvent("vmresumed step", false); //$NON-NLS-1$ } else { sendDebugEvent("vmresumed client", false); //$NON-NLS-1$ } } void processDebugCommand(String line) { StringTokenizer tokenizer = new StringTokenizer(line.trim()); if (line.length() == 0) { return; } String command = tokenizer.nextToken(); List tokens = new LinkedList<>(); while (tokenizer.hasMoreTokens()) { tokens.add(tokenizer.nextToken()); } Args args = new Args( tokens.toArray(new String[tokens.size()])); if ("children".equals(command)) { //$NON-NLS-1$ debugChildren(args); } else if ("clear".equals(command)) { //$NON-NLS-1$ debugClearBreakpoint(args); } else if ("data".equals(command)) { //$NON-NLS-1$ debugData(args); } else if ("drop".equals(command)) { //$NON-NLS-1$ debugDropFrame(args); } else if ("eval".equals(command)) { //$NON-NLS-1$ debugEval(args); } else if ("eventstop".equals(command)) { //$NON-NLS-1$ debugEventStop(args); } else if ("frame".equals(command)) { //$NON-NLS-1$ debugFrame(args); } else if ("groups".equals(command)) { //$NON-NLS-1$ debugGroups(args); } else if ("popdata".equals(command)) { //$NON-NLS-1$ debugPopData(args); } else if ("pushdata".equals(command)) { //$NON-NLS-1$ debugPushData(args); } else if ("registers".equals(command)) { //$NON-NLS-1$ debugRegisters(args); } else if ("restart".equals(command)) { //$NON-NLS-1$ debugRestart(args); } else if ("resume".equals(command)) { //$NON-NLS-1$ debugResume(args); } else if ("set".equals(command)) { //$NON-NLS-1$ debugSetBreakpoint(args); } else if ("setdata".equals(command)) { //$NON-NLS-1$ debugSetData(args); } else if ("setvar".equals(command)) { //$NON-NLS-1$ debugSetVariable(args); } else if ("stack".equals(command)) { //$NON-NLS-1$ debugStack(args); } else if ("stackdepth".equals(command)) { //$NON-NLS-1$ debugStackDepth(args); } else if ("state".equals(command)) { //$NON-NLS-1$ debugState(args); } else if ("step".equals(command)) { //$NON-NLS-1$ debugStep(args); } else if ("stepreturn".equals(command)) { //$NON-NLS-1$ debugStepReturn(args); } else if ("suspend".equals(command)) { //$NON-NLS-1$ debugSuspend(args); } else if ("terminate".equals(command)) { //$NON-NLS-1$ debugTerminate(); } else if ("threads".equals(command)) { //$NON-NLS-1$ debugThreads(); } else if ("var".equals(command)) { //$NON-NLS-1$ debugVar(args); } else if ("vmresume".equals(command)) { //$NON-NLS-1$ debugVMResume(); } else if ("vmsuspend".equals(command)) { //$NON-NLS-1$ debugVMSuspend(); } else if ("watch".equals(command)) { //$NON-NLS-1$ debugWatch(args); } else { sendCommandResponse("error: invalid command\n"); //$NON-NLS-1$ } } void debugChildren(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } int sfnumber = args.getNextIntArg(); String var = args.getNextStringArg(); Frame frame = sfnumber >= thread.fFrames.size() ? thread.fCurrentFrame : (Frame)thread.fFrames.get(sfnumber); String varDot = var + "."; //$NON-NLS-1$ List children = new ArrayList<>(); for (Iterator itr = frame.fLocalVariables.keySet().iterator(); itr.hasNext();) { String localVar = itr.next(); if (localVar.startsWith(varDot) && localVar.indexOf('.', varDot.length() + 1) == -1) { children.add(localVar); } } StringBuffer result = new StringBuffer(); for (Iterator itr = children.iterator(); itr.hasNext();) { result.append(itr.next()); result.append('|'); } result.append('\n'); sendCommandResponse(result.toString()); } void debugClearBreakpoint(Args args) { int line = args.getNextIntArg(); fBreakpoints.remove( Integer.valueOf(line) ); sendCommandResponse("ok\n"); //$NON-NLS-1$ } private static Pattern fPackPattern = Pattern.compile("%([a-fA-F0-9][a-fA-F0-9])"); //$NON-NLS-1$ void debugData(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } StringBuffer result = new StringBuffer(); for (Iterator itr = thread.fStack.iterator(); itr.hasNext();) { result.append(itr.next()); result.append('|'); } result.append('\n'); sendCommandResponse(result.toString()); } void debugDropFrame(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } if (!thread.fFrames.isEmpty()) { thread.fCurrentFrame = thread.fFrames.remove(thread.fFrames.size() - 1); } thread.fCurrentFrame.fPC--; sendCommandResponse("ok\n"); //$NON-NLS-1$ if (fSuspendVM != null) { sendDebugEvent("vmresumed drop", false); //$NON-NLS-1$ sendDebugEvent("vmsuspended " + thread.fID + " drop", false); //$NON-NLS-1$ //$NON-NLS-2$ } else { sendDebugEvent("resumed " + thread.fID + " drop", false); //$NON-NLS-1$ //$NON-NLS-2$ sendDebugEvent("suspended " + thread.fID + " drop", false); //$NON-NLS-1$ //$NON-NLS-2$ } } void debugEval(Args args) { if (fSuspendVM != null) { sendCommandResponse("error: cannot evaluate while vm is suspended\n"); //$NON-NLS-1$ return; } PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } if (thread.fSuspend == null) { sendCommandResponse("error: thread running\n"); //$NON-NLS-1$ return; } StringTokenizer tokenizer = new StringTokenizer(args.getNextStringArg(), "|"); //$NON-NLS-1$ tokenizer.countTokens(); int numEvalLines = tokenizer.countTokens(); thread.fThreadCode = new String[fCode.length + numEvalLines + 1]; System.arraycopy(fCode, 0, thread.fThreadCode, 0, fCode.length); for (int i = 0; i < numEvalLines; i++) { String line = tokenizer.nextToken(); StringBuffer lineBuf = new StringBuffer(line.length()); Matcher matcher = fPackPattern.matcher(line); int lastMatchEnd = 0; while (matcher.find()) { lineBuf.append(line.substring(lastMatchEnd, matcher.start())); String charCode = line.substring(matcher.start() + 1, matcher.start() + 3); try { lineBuf.append((char) Integer.parseInt(charCode, 16)); } catch (NumberFormatException e) { } lastMatchEnd = matcher.end(); } if (lastMatchEnd < line.length()) { lineBuf.append(line.substring(lastMatchEnd)); } thread.fThreadCode[fCode.length + i] = lineBuf.toString(); } thread.fThreadCode[fCode.length + numEvalLines] = "xyzzy"; //$NON-NLS-1$ thread.fThreadLabels = mapLabels(fCode); thread.fSavedPC = thread.fCurrentFrame.fPC; thread.fCurrentFrame.fPC = fCode.length; thread.fPerformingEval = true; thread.fSuspend = null; sendCommandResponse("ok\n"); //$NON-NLS-1$ sendDebugEvent("resumed " + thread.fID + " eval", false); //$NON-NLS-1$ //$NON-NLS-2$ } void debugEventStop(Args args) { String event = args.getNextStringArg(); int stop = args.getNextIntArg(); fEventStops.put(event, Boolean.valueOf(stop > 0)); sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugTerminate() { sendCommandResponse("ok\n"); //$NON-NLS-1$ sendDebugEvent("vmterminated", false); //$NON-NLS-1$ System.exit(0); } void debugFrame(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } int sfnumber = args.getNextIntArg(); Frame frame = null; if (sfnumber >= thread.fFrames.size()) { frame = thread.fCurrentFrame; } else { frame = thread.fFrames.get(sfnumber); } sendCommandResponse(printFrame(frame) + "\n"); //$NON-NLS-1$ } /** * @param args */ void debugGroups(Args args) { TreeSet groups = new TreeSet<>(); for (Iterator itr = fRegisters.values().iterator(); itr.hasNext();) { Register reg = itr.next(); groups.add(reg.fGroup); } StringBuffer response = new StringBuffer(); for (Iterator itr = groups.iterator(); itr.hasNext();) { response.append(itr.next()); response.append('|'); } response.append('\n'); sendCommandResponse(response.toString()); } void debugPopData(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } thread.fStack.pop(); sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugPushData(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } Object val = args.getNextIntOrStringArg(); thread.fStack.push(val); sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugRegisters(Args args) { String group = args.getNextStringArg(); StringBuffer response = new StringBuffer(); for (Iterator itr = fRegisters.values().iterator(); itr.hasNext();) { Register reg = itr.next(); if (group.equals(reg.fGroup)) { response.append(reg.fName); response.append(' '); response.append(reg.fIsWriteable); for (Iterator itr2 = reg.fBitFields.values().iterator(); itr2.hasNext();) { BitField bitField = itr2.next(); response.append('|'); response.append(bitField.fName); response.append(' '); response.append(bitField.fBitOffset); response.append(' '); response.append(bitField.fBitCount); response.append(' '); for (Iterator> itr3 = bitField.fMnemonics.entrySet().iterator(); itr3.hasNext();) { Entry mnemonicEntry = itr3.next(); response.append(mnemonicEntry.getKey()); response.append(' '); response.append(mnemonicEntry.getValue()); response.append(' '); } } response.append('#'); } } response.append('\n'); sendCommandResponse(response.toString()); } /** * @param args */ void debugRestart(Args args) { fSuspendVM = "restart"; //$NON-NLS-1$ for (Iterator itr = fThreads.keySet().iterator(); itr.hasNext();) { Integer id = itr.next(); sendDebugEvent("exited " + id, false); //$NON-NLS-1$ } fThreads.clear(); int id = fNextThreadId++; fThreads.put(Integer.valueOf(id), new PDAThread(id, "main", 0)); //$NON-NLS-1$ sendDebugEvent("started " + id, false); //$NON-NLS-1$ fRegisters.clear(); sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugResume(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } if (fSuspendVM != null) { sendCommandResponse("error: cannot resume thread when vm is suspended\n"); //$NON-NLS-1$ return; } if (thread.fSuspend == null) { sendCommandResponse("error: thread already running\n"); //$NON-NLS-1$ return; } thread.fSuspend = null; sendDebugEvent("resumed " + thread.fID + " client", false); //$NON-NLS-1$ //$NON-NLS-2$ sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugSetBreakpoint(Args args) { int line = args.getNextIntArg(); int stopVM = args.getNextIntArg(); fBreakpoints.put(Integer.valueOf(line), Boolean.valueOf(stopVM != 0)); sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugSetData(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } int offset = args.getNextIntArg(); Object val = args.getNextIntOrStringArg(); if (offset < thread.fStack.size()) { thread.fStack.set(offset, val); } else { thread.fStack.add(0, val); } sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugSetVariable(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } int sfnumber = args.getNextIntArg(); String var = args.getNextStringArg(); Object val = args.getNextIntOrStringArg(); while (args.hasNextArg()) { val = val.toString() + " " + args.getNextStringArg(); //$NON-NLS-1$ } if (sfnumber >= thread.fFrames.size()) { thread.fCurrentFrame.set(var, val); } else { thread.fFrames.get(sfnumber).set(var, val); } sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugStack(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } StringBuffer result = new StringBuffer(); for (Iterator itr = thread.fFrames.iterator(); itr.hasNext();) { Frame frame = itr.next(); result.append(printFrame(frame)); result.append('#'); } result.append(printFrame(thread.fCurrentFrame)); result.append('\n'); sendCommandResponse(result.toString()); } void debugStackDepth(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } sendCommandResponse( Integer.toString(thread.fFrames.size() + 1) + "\n" ); //$NON-NLS-1$ } /** * The stack frame output is: frame # frame # frame ... where each frame is: * filename | line number | function name | var | var | var | var ... */ private String printFrame(Frame frame) { StringBuffer buf = new StringBuffer(); buf.append(fFilename); buf.append('|'); buf.append(frame.fPC); buf.append('|'); buf.append(frame.fFunction); for (Iterator itr = frame.fLocalVariables.keySet().iterator(); itr.hasNext();) { String var = itr.next(); if (var.indexOf('.') == -1) { buf.append('|'); buf.append(var); } } return buf.toString(); } void debugState(Args args) { PDAThread thread = args.getThreadArg(); String response = null; if (thread == null) { response = fSuspendVM == null ? "running" : fSuspendVM; //$NON-NLS-1$ } else if (fSuspendVM != null) { response = "vm"; //$NON-NLS-1$ } else { response = thread.fSuspend == null ? "running" : thread.fSuspend; //$NON-NLS-1$ } sendCommandResponse(response + "\n"); //$NON-NLS-1$ } void debugStep(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } // Set suspend to null to allow the debug loop to exit back to the // instruction loop and thus run an instruction. However, we want to // come back to the debug loop right away, so the step flag is set to // true which will cause the suspend flag to get set to true when we // get to the next instruction. if (fSuspendVM != null) { // All threads are suspended, so suspend all threads again when // step completes. fSuspendVM = null; fStepVM = true; // Also mark the thread that initiated the step to mark it as // the triggering thread when suspending. thread.fStep = true; } else { if (thread.fSuspend == null) { sendCommandResponse("error: thread already running\n"); //$NON-NLS-1$ return; } thread.fSuspend = null; thread.fStep = true; sendDebugEvent("resumed " + thread.fID + " step", false); //$NON-NLS-1$ //$NON-NLS-2$ } sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugStepReturn(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } if (fSuspendVM != null) { fSuspendVM = null; fStepReturnVM = true; thread.fStepReturn = true; } else { if (thread.fSuspend == null) { sendCommandResponse("error: thread running\n"); //$NON-NLS-1$ return; } thread.fSuspend = null; thread.fStepReturn = true; sendDebugEvent("resumed " + thread.fID + " step", false); //$NON-NLS-1$ //$NON-NLS-2$ } sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugSuspend(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } if (fSuspendVM != null) { sendCommandResponse("error: vm already suspended\n"); //$NON-NLS-1$ return; } if (thread.fSuspend != null) { sendCommandResponse("error: thread already suspended\n"); //$NON-NLS-1$ return; } thread.fSuspend = "client"; //$NON-NLS-1$ sendDebugEvent("suspended " + thread.fID + " client", false); //$NON-NLS-1$ //$NON-NLS-2$ sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugThreads() { StringBuffer response = new StringBuffer(); for (Iterator itr = fThreads.keySet().iterator(); itr.hasNext();) { response.append(itr.next()); response.append(' '); } sendCommandResponse(response.toString().trim() + "\n"); //$NON-NLS-1$ } void debugVar(Args args) { PDAThread thread = args.getThreadArg(); if (thread == null) { sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$ return; } int sfnumber = args.getNextIntArg(); String var = args.getNextStringArg(); Frame frame = sfnumber >= thread.fFrames.size() ? thread.fCurrentFrame : (Frame)thread.fFrames.get(sfnumber); Object val = frame.get(var); if (val == null) { sendCommandResponse("error: variable undefined\n"); //$NON-NLS-1$ } else { sendCommandResponse(val.toString() + "\n"); //$NON-NLS-1$ } } void debugVMResume() { if (fSuspendVM == null) { sendCommandResponse("error: vm already running\n"); //$NON-NLS-1$ return; } fSuspendVM = null; sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugVMSuspend() { if (fSuspendVM != null) { sendCommandResponse("error: vm already suspended\n"); //$NON-NLS-1$ return; } fSuspendVM = "client"; //$NON-NLS-1$ sendCommandResponse("ok\n"); //$NON-NLS-1$ } void debugWatch(Args args) { String funcAndVar = args.getNextStringArg(); int flags = args.getNextIntArg(); fWatchpoints.put(funcAndVar, Integer.valueOf(flags)); sendCommandResponse("ok\n"); //$NON-NLS-1$ } /** * @param thread * @param args */ void iAdd(PDAThread thread, Args args) { Object val1 = thread.fStack.pop(); Object val2 = thread.fStack.pop(); if (val1 instanceof Integer && val2 instanceof Integer) { int intVal1 = ((Integer) val1).intValue(); int intVal2 = ((Integer) val2).intValue(); thread.fStack.push( Integer.valueOf(intVal1 + intVal2) ); } else { thread.fStack.push( Integer.valueOf(-1) ); } } void iBranchNotZero(PDAThread thread, Args args) { Object val = thread.fStack.pop(); if (val instanceof Integer && ((Integer) val).intValue() != 0) { String label = args.getNextStringArg(); if (thread.fThreadLabels.containsKey(label)) { thread.fCurrentFrame.fPC = thread.fThreadLabels.get(label).intValue(); } else { sendDebugEvent("no such label " + label, true); //$NON-NLS-1$ if ( fEventStops.get("nosuchlabel").booleanValue() ) { //$NON-NLS-1$ fSuspendVM = thread.fID + " event nosuchlabel"; //$NON-NLS-1$ thread.fStack.push(val); thread.fCurrentFrame.fPC--; } } } } void iCall(PDAThread thread, Args args) { String label = args.getNextStringArg(); if (thread.fThreadLabels.containsKey(label)) { thread.fFrames.add(thread.fCurrentFrame); thread.fCurrentFrame = new Frame(label, thread.fThreadLabels.get(label).intValue()); } else { sendDebugEvent("no such label " + label, true); //$NON-NLS-1$ if ( fEventStops.get("nosuchlabel").booleanValue() ) { //$NON-NLS-1$ fSuspendVM = thread.fID + " event nosuchlabel"; //$NON-NLS-1$ thread.fCurrentFrame.fPC--; } } } /** * @param thread * @param args */ void iDec(PDAThread thread, Args args) { Object val = thread.fStack.pop(); if (val instanceof Integer) { val = Integer.valueOf(((Integer) val).intValue() - 1); } thread.fStack.push(val); } /** * @param thread * @param args */ void iDef(PDAThread thread, Args args) { String type = args.getNextStringArg(); String name = args.getNextStringArg(); String regName = getRegisterPartOfName(name); String bitFieldName = getBitFieldPartOfName(name); if ("register".equals(type)) { //$NON-NLS-1$ Register reg = new Register(regName); reg.fGroup = args.getNextStringArg(); fRegisters.put(regName, reg); reg.fIsWriteable = args.getNextBooleanArg(); } else if ("bitfield".equals(type)) { //$NON-NLS-1$ Register reg = fRegisters.get(regName); if (reg == null) { return; } BitField bitField = new BitField(bitFieldName); bitField.fBitOffset = args.getNextIntArg(); bitField.fBitCount = args.getNextIntArg(); reg.fBitFields.put(bitFieldName, bitField); } else if ("mnemonic".equals(type)) { //$NON-NLS-1$ Register reg = fRegisters.get(regName); if (reg == null) { return; } BitField bitField = reg.fBitFields.get(bitFieldName); if (bitField == null) { return; } bitField.fMnemonics.put(args.getNextStringArg(), Integer.valueOf(args.getNextIntArg())); } sendDebugEvent("registers", false); //$NON-NLS-1$ } private String getRegisterPartOfName(String name) { if (name.startsWith("$")) { //$NON-NLS-1$ int end = name.indexOf('.'); end = end != -1 ? end : name.length(); return name.substring(1, end); } return null; } private String getBitFieldPartOfName(String name) { int start = name.indexOf('.'); if (name.startsWith("$") && start != -1) { //$NON-NLS-1$ return name.substring(start + 1, name.length()); } return null; } /** * @param thread * @param args */ void iDup(PDAThread thread, Args args) { Object val = thread.fStack.pop(); thread.fStack.push(val); thread.fStack.push(val); } void iExec(PDAThread thread, Args args) { String label = args.getNextStringArg(); if (fLabels.containsKey(label)) { int id = fNextThreadId++; fThreads.put( Integer.valueOf(id), new PDAThread(id, label, fLabels.get(label).intValue()) ); sendDebugEvent("started " + id, false); //$NON-NLS-1$ } else { sendDebugEvent("no such label " + label, true); //$NON-NLS-1$ if ( fEventStops.get("nosuchlabel").booleanValue() ) { //$NON-NLS-1$ thread.fSuspend = "event nosuchlabel"; //$NON-NLS-1$ thread.fCurrentFrame.fPC--; } } } /** * @param thread * @param args */ void iHalt(PDAThread thread, Args args) { thread.fRun = false; } /** * @param thread * @param args */ void iOutput(PDAThread thread, Args args) { System.out.println(thread.fStack.pop()); } void iPop(PDAThread thread, Args args) { String arg = args.getNextStringArg(); if (arg.startsWith("$")) { //$NON-NLS-1$ String var = arg.substring(1); thread.fCurrentFrame.set(var, thread.fStack.pop()); String key = thread.fCurrentFrame.fFunction + "::" + var; //$NON-NLS-1$ if ( fWatchpoints.containsKey(key) && (fWatchpoints.get(key).intValue() & 2) != 0 ) { fSuspendVM = thread.fID + " watch write " + key; //$NON-NLS-1$ } } else { thread.fStack.pop(); } } void iPush(PDAThread thread, Args args) { String arg = args.getNextStringArg(); while (arg.length() != 0) { if (arg.startsWith("$")) { //$NON-NLS-1$ String var = arg.substring(1); Object val = thread.fCurrentFrame.get(var); if (val == null) { val = ""; //$NON-NLS-1$ } thread.fStack.push(val); String key = thread.fCurrentFrame.fFunction + "::" + var; //$NON-NLS-1$ if (fWatchpoints.containsKey(key) && (fWatchpoints.get(key).intValue() & 1) != 0) { fSuspendVM = thread.fID + " watch read " + key; //$NON-NLS-1$ } } else { Object val = arg; if (args.hasNextArg()) { while (args.hasNextArg()) { val = val.toString() + " " + args.getNextStringArg(); //$NON-NLS-1$ } } else { try { val = Integer.valueOf(arg); } catch (NumberFormatException e) { } } thread.fStack.push(val); } arg = args.getNextStringArg(); } } /** * @param thread * @param args */ void iReturn(PDAThread thread, Args args) { if (!thread.fFrames.isEmpty()) { thread.fCurrentFrame = thread.fFrames.remove(thread.fFrames.size() - 1); } else { // Execution returned from the top frame, which means this thread // should exit. thread.fRun = false; } } void iVar(PDAThread thread, Args args) { String var = args.getNextStringArg(); thread.fCurrentFrame.set(var, Integer.valueOf(0)); } /** * @param thread * @param args */ void iInternalEndEval(PDAThread thread, Args args) { Object result = thread.fStack.pop(); thread.fThreadCode = fCode; thread.fThreadLabels = fLabels; thread.fCurrentFrame.fPC = thread.fSavedPC; sendDebugEvent("evalresult " + result, false); //$NON-NLS-1$ thread.fSuspend = "eval"; //$NON-NLS-1$ thread.fPerformingEval = false; } }