4 package net.sourceforge.phpeclipse.xdebug.php.model;
6 //import java.io.IOException;
9 import javax.xml.parsers.DocumentBuilder;
10 import javax.xml.parsers.DocumentBuilderFactory;
11 import javax.xml.parsers.ParserConfigurationException;
13 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
14 import net.sourceforge.phpeclipse.xdebug.core.IPHPDebugEvent;
15 import net.sourceforge.phpeclipse.xdebug.core.IProxyEventListener;
16 import net.sourceforge.phpeclipse.xdebug.core.IXDebugPreferenceConstants;
17 import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils;
18 import net.sourceforge.phpeclipse.xdebug.core.PathMapItem;
19 import net.sourceforge.phpeclipse.xdebug.core.XDebugCorePlugin;
20 import net.sourceforge.phpeclipse.xdebug.core.XDebugProxy;
21 import net.sourceforge.phpeclipse.xdebug.php.launching.IXDebugConstants;
23 import org.eclipse.core.resources.IMarker;
24 import org.eclipse.core.resources.IMarkerDelta;
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.IPath;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.Path;
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.IDebugEventSetListener;
33 import org.eclipse.debug.core.ILaunch;
35 import org.eclipse.debug.core.model.IBreakpoint;
36 import org.eclipse.debug.core.model.IDebugTarget;
37 import org.eclipse.debug.core.model.ILineBreakpoint;
38 import org.eclipse.debug.core.model.IMemoryBlock;
39 import org.eclipse.debug.core.model.IProcess;
40 import org.eclipse.debug.core.model.IThread;
41 import org.eclipse.jface.resource.ImageDescriptor;
42 import org.eclipse.ui.model.IWorkbenchAdapter;
43 import org.w3c.dom.Document;
44 import org.w3c.dom.NamedNodeMap;
45 import org.w3c.dom.Node;
46 //import org.xml.sax.SAXException;
48 import net.sourceforge.phpeclipse.xdebug.core.xdebug.ResponseListener;
49 import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugConnection;
50 import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugResponse;
56 public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugEventSetListener, IProxyEventListener {
57 private IProcess fProcess;
59 private ILaunch fLaunch;
61 private int fDebugPort;
63 private boolean fSuspended = false;
65 private boolean fTerminated = false;
67 private XDebugThread fThread;
68 private IThread[] fThreads;
70 private XDebugConnection fDebugConnection;
72 private ResponseListener fResponseListener;
74 private String fIdeKey;
78 * Constructs a new debug target in the given launch and waits until
79 * someone with the ideKey connects to the Debugproxy
82 * @param launch containing launch
83 * @param process process of the interpreter
85 * @exception CoreException if unable to connect to host
87 public XDebugTarget(ILaunch launch, IProcess process, String ideKey) throws CoreException {
90 fDebugConnection = null;
92 fThreads = new IThread[0];
95 fDebugPort = XDebugCorePlugin.getDefault().getPreferenceStore().getInt(IXDebugPreferenceConstants.DEBUGPORT_PREFERENCE);
96 if (fDebugPort == 0) {
97 fDebugPort = IXDebugPreferenceConstants.DEFAULT_DEBUGPORT;
100 DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
101 DebugPlugin.getDefault().addDebugEventListener(this);
104 public Object getAdapter(Class arg0) {
105 if (IWorkbenchAdapter.class.equals(arg0)) {
106 return new IWorkbenchAdapter() {
107 public Object[] getChildren(Object o) {
108 Object[] children = null;
109 IThread[] threads = getThreads();
110 if (null != threads) {
111 children = new Object[threads.length];
112 for (int i = 0; i < threads.length; ++i)
113 children[i] = threads[i];
118 public ImageDescriptor getImageDescriptor(Object object) {
122 public String getLabel(Object o) {
123 String label = "(Unable to look up name... check error log)";
126 } catch (DebugException x) {
127 PHPeclipsePlugin.log(label, x);
132 public Object getParent(Object o) {
133 return XDebugTarget.this.getLaunch();
138 if (arg0 == XDebugElement.class) {
142 return super.getAdapter(arg0);
147 * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
149 public IProcess getProcess() {
154 * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
156 public IThread[] getThreads() {
161 * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
163 public boolean hasThreads() throws DebugException {
164 return (fThreads.length > 0);
168 * @see org.eclipse.debug.core.model.IDebugTarget#getName()
170 public String getName() throws DebugException {
171 return "PHP XDebug Client at localhost:" + fDebugPort;
175 * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
177 public boolean supportsBreakpoint(IBreakpoint breakpoint) {
178 if (breakpoint.getModelIdentifier().equals(IXDebugConstants.ID_PHP_BREAKPOINT_MODEL)) {
185 * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
187 public IDebugTarget getDebugTarget() {
192 * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
194 public ILaunch getLaunch() {
199 * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
201 public boolean canTerminate() {
202 if (getProcess()!=null) // ther is no running Process in remote debugging
203 return getProcess().canTerminate();
208 * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
210 public boolean isTerminated() {
211 // return getProcess().isTerminated();
216 * @see org.eclipse.debug.core.model.ITerminate#terminate()
218 public void terminate() throws DebugException {
223 if (XDebugCorePlugin.getDefault() != null) {
224 XDebugProxy proxy = XDebugCorePlugin.getDefault().getXDebugProxy();
225 proxy.removeProxyEventListener(this, fIdeKey);
227 System.out.println("XDebug.Target: ProxyEventlistener removed");
232 XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this);
233 fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
234 DebugPlugin.getDefault().removeDebugEventListener(this);
239 * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
241 public boolean canResume() {
246 * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
248 public boolean canSuspend() {
253 * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
255 public boolean isSuspended() {
260 * @see org.eclipse.debug.core.model.ISuspendResume#resume()
262 public void resume() throws DebugException {
263 if (fDebugConnection != null) {
264 fThread.setBreakpoints(null);
265 resumed(DebugEvent.RESUME);
266 fDebugConnection.run();
271 * Notification the target has resumed for the given reason
273 * @param detail reason for the resume
275 private void resumed(int detail) {
277 fThread.fireResumeEvent(detail);
281 * Notification the target has suspended for the given reason
283 * @param detail reason for the suspend
285 public void suspended(int detail) {
287 fThread.fireSuspendEvent(detail);
291 * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
293 public void suspend() throws DebugException {
297 * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
299 public void breakpointAdded(IBreakpoint breakpoint) {
300 IMarker marker = breakpoint.getMarker(); // Get the breakpoints marker info (It's the local workspace path)
301 IPath path = marker.getResource().getLocation(); // Get the full path + file for the given breakpoint (It's the local real path)
302 IPath cp = path.removeLastSegments(1); // Get the full path only (without the file name)
306 pathMap = fLaunch.getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_PHP_PATHMAP,(List)null);
307 } catch (CoreException e2) {
308 // TODO Auto-generated catch block
309 e2.printStackTrace();
312 if ((fDebugConnection != null) && // If there is a connection to XDebug
313 (!fDebugConnection.isClosed ()) && // and this connection is not closed
314 (fProcess == null)) { //
315 PathMapItem pmi = null;
317 for (int i = 0; i < pathMap.size(); i++) { // For every path map pair the user have set
318 pmi = new PathMapItem((String) pathMap.get(i)); // Get the path map pair
319 IPath local = (IPath)pmi.getLocalPath().clone(); // Get the local
320 local = local.makeAbsolute();
321 int matchedSegments = local.segmentCount();
323 if (local.matchingFirstSegments(cp) == matchedSegments) {
324 IPath newPath = pmi.getRemotePath();
325 newPath = newPath.append(path.removeFirstSegments(matchedSegments));
326 newPath = newPath.makeAbsolute();
328 if (supportsBreakpoint(breakpoint)) {
330 if (breakpoint.isEnabled()) {
331 if (marker != null) {
332 int id = fDebugConnection.breakpointSet (newPath.toString(),
333 ((ILineBreakpoint)breakpoint).getLineNumber(),
334 marker.getAttribute (XDebugBreakpoint.HIT_COUNT, -1),
335 marker.getAttribute (XDebugBreakpoint.CONDITION_ENABLED, false),
336 marker.getAttribute (XDebugBreakpoint.CONDITION, ""));
337 XDebugResponse dr = getResponse(id);
339 String bpid = dr.getAttributeValue("id");
341 if (!"".equals(bpid))
342 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid));
345 } catch (DebugException e) {
347 } catch (CoreException e) {
353 XDebugCorePlugin.log (IStatus.WARNING, "path do not match: local path: " + local.toString () + " breakpoint file: " + path.toString ());
360 * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
362 public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
363 IMarker marker = breakpoint.getMarker(); // Get the breakpoints marker info (It's the local workspace path)
365 if (supportsBreakpoint(breakpoint)) {
366 int id = marker.getAttribute (XDebugLineBreakpoint.BREAKPOINT_ID, -1);
369 fDebugConnection.breakpointRemove(id);
375 * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
377 public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
378 IMarker oldmarker = breakpoint.getMarker ();
380 if (supportsBreakpoint(breakpoint)) {
382 if (breakpoint.isEnabled () && // Check if breakpoint state changed from disabled to enabled
383 !delta.getAttribute ("org.eclipse.debug.core.enabled", false)) {
384 breakpointAdded (breakpoint);
386 else if (!breakpoint.isEnabled () && // Check if breakpoint state changed from enabled to disabled
387 delta.getAttribute ("org.eclipse.debug.core.enabled", true)) {
388 breakpointRemoved (breakpoint, null);
390 else if (oldmarker.getAttribute (XDebugLineBreakpoint.CHANGE_ID, 1) !=
391 delta.getAttribute (XDebugLineBreakpoint.CHANGE_ID, 0)) {
392 if (breakpoint.isEnabled ()) { // If the breakpoint is already enabled
393 breakpointRemoved (breakpoint, null); // we remove this breakpoint first
394 breakpointAdded (breakpoint); // and then we add again (else XDebug would have two breakpoints!).
397 breakpointRemoved (breakpoint, null);
400 } catch (CoreException e) {
407 * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
409 public boolean canDisconnect() {
414 * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
416 public void disconnect() throws DebugException {
420 * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
422 public boolean isDisconnected() {
427 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
429 public boolean supportsStorageRetrieval() {
434 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
436 public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
441 * Notification we have connected to the PHP debugger and it has been started.
442 * Resume the the debugger.
444 public void started() throws DebugException {
445 fThread.setBreakpoints(null);
446 fThread.setStepping(false);
448 int id = fDebugConnection.featureGet("detach");
450 XDebugResponse response = getResponse(id);
452 Integer.parseInt(response.getValue());
453 System.out.println("in Target.started()");
456 // Need to refactory plugin to get variables in lazy mode.
457 int id1 = fDebugConnection.featureSet("max_depth", "1024" );
458 XDebugResponse response1 = getResponse(id1);
459 if (response1.getAttributeValue("success").equals("1") ) {
460 System.out.println("Set depth to 1024 (hack)");
462 int id2 = fDebugConnection.featureSet("max_children", "1024" );
463 XDebugResponse response2 = getResponse(id2);
464 if (response2.getAttributeValue("success").equals("1") ) {
465 System.out.println("Set children to 1024 (hack)");
468 installDeferredBreakpoints();
471 } catch (DebugException e) {
477 * Install breakpoints that are already registered with the breakpoint
480 private void installDeferredBreakpoints() {
481 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
482 for (int i = 0; i < breakpoints.length; i++) {
483 breakpointAdded(breakpoints[i]);
488 * Returns the current stack frames in the target.
490 * @return the current stack frames in the target
491 * @throws DebugException if unable to perform the request
493 public XDebugResponse getStackFrames() throws DebugException {
494 int id = fDebugConnection.stackGet();
495 XDebugResponse lastResponse = getResponse(id);
500 * Single step the interpreter.
502 * @throws DebugException if the request fails
504 protected void step_over() throws DebugException {
505 fThread.setStepping(true);
506 resumed(DebugEvent.STEP_OVER);
507 fDebugConnection.stepOver();
511 * Single step the interpreter.
513 * @throws DebugException if the request fails
515 protected void step_into() throws DebugException {
516 fThread.setStepping(true);
517 resumed(DebugEvent.STEP_INTO);
518 fDebugConnection.stepInto();
522 * Single step the interpreter.
524 * @throws DebugException if the request fails
526 protected void step_out() throws DebugException {
527 fThread.setStepping(true);
528 resumed(DebugEvent.STEP_RETURN);
529 fDebugConnection.stepOut();
532 public boolean setVarValue(String name, String value) {
533 int id = fDebugConnection.setVarValue(name,value);
534 XDebugResponse response = getResponse(id);
536 if ((response.getAttributeValue("success")).equals("1")) {
543 public Node eval(String expression) throws DebugException {
544 Node evalProperty = null;
545 if (fDebugConnection != null) {
546 int id = fDebugConnection.eval(expression);
547 //Node evalProperty = new Node("");
549 XDebugResponse response = getResponse(id);
551 Node evalResponse = response.getParentNode();
552 /*Node*/ evalProperty = evalResponse.getFirstChild();
557 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
558 DocumentBuilder builder = null;
562 builder = factory.newDocumentBuilder();
563 } catch (ParserConfigurationException e) {
567 doc = builder.newDocument(); // .parse("");
568 evalProperty = doc.createElement("value");
569 /*} catch (SAXException e) {
571 } catch (IOException e) {
579 public void handleDebugEvents(DebugEvent[] events) {
580 for (int i = 0; i < events.length; i++) {
581 DebugEvent event = events[i];
583 if (fResponseListener != null) {
585 s = event.getSource();
586 if (s instanceof ResponseListener) {
587 if (!fResponseListener.equals((ResponseListener) s)) {
595 if (event.getKind() == DebugEvent.MODEL_SPECIFIC) {
596 switch (event.getDetail()) {
597 case IPHPDebugEvent.BREAKPOINT_HIT:
598 int id = fDebugConnection.stackGet();
599 XDebugResponse lastResponse = getResponse(id);
601 IBreakpoint breakpoint = breakpointHit(lastResponse.getParentNode());
603 if (breakpoint != null) {
604 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
605 fThread.incrementStepCounter();
606 suspended(DebugEvent.BREAKPOINT);
610 } catch (DebugException e ) {
615 case IPHPDebugEvent.STEP_END:
616 fThread.incrementStepCounter();
617 suspended(DebugEvent.STEP_END);
619 case IPHPDebugEvent.STOPPED:
627 public void stopped() {
628 if(fDebugConnection == null) {
632 resumed(DebugEvent.TERMINATE);
635 fDebugConnection.close();
639 // Dirty hack to check debugging mode (remote or local)
640 if (fProcess != null) {
643 } catch (DebugException e) {
647 fDebugConnection = null;
648 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
651 fThread.removeEventListeners();
653 fThreads = new IThread[0];
656 public void handleProxyEvent(XDebugConnection connection) {
657 //System.out.println("* New Connection - XDebug.Target: " + fDebugConnection.getSessionID());
659 if (setDebugConnection(connection)) {
660 fThread = new XDebugThread(this);
661 fThreads = new IThread[] {fThread};
662 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CHANGE));
665 } catch( DebugException e ){
671 private boolean setDebugConnection(XDebugConnection connection) {
672 if (connection != null && fDebugConnection == null) {
673 fDebugConnection = connection;
674 fResponseListener = new ResponseListener(connection);
686 * @return Returns the fDebugConnection.
688 public XDebugConnection getDebugConnection() {
689 return fDebugConnection;
692 public void addProcess(IProcess p) {
696 public Node getLocalVariables(int level) throws DebugException {
697 int id = fDebugConnection.contextGet(level, 0);
698 XDebugResponse response = getResponse(id);
700 return response.getParentNode();
703 public Node getGlobalVariables(int level) throws DebugException {
704 int id = fDebugConnection.contextGet(level, 1);
705 XDebugResponse response = getResponse(id);
707 return response.getParentNode();
711 fDebugConnection.stop();
714 protected IBreakpoint breakpointHit(Node node) {
715 Node child = node.getFirstChild();
716 if (child.getNodeName().equals("stack")) {
717 int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "lineno"));
718 String filename = PHPDebugUtils.getAttributeValue(child, "filename");
719 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
720 for (int i = 0; i < breakpoints.length; i++) {
721 IBreakpoint breakpoint = breakpoints[i];
722 if (supportsBreakpoint(breakpoint)) {
723 if (breakpoint instanceof ILineBreakpoint) {
724 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
726 if (breakpoint.isEnabled()) {
727 IMarker marker = breakpoint.getMarker();
728 if (marker != null) {
731 if (getProcess() == null) {
732 endfilename = marker.getResource().getLocation().lastSegment();
734 endfilename = marker.getResource().getLocation().toOSString();
737 int id = fDebugConnection.breakpointGet(marker.getAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,-1));
738 XDebugResponse dr = getResponse(id);
740 Node hitCo = dr.getParentNode().getFirstChild();
742 if (hitCo.hasAttributes()) {
743 NamedNodeMap listAttribute = hitCo.getAttributes();
744 Node attribute = listAttribute.getNamedItem("hit_count");
745 if (attribute !=null) {
746 hitCount = Integer.parseInt(attribute.getNodeValue());
750 Path path1 = new Path (PHPDebugUtils.unescapeString (filename));
751 Path path2 = new Path (endfilename);
753 if (path1.toString ().endsWith (path2.toString ())
754 // if (strPath1.endsWith (strPath2)
755 //if(PHPDebugUtils.unescapeString(filename).endsWith(endfilename)
756 && (lineBreakpoint.getLineNumber() == lineNumber) ) {
757 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) > 0) {
758 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) == hitCount) {
767 } catch (CoreException e) {
777 public void startListener() {
778 fResponseListener.schedule();
781 public void stopListener() {
782 fResponseListener.cancel();
784 public XDebugResponse getResponse(int id) {
785 XDebugResponse response = fResponseListener.getResponse(id);