2 * Created on 23.11.2004
4 * TODO To change the template for this generated file go to
5 * Window - Preferences - Java - Code Style - Code Templates
7 package net.sourceforge.phpeclipse.xdebug.core;
9 import java.io.BufferedReader;
10 import java.io.IOException;
11 import java.io.InputStreamReader;
12 import java.net.Socket;
13 import java.net.ServerSocket;
14 import java.net.UnknownHostException;
15 import java.text.MessageFormat;
17 import javax.xml.parsers.*;
18 import javax.xml.parsers.DocumentBuilder;
19 import org.xml.sax.SAXException;
20 import java.io.StringBufferInputStream;
21 import java.io.OutputStreamWriter;
23 import org.eclipse.core.resources.IMarkerDelta;
24 import org.eclipse.core.runtime.CoreException;
25 import org.eclipse.core.runtime.IProgressMonitor;
26 import org.eclipse.core.runtime.IStatus;
27 import org.eclipse.core.runtime.Status;
28 import org.eclipse.core.runtime.jobs.Job;
29 import org.eclipse.debug.core.DebugEvent;
30 import org.eclipse.debug.core.DebugException;
31 import org.eclipse.debug.core.DebugPlugin;
32 import org.eclipse.debug.core.ILaunch;
33 import org.eclipse.debug.core.model.IBreakpoint;
34 import org.eclipse.debug.core.model.IDebugTarget;
35 import org.eclipse.debug.core.model.ILineBreakpoint;
36 import org.eclipse.debug.core.model.IMemoryBlock;
37 import org.eclipse.debug.core.model.IProcess;
38 import org.eclipse.debug.core.model.IStackFrame;
39 import org.eclipse.debug.core.model.IThread;
40 import org.eclipse.debug.core.model.IValue;
41 import net.sourceforge.phpdt.internal.debug.core.PHPDebugCorePlugin;
46 * TODO To change the template for this generated type comment go to
47 * Window - Preferences - Java - Code Style - Code Templates
49 public class XDebugTarget extends XDebugElement implements IDebugTarget {
51 // associated system process (VM)
52 private IProcess fProcess;
54 // containing launch object
55 private ILaunch fLaunch;
60 // sockets to communicate with XDebug
61 private ServerSocket fDebugServerSocket;
62 private Socket fDebugSocket;
63 private OutputStreamWriter fDebugWriter;
64 private BufferedReader fDebugReader;
67 private boolean fSuspended = true;
70 private boolean fTerminated = false;
73 private XDebugThread fThread;
74 private IThread[] fThreads;
77 private EventDispatchJob fEventDispatch;
79 // Settings for Debug Process
80 private String transaction_id = "";
81 private String fileuri = "";
84 * Listens to events from the XDebug and fires corresponding
87 class EventDispatchJob extends Job {
89 public EventDispatchJob() {
90 super("XDebug Event Dispatch");
94 public String getAttributeValue (Node CurrentNode, String AttributeName) {
96 if (CurrentNode.hasAttributes()) {
97 NamedNodeMap listAttribute = CurrentNode.getAttributes();
98 Node nodeTransactionID = listAttribute.getNamedItem(AttributeName);
99 strValue = nodeTransactionID.getNodeValue();
105 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
107 protected IStatus run(IProgressMonitor monitor) {
110 boolean ZeroByteFound;
113 while (!isTerminated() && event != null) {
115 ZeroByteFound = false;
117 while (!ZeroByteFound){
118 CurrentByte = fDebugReader.read();
119 if (CurrentByte == 0) {
120 ZeroByteFound = true;
122 BytesToRead = Integer.parseInt(event);
123 } catch (NumberFormatException e) {
127 event = event + (char)CurrentByte;
130 if (BytesToRead > 0) {
132 for (int i = 0; i < BytesToRead; i++) {
133 CurrentByte = fDebugReader.read();
134 InputXML = InputXML + (char)CurrentByte;
136 CurrentByte = fDebugReader.read(); // Das Null Byte nach dem String lesen.
138 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
139 DocumentBuilder builder = factory.newDocumentBuilder();
140 StringBufferInputStream InputXMLStream = new StringBufferInputStream(InputXML);
141 Document doc = builder.parse(InputXMLStream);
142 Node myNode = doc.getFirstChild();
143 if (myNode.getNodeName() == "init") {
144 transaction_id = getAttributeValue(myNode, "appid");
145 fileuri = getAttributeValue(myNode, "fileuri");
146 fThread.setBreakpoints(null);
147 fThread.setStepping(false);
149 } else if (myNode.getNodeName() == "response") {
153 } catch (ParserConfigurationException e) {
155 } catch (SAXException e) {
160 event = fDebugReader.readLine();
162 fThread.setBreakpoints(null);
163 fThread.setStepping(false);
164 if (event.equals("started")) {
166 } else if (event.equals("terminated")) {
168 } else if (event.startsWith("resumed")) {
169 if (event.endsWith("step")) {
170 fThread.setStepping(true);
171 resumed(DebugEvent.STEP_OVER);
172 } else if (event.endsWith("client")) {
173 resumed(DebugEvent.CLIENT_REQUEST);
175 } else if (event.startsWith("suspended")) {
176 if (event.endsWith("client")) {
177 suspended(DebugEvent.CLIENT_REQUEST);
178 } else if (event.endsWith("step")) {
179 suspended(DebugEvent.STEP_END);
180 } else if (event.indexOf("breakpoint") >= 0) {
181 breakpointHit(event);
186 } catch (IOException e) {
190 return Status.OK_STATUS;
196 * Constructs a new debug target in the given launch for the
197 * associated PDA VM process.
199 * @param launch containing launch
200 * @param debugPort port to read events from
201 * @exception CoreException if unable to connect to host
203 public XDebugTarget(ILaunch launch, int debugPort) throws CoreException {
208 fDebugServerSocket = new ServerSocket(debugPort);
209 fDebugSocket = fDebugServerSocket.accept();
210 fDebugWriter = new OutputStreamWriter(fDebugSocket.getOutputStream(), "UTF8");
211 fDebugReader = new BufferedReader(new InputStreamReader(fDebugSocket.getInputStream()));
213 } catch (UnknownHostException e) {
214 abort("Unable to connect to PDA VM", e);
215 } catch (IOException e) {
216 abort("Unable to connect to PDA VM", e);
218 fThread = new XDebugThread(this);
219 fThreads = new IThread[] {fThread};
220 fEventDispatch = new EventDispatchJob();
221 fEventDispatch.schedule();
222 DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
225 * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
227 public IProcess getProcess() {
231 * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
233 public IThread[] getThreads() throws DebugException {
237 * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
239 public boolean hasThreads() throws DebugException {
243 * @see org.eclipse.debug.core.model.IDebugTarget#getName()
245 public String getName() throws DebugException {
247 fName = "XDebug Core";
249 fName = getLaunch().getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_XDEBUG_PROGRAM, "PDA VM");
250 } catch (CoreException e) {
256 * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
258 public boolean supportsBreakpoint(IBreakpoint breakpoint) {
259 if (breakpoint.getModelIdentifier().equals(PHPDebugCorePlugin.PLUGIN_ID)) {
260 /* Axel: Weiß nicht ob das wichtig ist.
262 String program = getLaunch().getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_XDEBUG_PROGRAM, (String)null);
263 if (program != null) {
264 IMarker marker = breakpoint.getMarker();
265 if (marker != null) {
266 IPath p = new Path(program);
267 return marker.getResource().getFullPath().equals(p);
270 } catch (CoreException e) {
278 * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
280 public IDebugTarget getDebugTarget() {
284 * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
286 public ILaunch getLaunch() {
290 * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
292 public boolean canTerminate() {
293 // return getProcess().canTerminate();
297 * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
299 public boolean isTerminated() {
300 // return getProcess().isTerminated();
304 * @see org.eclipse.debug.core.model.ITerminate#terminate()
306 public void terminate() throws DebugException {
307 sendRequest ("stop -i " + transaction_id);
310 * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
312 public boolean canResume() {
313 return !isTerminated() && isSuspended();
316 * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
318 public boolean canSuspend() {
319 return !isTerminated() && !isSuspended();
322 * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
324 public boolean isSuspended() {
328 * @see org.eclipse.debug.core.model.ISuspendResume#resume()
330 public void resume() throws DebugException {
331 sendRequest("run -i " + transaction_id);
335 * Notification the target has resumed for the given reason
337 * @param detail reason for the resume
339 private void resumed(int detail) {
341 fThread.fireResumeEvent(detail);
345 * Notification the target has suspended for the given reason
347 * @param detail reason for the suspend
349 private void suspended(int detail) {
351 fThread.fireSuspendEvent(detail);
355 * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
357 public void suspend() throws DebugException {
360 * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
362 public void breakpointAdded(IBreakpoint breakpoint) {
363 if (supportsBreakpoint(breakpoint)) {
365 if (breakpoint.isEnabled()) {
367 sendRequest("breakpoint_set -i " + transaction_id +" -t line -n " + (((ILineBreakpoint)breakpoint).getLineNumber() - 1));
368 } catch (CoreException e) {
371 } catch (CoreException e) {
376 * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
378 public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
379 if (supportsBreakpoint(breakpoint)) {
381 sendRequest("clear " + ((ILineBreakpoint)breakpoint).getLineNumber());
382 } catch (CoreException e) {
387 * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
389 public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
390 if (supportsBreakpoint(breakpoint)) {
392 if (breakpoint.isEnabled()) {
393 breakpointAdded(breakpoint);
395 breakpointRemoved(breakpoint, null);
397 } catch (CoreException e) {
402 * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
404 public boolean canDisconnect() {
408 * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
410 public void disconnect() throws DebugException {
413 * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
415 public boolean isDisconnected() {
419 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
421 public boolean supportsStorageRetrieval() {
425 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
427 public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
432 * Notification we have connected to the VM and it has started.
435 private void started() {
437 installDeferredBreakpoints();
440 } catch (DebugException e) {
445 * Install breakpoints that are already registered with the breakpoint
448 private void installDeferredBreakpoints() {
449 IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(PHPDebugCorePlugin.PLUGIN_ID);
450 for (int i = 0; i < breakpoints.length; i++) {
451 breakpointAdded(breakpoints[i]);
456 * Called when this debug target terminates.
458 private void terminated() {
461 DebugPlugin.getDefault().getBreakpointManager().removeBreakpointListener(this);
462 fireTerminateEvent();
466 * Returns the current stack frames in the target.
468 * @return the current stack frames in the target
469 * @throws DebugException if unable to perform the request
471 protected IStackFrame[] getStackFrames() throws DebugException {
472 synchronized (fDebugSocket) {
473 sendRequest("stack -i " + transaction_id);
475 String framesData = fDebugReader.readLine();
476 if (framesData != null) {
477 String[] frames = framesData.split("#");
478 IStackFrame[] theFrames = new IStackFrame[frames.length];
479 for (int i = 0; i < frames.length; i++) {
480 String data = frames[i];
481 theFrames[frames.length - i - 1] = new XDebugStackFrame(fThread, data, i);
485 } catch (IOException e) {
486 abort("Unable to retrieve stack frames", e);
489 return new IStackFrame[0];
493 * Single step the interpreter.
495 * @throws DebugException if the request fails
497 protected void step() throws DebugException {
498 sendRequest("step_into -i " + transaction_id);
502 * Returns the current value of the given variable.
505 * @return variable value
506 * @throws DebugException if the request fails
508 protected IValue getVariableValue(XDebugVariable variable) throws DebugException {
509 synchronized (fDebugSocket) {
510 sendRequest("var " + variable.getStackFrame().getIdentifier() + " " + variable.getName());
512 String value = fDebugReader.readLine();
513 return new XDebugValue(this, value);
514 } catch (IOException e) {
515 abort(MessageFormat.format("Unable to retrieve value for variable {0}", new String[]{variable.getName()}), e);
522 * Returns the values on the data stack (top down)
524 * @return the values on the data stack (top down)
526 public IValue[] getDataStack() throws DebugException {
527 synchronized (fDebugSocket) {
528 sendRequest ("data");
530 String valueString = fDebugReader.readLine();
531 if (valueString != null && valueString.length() > 0) {
532 String[] values = valueString.split("\\|");
533 IValue[] theValues = new IValue[values.length];
534 for (int i = 0; i < values.length; i++) {
535 String value = values[values.length - i - 1];
536 theValues[i] = new XDebugValue(this, value);
540 } catch (IOException e) {
541 abort("Unable to retrieve data stack", e);
544 return new IValue[0];
548 * Sends a request to the PDA VM and waits for an OK.
550 * @param request debug command
551 * @throws DebugException if the request fails
553 private void sendRequest(String request) throws DebugException {
554 synchronized (fDebugSocket) {
556 fDebugWriter.write(request);
557 fDebugWriter.write(0);
558 fDebugWriter.flush();
559 } catch (IOException e) {
566 * Notification a breakpoint was encountered. Determine
567 * which breakpoint was hit and fire a suspend event.
569 * @param event debug event
571 private void breakpointHit(String event) {
572 // determine which breakpoint was hit, and set the thread's breakpoint
573 int lastSpace = event.lastIndexOf(' ');
575 String line = event.substring(lastSpace + 1);
576 int lineNumber = Integer.parseInt(line);
577 IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(PHPDebugCorePlugin.PLUGIN_ID);
578 for (int i = 0; i < breakpoints.length; i++) {
579 IBreakpoint breakpoint = breakpoints[i];
580 if (supportsBreakpoint(breakpoint)) {
581 if (breakpoint instanceof ILineBreakpoint) {
582 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
584 if (lineBreakpoint.getLineNumber() == lineNumber) {
585 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
588 } catch (CoreException e) {
594 suspended(DebugEvent.BREAKPOINT);