Initial implementation of the new Debug Plugin
[phpeclipse.git] / net.sourceforge.phpeclipse.xdebug.core / src / net / sourceforge / phpeclipse / xdebug / php / model / XDebugTarget.java
diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugTarget.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugTarget.java
new file mode 100644 (file)
index 0000000..b8d3f86
--- /dev/null
@@ -0,0 +1,528 @@
+/**
+ * 
+ */
+package net.sourceforge.phpeclipse.xdebug.php.model;
+
+import net.sourceforge.phpeclipse.xdebug.core.Base64;
+import net.sourceforge.phpeclipse.xdebug.core.DebugConnection;
+import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils;
+import net.sourceforge.phpeclipse.xdebug.core.XDebugCorePlugin;
+import net.sourceforge.phpeclipse.xdebug.core.DebugConnection.DebugResponse;
+import net.sourceforge.phpeclipse.xdebug.php.launching.IXDebugConstants;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IMarkerDelta;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.ILineBreakpoint;
+import org.eclipse.debug.core.model.IMemoryBlock;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.debug.core.model.IThread;
+import org.eclipse.debug.core.model.IValue;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * @author Christian
+ *
+ */
+public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugEventSetListener{
+       // associated system process (VM)
+       private IProcess fProcess;
+       
+       // containing launch object
+       private ILaunch fLaunch;
+       
+       // debugPort
+       private int fDebugPort;
+       
+       // program name
+//     private String fName;
+       
+       
+       // suspend state
+       private boolean fSuspended = true;
+       
+       // terminated state
+       private boolean fTerminated = false;
+       
+       // threads
+       private XDebugThread fThread;
+       private IThread[] fThreads;
+       
+       // event dispatch job
+//     private EventDispatchJob fEventDispatch;
+       
+
+       private DebugConnection fDebugConnection;
+//     private DebugResponse lastResponse;
+
+
+       
+       /**
+        * Constructs a new debug target in the given launch for the 
+        * associated PDA VM process.
+        * 
+        * @param launch containing launch
+        * @param debugPort port to read events from
+        * @exception CoreException if unable to connect to host
+        */
+       public XDebugTarget(ILaunch launch, IProcess process, int debugPort) throws CoreException {
+               super(null);
+               fLaunch = launch;
+               fProcess = process;
+               fTarget = this;
+               fDebugConnection= new DebugConnection(this,debugPort);
+               fThread = new XDebugThread(this);
+               fThreads = new IThread[] {fThread};
+               DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
+               DebugPlugin.getDefault().addDebugEventListener(this);
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
+        */
+       public IProcess getProcess() {
+               return fProcess;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
+        */
+       public IThread[] getThreads() throws DebugException {
+               return fThreads;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
+        */
+       public boolean hasThreads() throws DebugException {
+               return (fThreads.length>0);
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IDebugTarget#getName()
+        */
+       public String getName() throws DebugException {
+               return "PHP XDebug Client at localhost:" + fDebugPort;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
+        */
+       public boolean supportsBreakpoint(IBreakpoint breakpoint) {
+               if (breakpoint.getModelIdentifier().equals(IXDebugConstants.ID_PHP_DEBUG_MODEL)) {
+                       return true;
+               }
+               return false;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
+        */
+       public IDebugTarget getDebugTarget() {
+               return this;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
+        */
+       public ILaunch getLaunch() {
+               return fLaunch;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
+        */
+       public boolean canTerminate() {
+               return getProcess().canTerminate();
+//             return false;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
+        */
+       public boolean isTerminated() {
+//             return getProcess().isTerminated();
+               return fTerminated;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.ITerminate#terminate()
+        */
+       public void terminate() throws DebugException {
+               fDebugConnection.sendRequest ("stop");
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
+        */
+       public boolean canResume() {
+               return !isTerminated() && isSuspended();
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
+        */
+       public boolean canSuspend() {
+               return !isTerminated() && !isSuspended();
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
+        */
+       public boolean isSuspended() {
+               return fSuspended;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.ISuspendResume#resume()
+        */
+       public void resume() throws DebugException {
+               fDebugConnection.sendRequest("run");
+       }
+       
+       /**
+        * Notification the target has resumed for the given reason
+        * 
+        * @param detail reason for the resume
+        */
+       private void resumed(int detail) {
+               fSuspended = false;
+               fThread.fireResumeEvent(detail);
+       }
+       
+       /**
+        * Notification the target has suspended for the given reason
+        * 
+        * @param detail reason for the suspend
+        */
+       public void suspended(int detail) {
+               fSuspended = true;
+               fThread.fireSuspendEvent(detail);
+       }       
+       
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
+        */
+       public void suspend() throws DebugException {
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
+        */
+       public void breakpointAdded(IBreakpoint breakpoint) {
+               
+               if (supportsBreakpoint(breakpoint)) {
+                       try {
+                               if (breakpoint.isEnabled()) {
+                                       IMarker marker = breakpoint.getMarker();
+                                       if (marker != null) {
+                                               try {
+                                                       String fileName = PHPDebugUtils.escapeString(marker.getResource().getLocation().toString());
+                                                       String arg="-t line -f file:///"+fileName+" -n "+((ILineBreakpoint)breakpoint).getLineNumber();
+                                                       int id =fDebugConnection.sendRequest("breakpoint_set",arg);
+       // set the marker Attribute to make later idetification possible
+// TODO: make sure that attribute is set before response from debugger is beeing prosessed                                             
+                                                       marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,id);
+                                                       
+                                               } catch (CoreException e) {
+                                               }
+                                       }
+                               }
+                       } catch (CoreException e) {
+
+                       }
+               }
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
+        */
+       public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
+               if (supportsBreakpoint(breakpoint)) {
+                       try {
+                               int id =((XDebugLineBreakpoint)breakpoint).getID();
+                               if (id >0)
+                                       fDebugConnection.sendRequest("breakpoint_remove","-d "+id);
+                       } catch (CoreException e) {
+                       }
+               }
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
+        */
+       public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
+//             if (supportsBreakpoint(breakpoint)) {
+//                     try {
+//                             if (breakpoint.isEnabled()) {
+//                                     breakpointAdded(breakpoint);
+//                             } else {
+//                                     breakpointRemoved(breakpoint, null);
+//                             }
+//                     } catch (CoreException e) {
+//                     }
+//             }
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
+        */
+       public boolean canDisconnect() {
+               return false;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
+        */
+       public void disconnect() throws DebugException {
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
+        */
+       public boolean isDisconnected() {
+               return false;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
+        */
+       public boolean supportsStorageRetrieval() {
+               return false;
+       }
+       /* (non-Javadoc)
+        * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
+        */
+       public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
+               return null;
+       }
+
+       /**
+        * Notification we have connected to the PHP debugger and it has started.
+        * Resume the the debugger.
+        */
+       public void started() {
+
+               fThread.setBreakpoints(null);
+               fThread.setStepping(false);
+
+               installDeferredBreakpoints();
+               try {
+                       resume();
+//                     step();
+               } catch (DebugException e) {
+               }
+       }
+       
+       /**
+        * Install breakpoints that are already registered with the breakpoint
+        * manager.
+        */
+       private void installDeferredBreakpoints() {
+               IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
+               for (int i = 0; i < breakpoints.length; i++) {
+                       breakpointAdded(breakpoints[i]);
+               }
+       }
+       
+       /**
+        * Called when this debug target terminates.
+        */
+       public void terminated() {
+               fTerminated = true;
+               fSuspended = false;
+               XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this);
+               fireTerminateEvent();
+               DebugPlugin.getDefault().removeDebugEventListener(this);
+               fThread.removeEventListeners();
+       }
+       
+       /**
+        * Returns the current stack frames in the target.
+        * 
+        * @return the current stack frames in the target
+        * @throws DebugException if unable to perform the request
+        */
+       protected IStackFrame[] getStackFrames() throws DebugException {
+               int id=fDebugConnection.sendRequest("stack_get");
+               DebugResponse lastResponse=fDebugConnection.waitforTransID(id);
+               if (lastResponse.isError())
+                       return new IStackFrame[0];
+               Node response = lastResponse.getParentNode();
+               NodeList frames = response.getChildNodes();
+               IStackFrame[] theFrames = new IStackFrame[frames.getLength()];
+               for (int i = 0; i < frames.getLength(); i++) {
+                       Node stackNode = frames.item(i);
+                       theFrames[i] = new XDebugStackFrame(fThread, stackNode, i);
+               }
+               return theFrames;
+       }
+       
+       /**
+        * Single step the interpreter.
+        * 
+        * @throws DebugException if the request fails
+        */
+       protected void step_over() throws DebugException {
+               fThread.setStepping(true);
+               resumed(DebugEvent.STEP_OVER);
+               fDebugConnection.sendRequest("step_over");
+       }
+       
+       /**
+        * Single step the interpreter.
+        * 
+        * @throws DebugException if the request fails
+        */
+       protected void step_into() throws DebugException {
+               fThread.setStepping(true);
+               resumed(DebugEvent.STEP_INTO);
+               fDebugConnection.sendRequest("step_into");
+       }
+       
+       /**
+        * Single step the interpreter.
+        * 
+        * @throws DebugException if the request fails
+        */
+       protected void step_out() throws DebugException {
+               fThread.setStepping(true);
+               resumed(DebugEvent.STEP_RETURN);
+               fDebugConnection.sendRequest("step_out");
+       }
+
+
+       
+       /**
+        * Returns the current value of the given variable.
+        * 
+        * @param variable
+        * @return variable value
+        * @throws DebugException if the request fails
+        */
+       protected IValue getVariableValue(XDebugVariable variable) throws DebugException {
+//             synchronized (fDebugSocket) {
+//                     fDebugConnection.sendRequest("var","" + variable.getStackFrame().getIdentifier() + " " + variable.getName());
+//                     try {
+//                             String value = fDebugReader.readLine();
+//                             //return new XDebugValue(this, value);
+//                             
+//                     } catch (IOException e) {
+//                             abort(MessageFormat.format("Unable to retrieve value for variable {0}", new String[]{variable.getName()}), e);
+//                     }
+//             }
+               return null;
+       }
+       
+       /**
+        * Returns the values on the data stack (top down)
+        * 
+        * @return the values on the data stack (top down)
+        */
+       public IValue[] getDataStack() throws DebugException {
+//             synchronized (fDebugSocket) {
+//                     fDebugConnection.sendRequest ("data");
+//                     try {
+//                             String valueString = fDebugReader.readLine();
+//                             if (valueString != null && valueString.length() > 0) {
+//                                     String[] values = valueString.split("\\|");
+//                                     IValue[] theValues = new IValue[values.length];
+//                                     for (int i = 0; i < values.length; i++) {
+//                                             String value = values[values.length - i - 1];
+////                                           theValues[i] = new XDebugValue(this, value);
+//                                     }
+//                                     return theValues;
+//                             }
+//                     } catch (IOException e) {
+//                             abort("Unable to retrieve data stack", e);
+//                     }
+//             }
+               return new IValue[0];           
+       }
+       
+       public boolean setVarValue(String name, String value) {
+               int id=-1;
+               String str=Base64.encodeBytes(value.getBytes());
+               int len=str.length();
+
+               try {
+                       id =fDebugConnection.sendRequest("property_set","-n "+name+" -l "+len + " -- "+str);
+               } catch (DebugException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               }
+               DebugResponse dr=getResponse(id);
+               if ((dr.getAttributeValue("success")).equals("1"))
+                       return true;
+               
+               return false;
+       }
+       
+       public DebugResponse getResponse(int id) {
+               return fDebugConnection.waitforTransID(id);
+       }
+
+       /**
+        * Sends a request to the Debugengine and waits for an OK.
+        * 
+        * @param command debug command
+        * @throws DebugException if the request fails
+        */
+       
+       public int sendRequest(String command) throws DebugException {  
+               return fDebugConnection.sendRequest(command,"");
+       }
+
+       
+       /**
+        * Sends a request to the Debugengine and waits for an OK.
+        * 
+        * @param command debug command
+        * @arguments arguments for the command
+        * @throws DebugException if the request fails
+        */
+       
+       public int sendRequest(String command, String arguments) throws DebugException {
+               return fDebugConnection.sendRequest(command,arguments);
+       }
+       
+       /**
+        * Notification a breakpoint was encountered. Determine
+        * which breakpoint was hit and fire a suspend event.
+        * 
+        * @param event debug event
+        */
+       public void breakpointHit(Node node) {
+               // determine which breakpoint was hit, and set the thread's breakpoint
+               Node child=node.getFirstChild();
+               if (child.getNodeName().equals("stack")) {
+                       int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "lineno"));
+                       String filename=PHPDebugUtils.getAttributeValue(child, "filename").substring(8);  // remove file:///
+                       IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
+                       for (int i = 0; i < breakpoints.length; i++) {
+                               IBreakpoint breakpoint = breakpoints[i];
+                               if (supportsBreakpoint(breakpoint)) {
+                                       if (breakpoint instanceof ILineBreakpoint) {
+                                               ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
+                                               try {                                           
+                                                       if (breakpoint.isEnabled()) {
+                                                               IMarker marker = breakpoint.getMarker();
+                                                               if (marker != null) {
+                                                                       
+                                                                       String name = marker.getResource().getLocation().toOSString();
+                                                                       if(name.equals(PHPDebugUtils.unescapeString(filename)) && (lineBreakpoint.getLineNumber() == lineNumber)) {
+                                                                               fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
+                                                                               break;
+                                                                       }
+                                                               }
+                                                       
+                                                       }
+                                               } catch (CoreException e) {
+                                               }
+                                       }
+                               }
+                       }
+               }
+               suspended(DebugEvent.BREAKPOINT);
+       }
+       public void handleDebugEvents(DebugEvent[] events) {
+               for (int i=0;i<events.length;i++) {
+                       DebugEvent event=events[i];
+                       if((event.getKind()==DebugEvent.CREATE) && (event.getSource() instanceof XDebugElement)) {
+                               if(((XDebugElement)event.getSource()).getModelIdentifier()==IXDebugConstants.ID_PHP_DEBUG_MODEL) {
+                                       if (event.getKind()==DebugEvent.CREATE)
+                                               started();
+                               }
+                       }
+               }
+               
+       }       
+}
\ No newline at end of file