4 package net.sourceforge.phpeclipse.xdebug.php.model;
6 import net.sourceforge.phpeclipse.xdebug.core.Base64;
7 import net.sourceforge.phpeclipse.xdebug.core.DebugConnection;
8 import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils;
9 import net.sourceforge.phpeclipse.xdebug.core.XDebugCorePlugin;
10 import net.sourceforge.phpeclipse.xdebug.core.DebugConnection.DebugResponse;
11 import net.sourceforge.phpeclipse.xdebug.php.launching.IXDebugConstants;
13 import org.eclipse.core.resources.IMarker;
14 import org.eclipse.core.resources.IMarkerDelta;
15 import org.eclipse.core.runtime.CoreException;
16 import org.eclipse.debug.core.DebugEvent;
17 import org.eclipse.debug.core.DebugException;
18 import org.eclipse.debug.core.DebugPlugin;
19 import org.eclipse.debug.core.IDebugEventSetListener;
20 import org.eclipse.debug.core.ILaunch;
21 import org.eclipse.debug.core.model.IBreakpoint;
22 import org.eclipse.debug.core.model.IDebugTarget;
23 import org.eclipse.debug.core.model.ILineBreakpoint;
24 import org.eclipse.debug.core.model.IMemoryBlock;
25 import org.eclipse.debug.core.model.IProcess;
26 import org.eclipse.debug.core.model.IStackFrame;
27 import org.eclipse.debug.core.model.IThread;
28 import org.eclipse.debug.core.model.IValue;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NodeList;
36 public class XDebugTarget extends XDebugElement implements IDebugTarget,
37 IDebugEventSetListener {
38 // associated system process (VM)
39 private IProcess fProcess;
41 // containing launch object
42 private ILaunch fLaunch;
45 private int fDebugPort;
48 // private String fName;
51 private boolean fSuspended = true;
54 private boolean fTerminated = false;
57 private XDebugThread fThread;
59 private IThread[] fThreads;
62 // private EventDispatchJob fEventDispatch;
64 private DebugConnection fDebugConnection;
66 // private DebugResponse lastResponse;
69 * Constructs a new debug target in the given launch for the associated PDA
75 * port to read events from
76 * @exception CoreException
77 * if unable to connect to host
79 public XDebugTarget(ILaunch launch, IProcess process, int debugPort)
80 throws CoreException {
85 fDebugConnection = new DebugConnection(this, debugPort);
86 fThread = new XDebugThread(this);
87 fThreads = new IThread[] { fThread };
88 DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(
90 DebugPlugin.getDefault().addDebugEventListener(this);
96 * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
98 public IProcess getProcess() {
105 * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
107 public IThread[] getThreads() throws DebugException {
114 * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
116 public boolean hasThreads() throws DebugException {
117 return (fThreads.length > 0);
123 * @see org.eclipse.debug.core.model.IDebugTarget#getName()
125 public String getName() throws DebugException {
126 return "PHP XDebug Client at localhost:" + fDebugPort;
132 * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
134 public boolean supportsBreakpoint(IBreakpoint breakpoint) {
135 if (breakpoint.getModelIdentifier().equals(
136 IXDebugConstants.ID_PHP_DEBUG_MODEL)) {
145 * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
147 public IDebugTarget getDebugTarget() {
154 * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
156 public ILaunch getLaunch() {
163 * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
165 public boolean canTerminate() {
166 return getProcess().canTerminate();
173 * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
175 public boolean isTerminated() {
176 // return getProcess().isTerminated();
183 * @see org.eclipse.debug.core.model.ITerminate#terminate()
185 public void terminate() throws DebugException {
186 fDebugConnection.sendRequest("stop");
192 * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
194 public boolean canResume() {
195 return !isTerminated() && isSuspended();
201 * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
203 public boolean canSuspend() {
204 return !isTerminated() && !isSuspended();
210 * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
212 public boolean isSuspended() {
219 * @see org.eclipse.debug.core.model.ISuspendResume#resume()
221 public void resume() throws DebugException {
222 fDebugConnection.sendRequest("run");
226 * Notification the target has resumed for the given reason
229 * reason for the resume
231 private void resumed(int detail) {
233 fThread.fireResumeEvent(detail);
237 * Notification the target has suspended for the given reason
240 * reason for the suspend
242 public void suspended(int detail) {
244 fThread.fireSuspendEvent(detail);
250 * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
252 public void suspend() throws DebugException {
258 * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
260 public void breakpointAdded(IBreakpoint breakpoint) {
262 if (supportsBreakpoint(breakpoint)) {
264 if (breakpoint.isEnabled()) {
265 IMarker marker = breakpoint.getMarker();
266 if (marker != null) {
268 String fileName = PHPDebugUtils.escapeString(marker
269 .getResource().getLocation().toString());
270 String arg = "-t line -f file:///"
273 + ((ILineBreakpoint) breakpoint)
275 int id = fDebugConnection.sendRequest(
276 "breakpoint_set", arg);
277 // set the marker Attribute to make later
278 // idetification possible
279 // TODO: make sure that attribute is set before
280 // response from debugger is beeing prosessed
282 XDebugLineBreakpoint.BREAKPOINT_ID, id);
284 } catch (CoreException e) {
288 } catch (CoreException e) {
297 * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint,
298 * org.eclipse.core.resources.IMarkerDelta)
300 public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
301 if (supportsBreakpoint(breakpoint)) {
303 int id = ((XDebugLineBreakpoint) breakpoint).getID();
305 fDebugConnection.sendRequest("breakpoint_remove", "-d "
307 } catch (CoreException e) {
315 * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint,
316 * org.eclipse.core.resources.IMarkerDelta)
318 public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
319 // if (supportsBreakpoint(breakpoint)) {
321 // if (breakpoint.isEnabled()) {
322 // breakpointAdded(breakpoint);
324 // breakpointRemoved(breakpoint, null);
326 // } catch (CoreException e) {
334 * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
336 public boolean canDisconnect() {
343 * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
345 public void disconnect() throws DebugException {
351 * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
353 public boolean isDisconnected() {
360 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
362 public boolean supportsStorageRetrieval() {
369 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long,
372 public IMemoryBlock getMemoryBlock(long startAddress, long length)
373 throws DebugException {
378 * Notification we have connected to the PHP debugger and it has started.
379 * Resume the the debugger.
381 public void started() {
383 fThread.setBreakpoints(null);
384 fThread.setStepping(false);
386 installDeferredBreakpoints();
390 } catch (DebugException e) {
395 * Install breakpoints that are already registered with the breakpoint
398 private void installDeferredBreakpoints() {
399 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
400 for (int i = 0; i < breakpoints.length; i++) {
401 breakpointAdded(breakpoints[i]);
406 * Called when this debug target terminates.
408 public void terminated() {
411 XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this);
412 fireTerminateEvent();
413 DebugPlugin.getDefault().removeDebugEventListener(this);
414 fThread.removeEventListeners();
418 * Returns the current stack frames in the target.
420 * @return the current stack frames in the target
421 * @throws DebugException
422 * if unable to perform the request
424 protected IStackFrame[] getStackFrames() throws DebugException {
425 int id = fDebugConnection.sendRequest("stack_get");
426 DebugResponse lastResponse = fDebugConnection.waitforTransID(id);
427 if (lastResponse.isError())
428 return new IStackFrame[0];
429 Node response = lastResponse.getParentNode();
430 NodeList frames = response.getChildNodes();
431 IStackFrame[] theFrames = new IStackFrame[frames.getLength()];
432 for (int i = 0; i < frames.getLength(); i++) {
433 Node stackNode = frames.item(i);
434 theFrames[i] = new XDebugStackFrame(fThread, stackNode, i);
440 * Single step the interpreter.
442 * @throws DebugException
443 * if the request fails
445 protected void step_over() throws DebugException {
446 fThread.setStepping(true);
447 resumed(DebugEvent.STEP_OVER);
448 fDebugConnection.sendRequest("step_over");
452 * Single step the interpreter.
454 * @throws DebugException
455 * if the request fails
457 protected void step_into() throws DebugException {
458 fThread.setStepping(true);
459 resumed(DebugEvent.STEP_INTO);
460 fDebugConnection.sendRequest("step_into");
464 * Single step the interpreter.
466 * @throws DebugException
467 * if the request fails
469 protected void step_out() throws DebugException {
470 fThread.setStepping(true);
471 resumed(DebugEvent.STEP_RETURN);
472 fDebugConnection.sendRequest("step_out");
476 * Returns the current value of the given variable.
479 * @return variable value
480 * @throws DebugException
481 * if the request fails
483 protected IValue getVariableValue(XDebugVariable variable)
484 throws DebugException {
485 // synchronized (fDebugSocket) {
486 // fDebugConnection.sendRequest("var","" +
487 // variable.getStackFrame().getIdentifier() + " " + variable.getName());
489 // String value = fDebugReader.readLine();
490 // //return new XDebugValue(this, value);
492 // } catch (IOException e) {
493 // abort(MessageFormat.format("Unable to retrieve value for variable
494 // {0}", new String[]{variable.getName()}), e);
501 * Returns the values on the data stack (top down)
503 * @return the values on the data stack (top down)
505 public IValue[] getDataStack() throws DebugException {
506 // synchronized (fDebugSocket) {
507 // fDebugConnection.sendRequest ("data");
509 // String valueString = fDebugReader.readLine();
510 // if (valueString != null && valueString.length() > 0) {
511 // String[] values = valueString.split("\\|");
512 // IValue[] theValues = new IValue[values.length];
513 // for (int i = 0; i < values.length; i++) {
514 // String value = values[values.length - i - 1];
515 // // theValues[i] = new XDebugValue(this, value);
519 // } catch (IOException e) {
520 // abort("Unable to retrieve data stack", e);
523 return new IValue[0];
526 public boolean setVarValue(String name, String value) {
528 String str = Base64.encodeBytes(value.getBytes());
529 int len = str.length();
532 id = fDebugConnection.sendRequest("property_set", "-n " + name
533 + " -l " + len + " -- " + str);
534 } catch (DebugException e) {
535 // TODO Auto-generated catch block
538 DebugResponse dr = getResponse(id);
539 if ((dr.getAttributeValue("success")).equals("1"))
545 public DebugResponse getResponse(int id) {
546 return fDebugConnection.waitforTransID(id);
550 * Sends a request to the Debugengine and waits for an OK.
554 * @throws DebugException
555 * if the request fails
558 public int sendRequest(String command) throws DebugException {
559 return fDebugConnection.sendRequest(command, "");
563 * Sends a request to the Debugengine and waits for an OK.
567 * @arguments arguments for the command
568 * @throws DebugException
569 * if the request fails
572 public int sendRequest(String command, String arguments)
573 throws DebugException {
574 return fDebugConnection.sendRequest(command, arguments);
578 * Notification a breakpoint was encountered. Determine which breakpoint was
579 * hit and fire a suspend event.
584 public void breakpointHit(Node node) {
585 // determine which breakpoint was hit, and set the thread's breakpoint
586 Node child = node.getFirstChild();
587 if (child.getNodeName().equals("stack")) {
588 int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(
590 String filename = PHPDebugUtils
591 .getAttributeValue(child, "filename").substring(8); // remove
593 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
594 for (int i = 0; i < breakpoints.length; i++) {
595 IBreakpoint breakpoint = breakpoints[i];
596 if (supportsBreakpoint(breakpoint)) {
597 if (breakpoint instanceof ILineBreakpoint) {
598 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
600 if (breakpoint.isEnabled()) {
601 IMarker marker = breakpoint.getMarker();
602 if (marker != null) {
604 String name = marker.getResource()
605 .getLocation().toOSString();
606 if (name.equals(PHPDebugUtils
607 .unescapeString(filename))
608 && (lineBreakpoint.getLineNumber() == lineNumber)) {
610 .setBreakpoints(new IBreakpoint[] { breakpoint });
616 } catch (CoreException e) {
622 suspended(DebugEvent.BREAKPOINT);
625 public void handleDebugEvents(DebugEvent[] events) {
626 for (int i = 0; i < events.length; i++) {
627 DebugEvent event = events[i];
628 if ((event.getKind() == DebugEvent.CREATE)
629 && (event.getSource() instanceof XDebugElement)) {
630 if (((XDebugElement) event.getSource()).getModelIdentifier() == IXDebugConstants.ID_PHP_DEBUG_MODEL) {
631 if (event.getKind() == DebugEvent.CREATE)