/* * Created on 23.11.2004 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ package net.sourceforge.phpeclipse.xdebug.core; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.ServerSocket; import java.net.UnknownHostException; import java.text.MessageFormat; import org.w3c.dom.*; import javax.xml.parsers.*; import javax.xml.parsers.DocumentBuilder; import org.xml.sax.SAXException; import java.io.StringBufferInputStream; import java.io.OutputStreamWriter; import org.eclipse.core.resources.IMarkerDelta; 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.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; 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 net.sourceforge.phpdt.internal.debug.core.PHPDebugCorePlugin; /** * @author Axel * * TODO To change the template for this generated type comment go to * Window - Preferences - Java - Code Style - Code Templates */ public class XDebugTarget extends XDebugElement implements IDebugTarget { // associated system process (VM) private IProcess fProcess; // containing launch object private ILaunch fLaunch; // program name private String fName; // sockets to communicate with XDebug private ServerSocket fDebugServerSocket; private Socket fDebugSocket; private OutputStreamWriter fDebugWriter; private BufferedReader fDebugReader; // 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; // Settings for Debug Process private String transaction_id = ""; private String fileuri = ""; /** * Listens to events from the XDebug and fires corresponding * debug events. */ class EventDispatchJob extends Job { public EventDispatchJob() { super("XDebug Event Dispatch"); setSystem(true); } public String getAttributeValue (Node CurrentNode, String AttributeName) { String strValue = ""; if (CurrentNode.hasAttributes()) { NamedNodeMap listAttribute = CurrentNode.getAttributes(); Node nodeTransactionID = listAttribute.getNamedItem(AttributeName); strValue = nodeTransactionID.getNodeValue(); } return strValue; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ protected IStatus run(IProgressMonitor monitor) { String event = ""; int CurrentByte; boolean ZeroByteFound; int BytesToRead = 0; String InputXML; while (!isTerminated() && event != null) { try { ZeroByteFound = false; event = ""; while (!ZeroByteFound){ CurrentByte = fDebugReader.read(); if (CurrentByte == 0) { ZeroByteFound = true; try { BytesToRead = Integer.parseInt(event); } catch (NumberFormatException e) { BytesToRead = 0; } } else { event = event + (char)CurrentByte; } } if (BytesToRead > 0) { InputXML = ""; for (int i = 0; i < BytesToRead; i++) { CurrentByte = fDebugReader.read(); InputXML = InputXML + (char)CurrentByte; } CurrentByte = fDebugReader.read(); // Das Null Byte nach dem String lesen. try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); StringBufferInputStream InputXMLStream = new StringBufferInputStream(InputXML); Document doc = builder.parse(InputXMLStream); Node myNode = doc.getFirstChild(); if (myNode.getNodeName() == "init") { transaction_id = getAttributeValue(myNode, "appid"); fileuri = getAttributeValue(myNode, "fileuri"); fThread.setBreakpoints(null); fThread.setStepping(false); started(); } else if (myNode.getNodeName() == "response") { } } catch (ParserConfigurationException e) { } catch (SAXException e) { } } /* event = fDebugReader.readLine(); if (event != null) { fThread.setBreakpoints(null); fThread.setStepping(false); if (event.equals("started")) { } else if (event.equals("terminated")) { terminated(); } else if (event.startsWith("resumed")) { if (event.endsWith("step")) { fThread.setStepping(true); resumed(DebugEvent.STEP_OVER); } else if (event.endsWith("client")) { resumed(DebugEvent.CLIENT_REQUEST); } } else if (event.startsWith("suspended")) { if (event.endsWith("client")) { suspended(DebugEvent.CLIENT_REQUEST); } else if (event.endsWith("step")) { suspended(DebugEvent.STEP_END); } else if (event.indexOf("breakpoint") >= 0) { breakpointHit(event); } } } */ } catch (IOException e) { terminated(); } } return Status.OK_STATUS; } } /** * 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, int debugPort) throws CoreException { super(null); fLaunch = launch; fTarget = this; try { fDebugServerSocket = new ServerSocket(debugPort); fDebugSocket = fDebugServerSocket.accept(); fDebugWriter = new OutputStreamWriter(fDebugSocket.getOutputStream(), "UTF8"); fDebugReader = new BufferedReader(new InputStreamReader(fDebugSocket.getInputStream())); } catch (UnknownHostException e) { abort("Unable to connect to PDA VM", e); } catch (IOException e) { abort("Unable to connect to PDA VM", e); } fThread = new XDebugThread(this); fThreads = new IThread[] {fThread}; fEventDispatch = new EventDispatchJob(); fEventDispatch.schedule(); DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(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 false; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IDebugTarget#getName() */ public String getName() throws DebugException { if (fName == null) { fName = "XDebug Core"; try { fName = getLaunch().getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_XDEBUG_PROGRAM, "PDA VM"); } catch (CoreException e) { } } return fName; } /* (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(PHPDebugCorePlugin.PLUGIN_ID)) { /* Axel: Weiß nicht ob das wichtig ist. try { String program = getLaunch().getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_XDEBUG_PROGRAM, (String)null); if (program != null) { IMarker marker = breakpoint.getMarker(); if (marker != null) { IPath p = new Path(program); return marker.getResource().getFullPath().equals(p); } } } catch (CoreException e) { } */ 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 false; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ITerminate#terminate() */ public void terminate() throws DebugException { sendRequest ("stop -i " + transaction_id); } /* (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 { sendRequest("run -i " + transaction_id); } /** * 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 */ private 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()) { try { sendRequest("breakpoint_set -i " + transaction_id +" -t line -n " + (((ILineBreakpoint)breakpoint).getLineNumber() - 1)); } 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 { sendRequest("clear " + ((ILineBreakpoint)breakpoint).getLineNumber()); } 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 VM and it has started. * Resume the VM. */ private void started() { fireCreationEvent(); installDeferredBreakpoints(); try { resume(); } catch (DebugException e) { } } /** * Install breakpoints that are already registered with the breakpoint * manager. */ private void installDeferredBreakpoints() { IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(PHPDebugCorePlugin.PLUGIN_ID); for (int i = 0; i < breakpoints.length; i++) { breakpointAdded(breakpoints[i]); } } /** * Called when this debug target terminates. */ private void terminated() { fTerminated = true; fSuspended = false; DebugPlugin.getDefault().getBreakpointManager().removeBreakpointListener(this); fireTerminateEvent(); } /** * 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 { synchronized (fDebugSocket) { sendRequest("stack -i " + transaction_id); try { String framesData = fDebugReader.readLine(); if (framesData != null) { String[] frames = framesData.split("#"); IStackFrame[] theFrames = new IStackFrame[frames.length]; for (int i = 0; i < frames.length; i++) { String data = frames[i]; theFrames[frames.length - i - 1] = new XDebugStackFrame(fThread, data, i); } return theFrames; } } catch (IOException e) { abort("Unable to retrieve stack frames", e); } } return new IStackFrame[0]; } /** * Single step the interpreter. * * @throws DebugException if the request fails */ protected void step() throws DebugException { sendRequest("step_into -i " + transaction_id); } /** * 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) { 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) { 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]; } /** * Sends a request to the PDA VM and waits for an OK. * * @param request debug command * @throws DebugException if the request fails */ private void sendRequest(String request) throws DebugException { synchronized (fDebugSocket) { try { fDebugWriter.write(request); fDebugWriter.write(0); fDebugWriter.flush(); } catch (IOException e) { e.printStackTrace(); } } } /** * Notification a breakpoint was encountered. Determine * which breakpoint was hit and fire a suspend event. * * @param event debug event */ private void breakpointHit(String event) { // determine which breakpoint was hit, and set the thread's breakpoint int lastSpace = event.lastIndexOf(' '); if (lastSpace > 0) { String line = event.substring(lastSpace + 1); int lineNumber = Integer.parseInt(line); IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(PHPDebugCorePlugin.PLUGIN_ID); 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 (lineBreakpoint.getLineNumber() == lineNumber) { fThread.setBreakpoints(new IBreakpoint[]{breakpoint}); break; } } catch (CoreException e) { } } } } } suspended(DebugEvent.BREAKPOINT); } }