diff options
Diffstat (limited to 'dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/src/org/eclipse/cdt/examples/pdavm/PDAVirtualMachine.java')
-rw-r--r-- | dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/src/org/eclipse/cdt/examples/pdavm/PDAVirtualMachine.java | 1378 |
1 files changed, 1378 insertions, 0 deletions
diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/src/org/eclipse/cdt/examples/pdavm/PDAVirtualMachine.java b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/src/org/eclipse/cdt/examples/pdavm/PDAVirtualMachine.java new file mode 100644 index 00000000000..5b40a1da7c7 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/src/org/eclipse/cdt/examples/pdavm/PDAVirtualMachine.java @@ -0,0 +1,1378 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.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.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + */ +@SuppressWarnings("serial") +public class PDAVirtualMachine { + + static class Stack extends LinkedList<Object> { + public Object pop() { + return isEmpty() ? 0 : remove(size() - 1); + } + + public void push(Object value) { + add(value); + } + } + + static class Register { + Register(String name) { + fName = name; + } + String fName; + String fGroup = "<no_group>"; + boolean fIsWriteable = true; + Map<String, BitField> fBitFields = new LinkedHashMap<String, BitField>(0); + int fValue; + } + + static class BitField { + BitField(String name) { + fName = name; + } + String fName; + int fBitOffset; + int fBitCount; + Map<String, Integer> fMnemonics = new LinkedHashMap<String, Integer>(0); + } + + Map<String,Register> fRegisters = new LinkedHashMap<String,Register>(0); + + class Args { + final String[] fArgs; + + int next = 0; + + Args(String[] args) { + fArgs = args; + } + + String getNextStringArg() { + if (fArgs.length > next) { + return fArgs[next++]; + } + return ""; + } + + int getNextIntArg() { + String arg = getNextStringArg(); + try { + return Integer.parseInt(arg); + } catch (NumberFormatException e) { + } + return 0; + } + + boolean getNextBooleanArg() { + String arg = getNextStringArg(); + try { + return Boolean.parseBoolean(arg); + } catch (NumberFormatException e) { + } + return false; + } + + Object getNextIntOrStringArg() { + String arg = getNextStringArg(); + try { + return Integer.parseInt(arg); + } catch (NumberFormatException e) { + } + return arg; + } + + PDAThread getThreadArg() { + int id = getNextIntArg(); + return fThreads.get(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<String, Integer> fThreadLabels; + + /** The stack of stack frames (the control stack) */ + final List<Frame> fFrames = new LinkedList<Frame>(); + + /** 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<Integer, PDAThread> fThreads = new LinkedHashMap<Integer, PDAThread>(); + + 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<String, Integer> fLabels; + + /** Each stack frame is a mapping of variable names to values. */ + class Frame { + final Map<String, Object> fLocalVariables = new LinkedHashMap<String, Object>(); + + /** + * 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("$")) { + setRegisterValue(name, value); + } else { + fLocalVariables.put(name, value); + } + } + + Object get(String name) { + if (name.startsWith("$")) { + 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 & 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 (reg.fValue & registerMask) >> bitField.fBitOffset; + } else { + return 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<Integer, Boolean> fBreakpoints = new HashMap<Integer, Boolean>(); + + /** + * 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<String, Boolean> fEventStops = new HashMap<String, Boolean>(); + { + fEventStops.put("unimpinstr", false); + fEventStops.put("nosuchlabel", false); + } + + /** + * The watchpoints table holds watchpoint information. + * <p/> + * variablename_stackframedepth => N + * <ul> + * <li>N = 0 is no watch</li> + * <li>N = 1 is read watch</li> + * <li>N = 2 is write watch</li> + * <li>N = 3 is both, etc.</li> + */ + final Map<String, Integer> fWatchpoints = new HashMap<String, Integer>(); + + public static void main(String[] args) { + String programFile = args.length >= 1 ? args[0] : null; + if (programFile == null) { + System.err.println("Error: No program specified"); + return; + } + + String debugFlag = args.length >= 2 ? args[1] : ""; + boolean debug = "-debug".equals(debugFlag); + int commandPort = 0; + int eventPort = 0; + + if (debug) { + String commandPortStr = args.length >= 3 ? args[2] : ""; + try { + commandPort = Integer.parseInt(commandPortStr); + } catch (NumberFormatException e) { + System.err.println("Error: Invalid command port"); + return; + } + + String eventPortStr = args.length >= 4 ? args[3] : ""; + try { + eventPort = Integer.parseInt(eventPortStr); + } catch (NumberFormatException e) { + System.err.println("Error: Invalid event port"); + return; + } + } + + PDAVirtualMachine pdaVM = null; + try { + pdaVM = new PDAVirtualMachine(programFile, debug, commandPort, eventPort); + pdaVM.startDebugger(); + } catch (IOException e) { + System.err.println("Error: " + e.toString()); + return; + } + pdaVM.run(); + } + + PDAVirtualMachine(String inputFile, boolean debug, int commandPort, int eventPort) throws IOException { + fFilename = inputFile; + + // Load all the code into memory + FileReader fileReader = new FileReader(inputFile); + StringWriter stringWriter = new StringWriter(); + List<String> code = new LinkedList<String>(); + 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<String, Integer> mapLabels(String[] code) { + Map<String, Integer> labels = new HashMap<String, Integer>(); + for (int i = 0; i < code.length; i++) { + if (code[i].length() != 0 && code[i].charAt(0) == ':') { + labels.put(code[i].substring(1), 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); + System.exit(1); + } + } else if (error) { + System.err.println("Error: " + event); + } + } + + void startDebugger() throws IOException { + if (fDebug) { + System.out.println("-debug " + fCommandPort + " " + fEventPort); + } + + ServerSocket commandServerSocket = new ServerSocket(fCommandPort); + fCommandSocket = commandServerSocket.accept(); + fCommandReceiveStream = new BufferedReader(new InputStreamReader(fCommandSocket.getInputStream())); + fCommandResponseStream = new PrintStream(fCommandSocket.getOutputStream()); + commandServerSocket.close(); + + ServerSocket eventServerSocket = new ServerSocket(fEventPort); + fEventSocket = eventServerSocket.accept(); + fEventStream = new PrintStream(fEventSocket.getOutputStream()); + eventServerSocket.close(); + + System.out.println("debug connection accepted"); + + fSuspendVM = "client"; + } + + void run() { + int id = fNextThreadId++; + fThreads.put(id, new PDAThread(id, "main", 0)); + if (fDebug) { + sendDebugEvent("started " + id, false); + } + + 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 (PDAThread thread : threadsCopy) { + 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)) { + // Note: this will only be triggered if the current + // thread also has the fStepReturn flag set. + if (fStepReturnVM) { + fSuspendVM = thread.fID + " step"; + } else { + thread.fSuspend = "step"; + } + } + } + if (!thread.fRun) { + sendDebugEvent("exited " + thread.fID, false); + fThreads.remove(thread.fID); + } else if (thread.fSuspend != null) { + sendDebugEvent("suspended " + thread.fID + " " + thread.fSuspend, false); + thread.fStep = thread.fStepReturn = thread.fPerformingEval = false; + } + } + } + + // Force thread context switch to avoid starving out other + // processes in the system. + Thread.yield(); + } + + sendDebugEvent("terminated", false); + if (fDebug) { + try { + fCommandReceiveStream.close(); + fCommandResponseStream.close(); + fCommandSocket.close(); + fEventStream.close(); + fEventSocket.close(); + } catch (IOException e) { + System.out.println("Error: " + e); + } + } + + } + + void doOneInstruction(PDAThread thread, String instr) { + StringTokenizer tokenizer = new StringTokenizer(instr); + String op = tokenizer.nextToken(); + List<String> tokens = new LinkedList<String>(); + while (tokenizer.hasMoreTokens()) { + tokens.add(tokenizer.nextToken()); + } + Args args = new Args(tokens.toArray(new String[tokens.size()])); + + boolean opValid = true; + if (op.equals("add")) iAdd(thread, args); + else if (op.equals("branch_not_zero")) iBranchNotZero(thread, args); + else if (op.equals("call")) iCall(thread, args); + else if (op.equals("dec")) iDec(thread, args); + else if (op.equals("def")) iDef(thread, args); + else if (op.equals("dup")) iDup(thread, args); + else if (op.equals("exec")) iExec(thread, args); + else if (op.equals("halt")) iHalt(thread, args); + else if (op.equals("output")) iOutput(thread, args); + else if (op.equals("pop")) iPop(thread, args); + else if (op.equals("push")) iPush(thread, args); + else if (op.equals("return")) iReturn(thread, args); + else if (op.equals("var")) iVar(thread, args); + else if (op.equals("xyzzy")) iInternalEndEval(thread, args); + else if (op.startsWith(":")) {} // label + else if (op.startsWith("#")) {} // comment + else { + opValid = false; + } + + if (!opValid) { + sendDebugEvent("unimplemented instruction " + op, true); + if (fEventStops.get("unimpinstr")) { + fSuspendVM = thread.fID + " event unimpinstr"; + thread.fCurrentFrame.fPC--; + } + } else if (thread.fStep) { + if (fStepVM) { + fSuspendVM = thread.fID + " step"; + fStepVM = false; + } else { + thread.fSuspend = "step"; + } + thread.fStep = false; + } + } + + void checkForBreakpoint() { + if (fDebug) { + for (PDAThread thread : fThreads.values()) { + int pc = 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)) { + fSuspendVM = thread.fID + " breakpoint " + pc; + } else { + thread.fSuspend = "breakpoint " + pc; + thread.fStep = thread.fStepReturn = false; + sendDebugEvent("suspended " + thread.fID + " " + thread.fSuspend, false); + } + } + } + } + } + + /** + * 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 = ""; + try { + if (allThreadsSuspended || fCommandReceiveStream.ready()) { + line = fCommandReceiveStream.readLine(); + processDebugCommand(line); + } + } catch (IOException e) { + System.err.println("Error: " + e); + System.exit(1); + } + } + } + + /** + * Service the debugger commands while the VM is suspended + */ + void debugUI() { + if (!fStarted) { + sendDebugEvent("vmsuspended " + fSuspendVM, false); + } 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 (PDAThread thread : fThreads.values()) { + thread.fSuspend = null; + thread.fStep = thread.fStepReturn = thread.fPerformingEval = false; + } + + while (fSuspendVM != null) { + String line = ""; + try { + line = fCommandReceiveStream.readLine(); + } catch (IOException e) { + System.err.println("Error: " + e); + System.exit(1); + return; + } + processDebugCommand(line); + } + + if (fStepVM || fStepReturnVM) { + sendDebugEvent("vmresumed step", false); + } else { + sendDebugEvent("vmresumed client", false); + } + } + + void processDebugCommand(String line) { + StringTokenizer tokenizer = new StringTokenizer(line.trim()); + if (line.length() == 0) { + return; + } + + String command = tokenizer.nextToken(); + List<String> tokens = new LinkedList<String>(); + while (tokenizer.hasMoreTokens()) { + tokens.add(tokenizer.nextToken()); + } + Args args = new Args(tokens.toArray(new String[tokens.size()])); + + if ("children".equals(command)) debugChildren(args); + else if ("clear".equals(command)) debugClearBreakpoint(args); + else if ("data".equals(command)) debugData(args); + else if ("drop".equals(command)) debugDropFrame(args); + else if ("eval".equals(command)) debugEval(args); + else if ("eventstop".equals(command)) debugEventStop(args); + else if ("exit".equals(command)) debugExit(); + else if ("frame".equals(command)) debugFrame(args); + else if ("groups".equals(command)) debugGroups(args); + else if ("popdata".equals(command)) debugPop(args); + else if ("pushdata".equals(command)) debugPush(args); + else if ("registers".equals(command)) debugRegisters(args); + else if ("resume".equals(command)) debugResume(args); + else if ("set".equals(command)) debugSetBreakpoint(args); + else if ("setdata".equals(command)) debugSetData(args); + else if ("setvar".equals(command)) debugSetVariable(args); + else if ("stack".equals(command)) debugStack(args); + else if ("stackdepth".equals(command)) debugStackDepth(args); + else if ("state".equals(command)) debugState(args); + else if ("step".equals(command)) debugStep(args); + else if ("stepreturn".equals(command)) debugStepReturn(args); + else if ("suspend".equals(command)) debugSuspend(args); + else if ("threads".equals(command)) debugThreads(); + else if ("var".equals(command)) debugVar(args); + else if ("vmresume".equals(command)) debugVMResume(); + else if ("vmsuspend".equals(command)) debugVMSuspend(); + else if ("watch".equals(command)) debugWatch(args); + else { + sendCommandResponse("error: invalid command\n"); + } + } + + void debugChildren(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + int sfnumber = args.getNextIntArg(); + String var = args.getNextStringArg(); + + Frame frame = sfnumber >= thread.fFrames.size() + ? thread.fCurrentFrame : thread.fFrames.get(sfnumber); + + String varDot = var + "."; + List<String> children = new ArrayList<String>(); + for (String localVar : frame.fLocalVariables.keySet()) { + if (localVar.startsWith(varDot) && localVar.indexOf('.', varDot.length() + 1) == -1) { + children.add(localVar); + } + } + + StringBuffer result = new StringBuffer(); + for (String child : children) { + result.append(child); + result.append('|'); + } + result.append('\n'); + + sendCommandResponse(result.toString()); + } + + void debugClearBreakpoint(Args args) { + int line = args.getNextIntArg(); + + fBreakpoints.remove(line); + sendCommandResponse("ok\n"); + } + + private static Pattern fPackPattern = Pattern.compile("%([a-fA-F0-9][a-fA-F0-9])"); + + void debugData(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + StringBuffer result = new StringBuffer(); + for (Object val : thread.fStack) { + result.append(val); + result.append('|'); + } + result.append('\n'); + sendCommandResponse(result.toString()); + } + + void debugDropFrame(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + if (!thread.fFrames.isEmpty()) { + thread.fCurrentFrame = thread.fFrames.remove(thread.fFrames.size() - 1); + } + thread.fCurrentFrame.fPC--; + sendCommandResponse("ok\n"); + if (fSuspendVM != null) { + sendDebugEvent("vmresumed drop", false); + sendDebugEvent("vmsuspended " + thread.fID + " drop", false); + } else { + sendDebugEvent("resumed " + thread.fID + " drop", false); + sendDebugEvent("suspended " + thread.fID + " drop", false); + } + } + + void debugEval(Args args) { + if (fSuspendVM != null) { + sendCommandResponse("error: cannot evaluate while vm is suspended\n"); + return; + } + + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + if (thread.fSuspend == null) { + sendCommandResponse("error: thread running\n"); + return; + } + + StringTokenizer tokenizer = new StringTokenizer(args.getNextStringArg(), "|"); + 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"; + thread.fThreadLabels = mapLabels(fCode); + + thread.fSavedPC = thread.fCurrentFrame.fPC; + thread.fCurrentFrame.fPC = fCode.length; + thread.fPerformingEval = true; + + thread.fSuspend = null; + + sendCommandResponse("ok\n"); + + sendDebugEvent("resumed " + thread.fID + " eval", false); + } + + void debugEventStop(Args args) { + String event = args.getNextStringArg(); + int stop = args.getNextIntArg(); + fEventStops.put(event, stop > 0); + sendCommandResponse("ok\n"); + } + + void debugExit() { + sendCommandResponse("ok\n"); + sendDebugEvent("terminated", false); + System.exit(0); + } + + void debugFrame(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + 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"); + } + + void debugGroups(Args args) { + TreeSet<String> groups = new TreeSet<String>(); + for (Register reg : fRegisters.values()) { + groups.add(reg.fGroup); + } + StringBuffer response = new StringBuffer(); + for (String group : groups) { + response.append(group); + response.append('|'); + } + response.append('\n'); + sendCommandResponse(response.toString()); + } + + void debugPop(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + thread.fStack.pop(); + sendCommandResponse("ok\n"); + } + + void debugPush(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + Object val = args.getNextIntOrStringArg(); + thread.fStack.push(val); + sendCommandResponse("ok\n"); + } + + void debugRegisters(Args args) { + String group = args.getNextStringArg(); + + StringBuffer response = new StringBuffer(); + for (Register reg : fRegisters.values()) { + if (group.equals(reg.fGroup)) { + response.append(reg.fName); + response.append(' '); + response.append(reg.fIsWriteable); + for (BitField bitField : reg.fBitFields.values()) { + response.append('|'); + response.append(bitField.fName); + response.append(' '); + response.append(bitField.fBitOffset); + response.append(' '); + response.append(bitField.fBitCount); + response.append(' '); + for (Map.Entry<String, Integer> mnemonicEntry : bitField.fMnemonics.entrySet()) { + response.append(mnemonicEntry.getKey()); + response.append(' '); + response.append(mnemonicEntry.getValue()); + response.append(' '); + } + } + + response.append('#'); + } + } + response.append('\n'); + sendCommandResponse(response.toString()); + } + + void debugResume(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + if (fSuspendVM != null) { + sendCommandResponse("error: cannot resume thread when vm is suspended\n"); + return; + } + if (thread.fSuspend == null) { + sendCommandResponse("error: thread already running\n"); + return; + } + + thread.fSuspend = null; + sendDebugEvent("resumed " + thread.fID + " client", false); + + sendCommandResponse("ok\n"); + } + + void debugSetBreakpoint(Args args) { + int line = args.getNextIntArg(); + int stopVM = args.getNextIntArg(); + + fBreakpoints.put(line, stopVM != 0); + sendCommandResponse("ok\n"); + } + + void debugSetData(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + 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"); + } + + void debugSetVariable(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + int sfnumber = args.getNextIntArg(); + String var = args.getNextStringArg(); + Object val = args.getNextIntOrStringArg(); + + if (sfnumber >= thread.fFrames.size()) { + thread.fCurrentFrame.set(var, val); + } else { + thread.fFrames.get(sfnumber).set(var, val); + } + sendCommandResponse("ok\n"); + } + + void debugStack(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + StringBuffer result = new StringBuffer(); + for (Frame frame : thread.fFrames) { + 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"); + return; + } + sendCommandResponse( Integer.toString(thread.fFrames.size() + 1) + "\n" ); + } + + + /** + * 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 (String var : frame.fLocalVariables.keySet()) { + 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; + } else if (fSuspendVM != null) { + response = "vm"; + } else { + response = thread.fSuspend == null ? "running" : thread.fSuspend; + } + sendCommandResponse(response + "\n"); + } + + void debugStep(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + 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"); + return; + } + thread.fSuspend = null; + thread.fStep = true; + sendDebugEvent("resumed " + thread.fID + " step", false); + } + sendCommandResponse("ok\n"); + } + + void debugStepReturn(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + if (fSuspendVM != null) { + fSuspendVM = null; + fStepReturnVM = true; + thread.fStepReturn = true; + } else { + if (thread.fSuspend == null) { + sendCommandResponse("error: thread running\n"); + return; + } + thread.fSuspend = null; + thread.fStepReturn = true; + sendDebugEvent("resumed " + thread.fID + " step", false); + } + sendCommandResponse("ok\n"); + } + + void debugSuspend(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + if (fSuspendVM != null) { + sendCommandResponse("error: vm already suspended\n"); + return; + } + if (thread.fSuspend != null) { + sendCommandResponse("error: thread already suspended\n"); + return; + } + + thread.fSuspend = "client"; + sendDebugEvent("suspended " + thread.fID + " client", false); + sendCommandResponse("ok\n"); + } + + void debugThreads() { + StringBuffer response = new StringBuffer(); + for (int threadId : fThreads.keySet()) { + response.append(threadId); + response.append(' '); + } + sendCommandResponse(response.toString().trim() + "\n"); + } + + void debugVar(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + int sfnumber = args.getNextIntArg(); + String var = args.getNextStringArg(); + + Frame frame = sfnumber >= thread.fFrames.size() + ? thread.fCurrentFrame : thread.fFrames.get(sfnumber); + + Object val = frame.get(var); + if (val == null) { + sendCommandResponse("error: variable undefined\n"); + } else { + sendCommandResponse(val.toString() + "\n"); + } + } + + void debugVMResume() { + if (fSuspendVM == null) { + sendCommandResponse("error: vm already running\n"); + return; + } + + fSuspendVM = null; + sendCommandResponse("ok\n"); + } + + void debugVMSuspend() { + if (fSuspendVM != null) { + sendCommandResponse("error: vm already suspended\n"); + return; + } + + fSuspendVM = "client"; + sendCommandResponse("ok\n"); + } + + void debugWatch(Args args) { + String funcAndVar = args.getNextStringArg(); + int flags = args.getNextIntArg(); + fWatchpoints.put(funcAndVar, flags); + sendCommandResponse("ok\n"); + } + + 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(intVal1 + intVal2); + } else { + thread.fStack.push(-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); + } else { + sendDebugEvent("no such label " + label, true); + if (fEventStops.get("nosuchlabel")) { + fSuspendVM = thread.fID + " event nosuchlabel"; + 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)); + } else { + sendDebugEvent("no such label " + label, true); + if (fEventStops.get("nosuchlabel")) { + fSuspendVM = thread.fID + " event nosuchlabel"; + thread.fCurrentFrame.fPC--; + } + } + } + + void iDec(PDAThread thread, Args args) { + Object val = thread.fStack.pop(); + if (val instanceof Integer) { + val = new Integer(((Integer) val).intValue() - 1); + } + thread.fStack.push(val); + } + + 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)) { + Register reg = new Register(regName); + reg.fGroup = args.getNextStringArg(); + fRegisters.put(regName, reg); + reg.fIsWriteable = args.getNextBooleanArg(); + } else if ("bitfield".equals(type)) { + 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)) { + Register reg = fRegisters.get(regName); + if (reg == null) return; + BitField bitField = reg.fBitFields.get(bitFieldName); + if (bitField == null) return; + bitField.fMnemonics.put(args.getNextStringArg(), args.getNextIntArg()); + } + sendDebugEvent("registers", false); + } + + private String getRegisterPartOfName(String name) { + if (name.startsWith("$")) { + 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) { + return name.substring(start + 1, name.length()); + } + return null; + } + + 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(id, new PDAThread(id, label, fLabels.get(label))); + sendDebugEvent("started " + id, false); + } else { + sendDebugEvent("no such label " + label, true); + if (fEventStops.get("nosuchlabel")) { + thread.fSuspend = "event nosuchlabel"; + thread.fCurrentFrame.fPC--; + } + } + } + + void iHalt(PDAThread thread, Args args) { + thread.fRun = false; + } + + 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("$")) { + String var = arg.substring(1); + thread.fCurrentFrame.set(var, thread.fStack.pop()); + String key = thread.fCurrentFrame.fFunction + "::" + var; + if (fWatchpoints.containsKey(key) && (fWatchpoints.get(key) & 2) != 0) { + fSuspendVM = thread.fID + " watch write " + key; + } + } else { + thread.fStack.pop(); + } + } + + void iPush(PDAThread thread, Args args) { + String arg = args.getNextStringArg(); + while (arg.length() != 0) { + if (arg.startsWith("$")) { + String var = arg.substring(1); + Object val = thread.fCurrentFrame.get(var); + if (val == null) val = "<undefined>"; + thread.fStack.push(val); + String key = thread.fCurrentFrame.fFunction + "::" + var; + if (fWatchpoints.containsKey(key) && (fWatchpoints.get(key) & 1) != 0) { + fSuspendVM = thread.fID + " watch read " + key; + } + } else { + Object val = arg; + try { + val = Integer.parseInt(arg); + } catch (NumberFormatException e) { + } + thread.fStack.push(val); + } + + arg = args.getNextStringArg(); + } + } + + 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, 0); + } + + 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); + thread.fSuspend = "eval"; + thread.fPerformingEval = false; + } + +} |