/** * */ package net.sourceforge.phpeclipse.xdebug.php.model; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import net.sourceforge.phpeclipse.xdebug.core.Base64; import net.sourceforge.phpeclipse.xdebug.core.IPHPDebugEvent; import net.sourceforge.phpeclipse.xdebug.core.IProxyEventListener; import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils; import net.sourceforge.phpeclipse.xdebug.core.PathMapItem; import net.sourceforge.phpeclipse.xdebug.core.XDebugCorePlugin; import net.sourceforge.phpeclipse.xdebug.core.XDebugProxy; 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.core.runtime.IPath; 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.ILaunchListener; 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.eclipse.debug.core.model.IVariable; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugConnection; import net.sourceforge.phpeclipse.xdebug.core.xdebug.ResponseListener.DebugResponse; /** * @author Christian * */ public class XDebugTarget extends XDebugElement implements IDebugTarget, ILaunchListener, IDebugEventSetListener, IProxyEventListener { private IProcess fProcess; private ILaunch fLaunch; private int fDebugPort; private boolean fSuspended = false; private boolean fTerminated = false; private XDebugThread fThread; private IThread[] fThreads; private XDebugConnection fDebugConnection; private String fIdeKey; /** * Constructs a new debug target in the given launch and waits until * someone with the ideKey connects to the Debugproxy * * * @param launch containing launch * @param process process of the interpreter * @param ideKey * @param pathMap Pathmap for the debug session * @exception CoreException if unable to connect to host */ public XDebugTarget(ILaunch launch, IProcess process, String ideKey) throws CoreException { fLaunch = launch; fProcess = process; fTarget = this; fDebugConnection = null; fThread = null; fThreads = new IThread[0]; fIdeKey = ideKey; XDebugProxy proxy = XDebugCorePlugin.getDefault().getXDebugProxy(); proxy.addProxyEventListener(this,ideKey); if (!proxy.isRunning()) proxy.start(); proxy.setTarget(this); fDebugPort = proxy.getProxyPort(); 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() { if (getProcess()!=null) // ther is no running Process in remote debugging return getProcess().canTerminate(); return true; } /* (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 { XDebugProxy proxy=XDebugCorePlugin.getDefault().getXDebugProxy(); proxy.stop(); proxy.removeProxyEventListener(this,fIdeKey); System.out.println("XDebug.Target: ProxyEventlistener removed"); fTerminated = true; //fDisconnected = true; fSuspended = false; XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this); fireTerminateEvent(); DebugPlugin.getDefault().removeDebugEventListener(this); if (fThread!=null) fThread.removeEventListeners(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#canResume() */ public boolean canResume() { return false; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend() */ public boolean canSuspend() { return false; } /* (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 { if (fDebugConnection != null) { fThread.setBreakpoints(null); fDebugConnection.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) { IMarker marker = breakpoint.getMarker(); IPath path = marker.getResource().getFullPath(); IPath cp = path.removeLastSegments(1); List pathMap = null; try { pathMap = fLaunch.getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_PHP_PATHMAP,(List)null); } catch (CoreException e2) { // TODO Auto-generated catch block e2.printStackTrace(); } if (!fDebugConnection.isClosed()) { if (fProcess == null) { PathMapItem pmi = null; for (int i = 0; i < pathMap.size(); i++) { pmi = new PathMapItem((String) pathMap.get(i)); IPath local = (IPath)pmi.getLocalPath().clone(); local = local.makeAbsolute(); int matchedSegments = local.segmentCount(); if (local.matchingFirstSegments(cp) == matchedSegments) { IPath newPath = pmi.getRemotePath(); newPath = newPath.append(path.removeFirstSegments(matchedSegments)); newPath = newPath.makeAbsolute(); if (supportsBreakpoint(breakpoint)) { try { if (breakpoint.isEnabled()) { if (marker != null) { //fDebugConnection.addBreakpoint(breakpoint, newPath); DebugResponse dr = fDebugConnection.breakpointSet(newPath.toString(), ((ILineBreakpoint)breakpoint).getLineNumber()); String bpid = dr.getAttributeValue("id"); if (!"".equals(bpid)) marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid)); } } } catch (DebugException e) { e.printStackTrace(); } catch (CoreException e) { e.printStackTrace(); } } } } } else { if (supportsBreakpoint(breakpoint)) { try { if (breakpoint.isEnabled()) { if (marker != null) { DebugResponse dr = fDebugConnection.breakpointSet(path.toString(), ((ILineBreakpoint)breakpoint).getLineNumber()); String bpid = dr.getAttributeValue("id"); if (!"".equals(bpid)) marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid)); } } } catch (DebugException e) { e.printStackTrace(); } catch (CoreException e) { e.printStackTrace(); } } } } } /* (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.breakpointRemove(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 (fDebugConnection==null); } /* (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 been started. * Resume the the debugger. */ public void started() throws DebugException { fThread.setBreakpoints(null); fThread.setStepping(false); Integer.parseInt(fDebugConnection.featureGet("detach").getValue()); System.out.println("in Target.started()"); DebugResponse response = fDebugConnection.featureGet("max_children"); String a1 = response.getValue(); System.out.println("max children:"+a1); DebugResponse response1 = fDebugConnection.featureGet("max_children"); String a2 = response1.getValue(); System.out.println("max depth:"+a2); boolean res = fDebugConnection.featureSet("max_depth", "100" ); if( res == true ) { System.out.println("Set depth to 100 (hack)"); } installDeferredBreakpoints(); try { resume(); } catch (DebugException e) { e.printStackTrace(); } } /** * 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 */ public IStackFrame[] getStackFrames() throws DebugException { DebugResponse lastResponse = fDebugConnection.stackGet(0); 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); XDebugStackFrame frame = new XDebugStackFrame(fThread, i); String level =PHPDebugUtils.getAttributeValue(stackNode,"level"); if (!"".equals(level)) frame.setLevel(Integer.parseInt(level)); frame.setType(PHPDebugUtils.getAttributeValue(stackNode,"type")); String fileName=PHPDebugUtils.unescapeString(PHPDebugUtils.getAttributeValue(stackNode,"filename")); String lineNo=PHPDebugUtils.getAttributeValue(stackNode,"lineno"); if (!"".equals(lineNo)) frame.setLineNumber(Integer.parseInt(lineNo)); frame.setWhere(PHPDebugUtils.getAttributeValue(stackNode,"where")); try { frame.setFullName(new URL(fileName)); } catch (MalformedURLException e) { e.printStackTrace(); } frame.incrementStepCounter(); theFrames[i] = frame; } 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.stepOver(); } /** * Single step the interpreter. * * @throws DebugException if the request fails */ protected void step_into() throws DebugException { fThread.setStepping(true); resumed(DebugEvent.STEP_INTO); fDebugConnection.stepInto(); } /** * Single step the interpreter. * * @throws DebugException if the request fails */ protected void step_out() throws DebugException { fThread.setStepping(true); resumed(DebugEvent.STEP_RETURN); fDebugConnection.stepOut(); } /** * 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 { 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 { return new IValue[0]; }*/ public boolean setVarValue(String name, String value) { return fDebugConnection.setVarValue(name,value); } public void handleDebugEvents(DebugEvent[] events) { for (int i = 0; i < events.length; i++) { DebugEvent event = events[i]; if (event.getKind() == DebugEvent.MODEL_SPECIFIC) { switch (event.getDetail()) { case IPHPDebugEvent.BREAKPOINT_HIT: DebugResponse lastResponse = ((XDebugConnection) fDebugConnection).stackGet(0); IBreakpoint breakpoint = breakpointHit(lastResponse.getParentNode()); fThread.setBreakpoints(new IBreakpoint[]{breakpoint}); fThread.incrementStepCounter(); suspended(DebugEvent.BREAKPOINT); break; case IPHPDebugEvent.STEP_END: fThread.incrementStepCounter(); suspended(DebugEvent.STEP_END); break; case IPHPDebugEvent.STOPPED: fThread.removeEventListeners(); fThread = null; fThreads = new IThread[0]; fDebugConnection.close(); fSuspended = false; // Dirty hack to check debugging mode (remote or local) if (fProcess!=null) { try { terminate(); } catch (DebugException e) { e.printStackTrace(); } } else { fDebugConnection = null; fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT)); } break; } } } } public void handleProxyEvent(String ideKey, String initString, /*AbstractDebugConnection*/ XDebugConnection connection) { System.out.println("* New Connection - XDebug.Target: "+ideKey); setDebugConnection(connection); XDebugProxy proxy=XDebugCorePlugin.getDefault().getXDebugProxy(); fDebugPort=proxy.getProxyPort(); fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CHANGE)); fThread = new XDebugThread(this); fThreads = new IThread[] {fThread}; try { started(); } catch( DebugException e ){ e.printStackTrace(); } } private void setDebugConnection(XDebugConnection connection) { if (connection != null) { fDebugConnection = connection; fDebugConnection.startListener(); } } /** * @return Returns the fDebugConnection. */ public XDebugConnection getDebugConnection() { return fDebugConnection; } public void addProcess(IProcess p) { fProcess = p; } public void launchRemoved(ILaunch launch) { } /** * Notifies this listener that the specified launch * has been added. * * @param launch the newly added launch * @since 2.0 */ public void launchAdded(ILaunch launch){ } /** * Notifies this listener that the specified launch * has changed. For example, a process or debug target * has been added to the launch. * * @param launch the changed launch * @since 2.0 */ public void launchChanged(ILaunch launch) { } public IVariable[] getVariables(XDebugStackFrame frame, int level) throws DebugException { IVariable[] variables = null; DebugResponse response = fDebugConnection.contextGet(level, 0); Node responseNode = response.getParentNode(); NodeList property = responseNode.getChildNodes(); DebugResponse responseGlobal = fDebugConnection.contextGet(level, 1); Node responseGlobalNode = responseGlobal.getParentNode(); NodeList propertyGlobal = responseGlobalNode.getChildNodes(); variables = new IVariable[property.getLength() + propertyGlobal.getLength()]; // variables = new IVariable[property.getLength()]; // + propertyGlobal.getLength()]; int length = property.getLength(); for (int i = 0; i < length; i++) { Node propertyNode = property.item(i); XDebugVariable var=/*fDebugConnection.*/getVariableFromNode(frame,propertyNode); variables[i]=var; } int globalLength = propertyGlobal.getLength(); for (int k = 0; k < globalLength; k++) { Node propertyGlobalNode = propertyGlobal.item(k); XDebugVariable var=/*fDebugConnection.*/getVariableFromNode(frame,propertyGlobalNode); variables[k + length]=var; } return variables; } public XDebugVariable getVariableFromNode(XDebugStackFrame frame, Node property) { String varFullName = PHPDebugUtils.getAttributeValue(property, "fullname"); String varName = PHPDebugUtils.getAttributeValue(property, "name"); String varEncoding = PHPDebugUtils.getAttributeValue(property, "encoding"); int varNumChildren = 0; if (PHPDebugUtils.getAttributeValue(property, "numchildren").equals("")) { varNumChildren = 0; } else { varNumChildren = Integer.parseInt(PHPDebugUtils.getAttributeValue(property, "numchildren")); } String typeName = PHPDebugUtils.getAttributeValue(property,"type"); XDebugVariable variable = new XDebugVariable(frame,varFullName,varName,typeName); variable.setEncoding(varEncoding); variable.setNumChildren(varNumChildren); XDebugAbstractValue val=null; try { val = (XDebugAbstractValue) variable.getValue(); } catch (DebugException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } if (val.getType()!= XDebugAbstractValue.VALUETYPE_UNINITIALIZED) { if (variable.hasChildren()) { NodeList varNodes = property.getChildNodes(); val.renderValueString(""+varNodes.getLength()); IVariable[] variables = new IVariable[varNodes.getLength()]; for (int i = 0; i