/** * */ 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(); } } } } }