Thread: [Pydev-cvs] org.python.pydev.debug/src/org/python/pydev/debug/model/remote VersionCommand.java,NONE,
Brought to you by:
fabioz
From: Aleksandar T. <at...@us...> - 2004-05-05 02:02:09
|
Update of /cvsroot/pydev/org.python.pydev.debug/src/org/python/pydev/debug/model/remote In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17261/src/org/python/pydev/debug/model/remote Added Files: VersionCommand.java ICommandResponseListener.java ThreadListCommand.java GetVariableCommand.java StepCommand.java RemoteDebugger.java ThreadKillCommand.java ThreadSuspendCommand.java ThreadRunCommand.java AbstractDebuggerCommand.java Log Message: variables can now be displayed --- NEW FILE: VersionCommand.java --- /* * Author: atotic * Created on Apr 19, 2004 * License: Common Public License v1.0 */ package org.python.pydev.debug.model.remote; /** * Version debugger command. * * See protocol definition for more info. Used as */ public class VersionCommand extends AbstractDebuggerCommand { static final String VERSION = "1.0"; /** * @param debugger */ public VersionCommand(RemoteDebugger debugger) { super(debugger); } public String getOutgoing() { return makeCommand(Integer.toString(CMD_VERSION), sequence, VERSION); } public boolean needResponse() { return true; } public void processOKResponse(int cmdCode, String payload) { // System.err.println("The version is " + payload); // not checking for versioning in 1.0, might come in useful later } } --- NEW FILE: ThreadRunCommand.java --- /* * Author: atotic * Created on Apr 22, 2004 * License: Common Public License v1.0 */ package org.python.pydev.debug.model.remote; /** * Run thread network command */ public class ThreadRunCommand extends AbstractDebuggerCommand { String thread; public ThreadRunCommand(RemoteDebugger debugger, String thread) { super(debugger); this.thread = thread; } public String getOutgoing() { return makeCommand(Integer.toString(CMD_THREAD_RUN), sequence, thread); } } --- NEW FILE: RemoteDebugger.java --- /* * Author: atotic * Created on Mar 23, 2004 * License: Common Public License v1.0 */ package org.python.pydev.debug.model.remote; import java.io.*; import java.net.*; import java.util.ArrayList; import java.util.Hashtable; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.model.IProcess; import org.python.pydev.debug.core.PydevDebugPlugin; import org.python.pydev.debug.model.PyDebugTarget; import org.python.pydev.debug.ui.launching.PythonRunnerConfig; /** * Network interface to the remote debugger. */ public class RemoteDebugger extends Object { private int sequence = -1; // sequence seed for command numbers private Socket socket; // connection socket private Reader reader; // reading thread private Writer writer; // writing thread private ListenConnector connector; // Runnable that connects to the debugger private Thread connectThread; // private PythonRunnerConfig config; private PyDebugTarget target = null; protected class ListenConnector implements Runnable { int port; int timeout; ServerSocket serverSocket; Socket socket; // what got accepted Exception e; boolean terminated; public ListenConnector(int port, int timeout) throws IOException { this.port = port; this.timeout = timeout; serverSocket = new ServerSocket(port); } Exception getException() { return e; } public Socket getSocket() { return socket; } public void stopListening() { if (serverSocket != null) try { serverSocket.close(); } catch (IOException e) { PydevDebugPlugin.log(IStatus.WARNING, "Error closing pydevd socket", e); } terminated = true; } public void run() { try { serverSocket.setSoTimeout(timeout); socket = serverSocket.accept(); } catch (IOException e) { this.e = e; } } } /** * Writer writes debugger commands to the network. * Use postCommand to put new ones in queue. */ protected class Writer implements Runnable { ArrayList cmdQueue; // a list of RemoteDebuggerCommands OutputStreamWriter out; boolean done; public Writer(Socket s) throws IOException { done = false; cmdQueue = new ArrayList(); OutputStream sout; sout = s.getOutputStream(); out = new OutputStreamWriter(sout); } /** * Add command for processing */ public void postCommand(AbstractDebuggerCommand cmd) { synchronized(cmdQueue) { cmdQueue.add(cmd); } } public void done() { this.done = true; } /** * Loops and writes commands to the output */ public void run() { while (!done) { AbstractDebuggerCommand cmd = null; synchronized (cmdQueue) { if (cmdQueue.size() > 0) cmd = (AbstractDebuggerCommand) cmdQueue.remove(0); } try { if (cmd != null) { cmd.aboutToSend(); out.write(cmd.getOutgoing()); out.write("\n"); out.flush(); } Thread.sleep(100); } catch (InterruptedException e) { done = true; } catch (IOException e1) { done = true; } } } } /** * Reads and dispatches commands */ protected class Reader implements Runnable { boolean done; Hashtable responseQueue; // commands waiting for response. Their keys are the sequence ids BufferedReader in; public Reader(Socket socket) throws IOException { done = false; responseQueue = new Hashtable(); InputStream sin = socket.getInputStream(); in = new BufferedReader(new InputStreamReader(sin)); } public void done() { this.done = true; } public void addToResponseQueue(AbstractDebuggerCommand cmd) { responseQueue.put(new Integer(cmd.getSequence()), cmd); Object o = responseQueue.remove(new Integer(cmd.getSequence())); responseQueue.put(new Integer(cmd.getSequence()), cmd); } /** * Parses & dispatches the command */ private void processCommand(String cmdLine) { int cmdCode; int seqCode; String payload; String[] cmdParsed = cmdLine.split("\t", 3); cmdCode = Integer.parseInt(cmdParsed[0]); seqCode = Integer.parseInt(cmdParsed[1]); payload = URLDecoder.decode(cmdParsed[2]); // is there a response waiting AbstractDebuggerCommand cmd = (AbstractDebuggerCommand)responseQueue.remove(new Integer(seqCode)); if (cmd == null) if (target != null) target.processCommand(cmdParsed[0], cmdParsed[1], payload); else PydevDebugPlugin.log(IStatus.ERROR, "internal error, command received no target", null); else cmd.processResponse(cmdCode, payload); } public void run() { while (!done) { try { if (in.ready()) { String cmdLine = in.readLine(); processCommand(cmdLine); } Thread.sleep(100); } catch (IOException e) { done = true; } catch (InterruptedException e1) { e1.printStackTrace(); } if ((socket == null) || !socket.isConnected()) { if (target != null) { target.debuggerDisconnected(); } done = true; } } } } public RemoteDebugger(PythonRunnerConfig config) { this.config = config; } public void setTarget(PyDebugTarget target) { this.target = target; } public void startTransmission() throws IOException { this.reader = new Reader(socket); this.writer = new Writer(socket); Thread t = new Thread(reader, "pydevd.reader"); t.start(); t = new Thread(writer, "pydevd.writer"); t.start(); writer.postCommand(new VersionCommand(this)); } /** * @return next available debugger command sequence number */ public int getNextSequence() { sequence += 2; return sequence; } public void startConnect(IProgressMonitor monitor) throws IOException, CoreException { monitor.subTask("Finding free socket..."); connector = new ListenConnector(config.getDebugPort(), config.acceptTimeout); connectThread = new Thread(connector, "pydevd.connect"); connectThread.start(); } /** * Wait for the connection to the debugger to complete. * * If this method returns without an exception, we've connected. * @return true if operation was cancelled by user */ public boolean waitForConnect(IProgressMonitor monitor, Process p, IProcess ip) throws Exception { // Launch the debug listener on a thread, and wait until it completes while (connectThread.isAlive()) { if (monitor.isCanceled()) { connector.stopListening(); p.destroy(); return true; } try { p.exitValue(); // throws exception if process has terminated // process has terminated - stop waiting for a connection connector.stopListening(); String errorMessage= ip.getStreamsProxy().getErrorStreamMonitor().getContents(); if (errorMessage.length() != 0) // not sure if this is really an error throw new CoreException(new Status(IStatus.ERROR, PydevDebugPlugin.getPluginID(), 0, "Something got printed in the error stream", null)); } catch (IllegalThreadStateException e) { // expected while process is alive } try { Thread.sleep(100); } catch (InterruptedException e) { } } if (connector.getException() != null) throw connector.getException(); connected(connector.getSocket()); return false; } /** * Remote debugger has connected */ public void connected(Socket socket) throws IOException { this.socket = socket; } public void disconnect() { try { if (socket != null) { socket.shutdownInput(); // trying to make my pydevd notice that the socket is gone socket.shutdownOutput(); socket.close(); } } catch (IOException e) { e.printStackTrace(); // it is going away } socket = null; if (target != null) target.debuggerDisconnected(); } /** * Dispose must be called to clean up. * Because we call this from PyDebugTarget.terminate, we can be called multiple times * But, once dispose() is called, no other calls will be made. */ public void dispose() { if (connector != null) { connector.stopListening(); connector = null; } if (writer != null) { writer.done(); writer = null; } if (reader != null) { reader.done(); reader = null; } disconnect(); target = null; } public void addToResponseQueue(AbstractDebuggerCommand cmd) { reader.addToResponseQueue(cmd); } public void postCommand(AbstractDebuggerCommand cmd) { writer.postCommand(cmd); } } --- NEW FILE: ThreadKillCommand.java --- /* * Author: atotic * Created on Apr 22, 2004 * License: Common Public License v1.0 */ package org.python.pydev.debug.model.remote; /** * KILL_THREAD debugger command * */ public class ThreadKillCommand extends AbstractDebuggerCommand { String thread_id; public ThreadKillCommand(RemoteDebugger debugger, String thread_id) { super(debugger); this.thread_id = thread_id; } public String getOutgoing() { return makeCommand(Integer.toString(CMD_THREAD_KILL), sequence, thread_id); } } --- NEW FILE: AbstractDebuggerCommand.java --- /* * Author: atotic * Created on Mar 23, 2004 * License: Common Public License v1.0 */ package org.python.pydev.debug.model.remote; import org.eclipse.core.runtime.IStatus; import org.python.pydev.debug.core.PydevDebugPlugin; /** * Superclass of all debugger commands. * * Debugger commands know how to interact with pydevd.py. * See pydevd.py for protocol information. * * Command lifecycle: * cmd = new Command() // creation * cmd.getSequence() // get the sequence number of the command * cmd.getOutgoing() // asks command for outgoing message * cmd.aboutToSend() // called right before we go on wire * // by default, if command needs response * // it gets posted to in the response queue * if (cmd.needsResponse()) * post the command to response queue, otherwise we are done * when response arrives: * if response is an error * cmd.processResponse() * else * cmd.processErrorResponse() * */ public abstract class AbstractDebuggerCommand { static public final int CMD_LIST_THREADS = 102; static public final int CMD_THREAD_CREATED = 103; static public final int CMD_THREAD_KILL = 104; static public final int CMD_THREAD_SUSPEND = 105; static public final int CMD_THREAD_RUN = 106; static public final int CMD_STEP_INTO = 107; static public final int CMD_STEP_OVER = 108; static public final int CMD_STEP_RETURN = 109; static public final int CMD_GET_VARIABLE = 110; static public final int CMD_ERROR = 901; static public final int CMD_VERSION = 501; static public final int CMD_RETURN = 502; protected RemoteDebugger debugger; protected ICommandResponseListener responseListener; int sequence; public AbstractDebuggerCommand(RemoteDebugger debugger) { this.debugger = debugger; this.responseListener = null; sequence = debugger.getNextSequence(); } public void setCompletionListener(ICommandResponseListener listener) { this.responseListener = listener; } /** * @return outgoing message */ public abstract String getOutgoing(); /** * Notification right before the command is sent. * If subclassed, call super() */ public void aboutToSend() { // if we need a response, put me on the waiting queue if (needResponse()) debugger.addToResponseQueue(this); } /** * Does this command require a response? */ public boolean needResponse() { return false; } /** * returns Sequence # */ public final int getSequence() { return sequence; } /** * Called when command completes, if needResponse was true */ public final void processResponse(int cmdCode, String payload) { if (cmdCode / 100 == 9) processErrorResponse(cmdCode, payload); else processOKResponse(cmdCode, payload); if (responseListener != null) responseListener.commandComplete(this); } /** * notification of the response to the command. * You'll get either processResponse or processErrorResponse */ public void processOKResponse(int cmdCode, String payload) { PydevDebugPlugin.log(IStatus.ERROR, "Debugger command ignored response " + getClass().toString() + payload, null); } /** * notification of the response to the command. * You'll get either processResponse or processErrorResponse */ public void processErrorResponse(int cmdCode, String payload) { PydevDebugPlugin.log(IStatus.ERROR, "Debugger command ignored error response " + getClass().toString() + payload, null); } public static String makeCommand(String code, int sequence, String payload) { StringBuffer s = new StringBuffer(); s.append(code); s.append("\t"); s.append(sequence); s.append("\t"); s.append(payload); return s.toString(); } } --- NEW FILE: ThreadListCommand.java --- /* * Author: atotic * Created on Apr 21, 2004 * License: Common Public License v1.0 */ package org.python.pydev.debug.model.remote; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.debug.core.model.IThread; import org.python.pydev.debug.core.PydevDebugPlugin; import org.python.pydev.debug.model.*; import org.python.pydev.debug.model.PyDebugTarget; /** * ListThreads command. * * See protocol for more info */ public class ThreadListCommand extends AbstractDebuggerCommand { boolean done; PyDebugTarget target; IThread[] threads; public ThreadListCommand(RemoteDebugger debugger, PyDebugTarget target) { super(debugger); this.target = target; done = false; } public void waitUntilDone(int timeout) throws InterruptedException { while (!done && timeout > 0) { timeout -= 100; Thread.sleep(100); } if (timeout < 0) throw new InterruptedException(); } public IThread[] getThreads() { return threads; } public String getOutgoing() { return makeCommand(Integer.toString(CMD_LIST_THREADS), sequence, ""); } public boolean needResponse() { return true; } /** * The response is a list of threads */ public void processOKResponse(int cmdCode, String payload) { if (cmdCode != 102) { PydevDebugPlugin.log(IStatus.ERROR, "Unexpected response to LIST THREADS" + payload, null); return; } try { threads = XMLUtils.ThreadsFromXML(target, payload); } catch (CoreException e) { PydevDebugPlugin.log(IStatus.ERROR, "LIST THREADS got an unexpected response " + payload, null); e.printStackTrace(); } done = true; } public void processErrorResponse(int cmdCode, String payload) { PydevDebugPlugin.log(IStatus.ERROR, "LIST THREADS got an error " + payload, null); } } --- NEW FILE: ThreadSuspendCommand.java --- /* * Author: atotic * Created on Apr 22, 2004 * License: Common Public License v1.0 */ package org.python.pydev.debug.model.remote; /** * Suspend thread network command. * * See protocol docs for more info. */ public class ThreadSuspendCommand extends AbstractDebuggerCommand { String thread; public ThreadSuspendCommand(RemoteDebugger debugger, String thread) { super(debugger); this.thread = thread; } public String getOutgoing() { return makeCommand(Integer.toString(CMD_THREAD_SUSPEND), sequence, thread); } } --- NEW FILE: ICommandResponseListener.java --- /* * Author: atotic * Created on May 4, 2004 * License: Common Public License v1.0 */ package org.python.pydev.debug.model.remote; /** * All commands are executed asynchronously. * * This interface, if specified, is called when command completes. */ public interface ICommandResponseListener { public void commandComplete(AbstractDebuggerCommand cmd); } --- NEW FILE: StepCommand.java --- /* * Author: atotic * Created on Apr 27, 2004 * License: Common Public License v1.0 */ package org.python.pydev.debug.model.remote; /** * Debugger step command. */ public class StepCommand extends AbstractDebuggerCommand { int command_id; String thread_id; /** * * @param command_id CMD_STEP_INTO CMD_STEP_OVER CMD_STEP_RETURN */ public StepCommand(RemoteDebugger debugger, int command_id, String thread_id) { super(debugger); this.command_id = command_id; this.thread_id = thread_id; } public String getOutgoing() { return makeCommand(Integer.toString(command_id), sequence, thread_id); } } --- NEW FILE: GetVariableCommand.java --- /* * Author: atotic * Created on Apr 30, 2004 * License: Common Public License v1.0 */ package org.python.pydev.debug.model.remote; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.python.pydev.debug.core.PydevDebugPlugin; /** * GetVariable network command. * * GetVariable gets the value of the variable from network as XML. * The caller can busy-wait for the response. */ public class GetVariableCommand extends AbstractDebuggerCommand { String locator; boolean isError = false; int responseCode; String payload; public GetVariableCommand(RemoteDebugger debugger, String locator) { super(debugger); this.locator = locator; } public String getOutgoing() { return makeCommand(Integer.toString(CMD_GET_VARIABLE), sequence, locator); } public boolean needResponse() { return true; } public void processOKResponse(int cmdCode, String payload) { responseCode = cmdCode; if (cmdCode == CMD_GET_VARIABLE) this.payload = payload; else { isError = true; PydevDebugPlugin.log(IStatus.ERROR, "Unexpected response to GetVariableCommand", null); } } public void processErrorResponse(int cmdCode, String payload) { responseCode = cmdCode; this.payload = payload; isError = true; } public String getResponse() throws CoreException { if (isError) throw new CoreException( new Status(IStatus.ERROR, PydevDebugPlugin.getPluginID(), 0, "pydevd error:" + payload , null)); else return payload; } } |