| /******************************************************************************* |
| * Copyright (c) 2005, 2009 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) |
| *******************************************************************************/ |
| 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.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; |
| |
| public Object pop() { |
| return isEmpty() ? new Integer(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 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 ""; |
| } |
| |
| 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 new Integer(arg); |
| } catch (NumberFormatException e) { |
| } |
| return arg; |
| } |
| |
| PDAThread getThreadArg() { |
| int id = getNextIntArg(); |
| return (PDAThread)fThreads.get( new Integer(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("$")) { |
| 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 = (Register)fRegisters.get(getRegisterPartOfName(name)); |
| if (reg == null) return; |
| String bitFieldName = getBitFieldPartOfName(name); |
| if (bitFieldName != null) { |
| BitField 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 = (Integer)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 = (Register)fRegisters.get(getRegisterPartOfName(name)); |
| if (reg == null) return null; |
| String bitFieldName = getBitFieldPartOfName(name); |
| if (bitFieldName != null) { |
| BitField bitField = (BitField)reg.fBitFields.get(bitFieldName); |
| if (bitField == null) return null; |
| int bitFieldMask = 2^(bitField.fBitCount - 1); |
| int registerMask = bitFieldMask << bitField.fBitOffset; |
| return new Integer( (reg.fValue & registerMask) >> bitField.fBitOffset ); |
| } else { |
| return new Integer(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); |
| fEventStops.put("nosuchlabel", Boolean.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 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"); |
| 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 code = new LinkedList(); |
| 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 = (String[])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), new Integer(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++; |
| sendDebugEvent("vmstarted", false); |
| fThreads.put(new Integer(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 = (PDAThread[])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)) { |
| // 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(new Integer(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("vmterminated", 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 tokens = new LinkedList(); |
| while (tokenizer.hasMoreTokens()) { |
| tokens.add(tokenizer.nextToken()); |
| } |
| Args args = new Args( (String[])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 ( ((Boolean)fEventStops.get("unimpinstr")).booleanValue() ) { |
| 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 (Iterator itr = fThreads.values().iterator(); itr.hasNext();) { |
| PDAThread thread = (PDAThread)itr.next(); |
| Integer pc = new Integer(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 ( ((Boolean)fBreakpoints.get(pc)).booleanValue() ) { |
| 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 (Iterator itr = fThreads.values().iterator(); itr.hasNext();) { |
| PDAThread thread = (PDAThread)itr.next(); |
| 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 tokens = new LinkedList(); |
| while (tokenizer.hasMoreTokens()) { |
| tokens.add(tokenizer.nextToken()); |
| } |
| Args args = new Args( (String[])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 ("frame".equals(command)) debugFrame(args); |
| else if ("groups".equals(command)) debugGroups(args); |
| else if ("popdata".equals(command)) debugPopData(args); |
| else if ("pushdata".equals(command)) debugPushData(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 ("terminate".equals(command)) debugTerminate(); |
| 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 : (Frame)thread.fFrames.get(sfnumber); |
| |
| String varDot = var + "."; |
| List children = new ArrayList(); |
| for (Iterator itr = frame.fLocalVariables.keySet().iterator(); itr.hasNext();) { |
| String localVar = (String)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( new Integer(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 (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"); |
| return; |
| } |
| |
| if (!thread.fFrames.isEmpty()) { |
| thread.fCurrentFrame = (Frame)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, new Boolean(stop > 0)); |
| sendCommandResponse("ok\n"); |
| } |
| |
| void debugTerminate() { |
| sendCommandResponse("ok\n"); |
| sendDebugEvent("vmterminated", 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 = (Frame)thread.fFrames.get(sfnumber); |
| } |
| sendCommandResponse(printFrame(frame) + "\n"); |
| } |
| |
| void debugGroups(Args args) { |
| TreeSet groups = new TreeSet(); |
| for (Iterator itr = fRegisters.values().iterator(); itr.hasNext();) { |
| Register reg = (Register)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"); |
| return; |
| } |
| |
| thread.fStack.pop(); |
| sendCommandResponse("ok\n"); |
| } |
| |
| void debugPushData(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 (Iterator itr = fRegisters.values().iterator(); itr.hasNext();) { |
| Register reg = (Register)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 = (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();) { |
| Map.Entry mnemonicEntry = (Map.Entry)itr3.next(); |
| 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(new Integer(line), new Boolean(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(); |
| while (args.hasNextArg()) { |
| val = val.toString() + " " + args.getNextStringArg(); |
| } |
| |
| if (sfnumber >= thread.fFrames.size()) { |
| thread.fCurrentFrame.set(var, val); |
| } else { |
| ((Frame)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 (Iterator itr = thread.fFrames.iterator(); itr.hasNext();) { |
| Frame 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"); |
| 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 (Iterator itr = frame.fLocalVariables.keySet().iterator(); itr.hasNext();) { |
| String var = (String)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; |
| } 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 (Iterator itr = fThreads.keySet().iterator(); itr.hasNext();) { |
| response.append(itr.next()); |
| 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 : (Frame)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, new Integer(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( new Integer(intVal1 + intVal2) ); |
| } else { |
| thread.fStack.push( new Integer(-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 = ((Integer)thread.fThreadLabels.get(label)).intValue(); |
| } else { |
| sendDebugEvent("no such label " + label, true); |
| if ( ((Boolean)fEventStops.get("nosuchlabel")).booleanValue() ) { |
| 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, ((Integer)thread.fThreadLabels.get(label)).intValue()); |
| } else { |
| sendDebugEvent("no such label " + label, true); |
| if ( ((Boolean)fEventStops.get("nosuchlabel")).booleanValue() ) { |
| 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 = (Register)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 = (Register)fRegisters.get(regName); |
| if (reg == null) return; |
| BitField bitField = (BitField)reg.fBitFields.get(bitFieldName); |
| if (bitField == null) return; |
| bitField.fMnemonics.put(args.getNextStringArg(), new Integer(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( new Integer(id), new PDAThread(id, label, ((Integer)fLabels.get(label)).intValue()) ); |
| sendDebugEvent("started " + id, false); |
| } else { |
| sendDebugEvent("no such label " + label, true); |
| if ( ((Boolean)fEventStops.get("nosuchlabel")).booleanValue() ) { |
| 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) && (((Integer)fWatchpoints.get(key)).intValue() & 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) && (((Integer)fWatchpoints.get(key)).intValue() & 1) != 0) { |
| fSuspendVM = thread.fID + " watch read " + key; |
| } |
| } else { |
| Object val = arg; |
| if (args.hasNextArg()) { |
| while (args.hasNextArg()) { |
| val = val.toString() + " " + args.getNextStringArg(); |
| } |
| } else { |
| try { |
| val = new Integer(arg); |
| } catch (NumberFormatException e) { |
| } |
| } |
| thread.fStack.push(val); |
| } |
| |
| arg = args.getNextStringArg(); |
| } |
| } |
| |
| void iReturn(PDAThread thread, Args args) { |
| if (!thread.fFrames.isEmpty()) { |
| thread.fCurrentFrame = (Frame)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, new Integer(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; |
| } |
| |
| } |