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.Path;
28 import org.eclipse.debug.core.DebugEvent;
29 import org.eclipse.debug.core.DebugException;
30 import org.eclipse.debug.core.DebugPlugin;
31 import org.eclipse.debug.core.IDebugEventSetListener;
32 import org.eclipse.debug.core.ILaunch;
34 import org.eclipse.debug.core.model.IBreakpoint;
35 import org.eclipse.debug.core.model.IDebugTarget;
36 import org.eclipse.debug.core.model.ILineBreakpoint;
37 import org.eclipse.debug.core.model.IMemoryBlock;
38 import org.eclipse.debug.core.model.IProcess;
39 import org.eclipse.debug.core.model.IThread;
40 import org.eclipse.jface.resource.ImageDescriptor;
41 import org.eclipse.ui.model.IWorkbenchAdapter;
42 import org.w3c.dom.Document;
43 import org.w3c.dom.NamedNodeMap;
44 import org.w3c.dom.Node;
45 //import org.xml.sax.SAXException;
47 import net.sourceforge.phpeclipse.xdebug.core.xdebug.ResponseListener;
48 import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugConnection;
49 import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugResponse;
55 public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugEventSetListener, IProxyEventListener {
56 private IProcess fProcess;
58 private ILaunch fLaunch;
60 private int fDebugPort;
62 private boolean fSuspended = false;
64 private boolean fTerminated = false;
66 private XDebugThread fThread;
67 private IThread[] fThreads;
69 private XDebugConnection fDebugConnection;
71 private ResponseListener fResponseListener;
73 private String fIdeKey;
77 * Constructs a new debug target in the given launch and waits until
78 * someone with the ideKey connects to the Debugproxy
81 * @param launch containing launch
82 * @param process process of the interpreter
84 * @exception CoreException if unable to connect to host
86 public XDebugTarget(ILaunch launch, IProcess process, String ideKey) throws CoreException {
89 fDebugConnection = null;
91 fThreads = new IThread[0];
94 fDebugPort = XDebugCorePlugin.getDefault().getPreferenceStore().getInt(IXDebugPreferenceConstants.DEBUGPORT_PREFERENCE);
95 if (fDebugPort == 0) {
96 fDebugPort = IXDebugPreferenceConstants.DEFAULT_DEBUGPORT;
99 DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
100 DebugPlugin.getDefault().addDebugEventListener(this);
103 public Object getAdapter(Class arg0) {
104 if (IWorkbenchAdapter.class.equals(arg0)) {
105 return new IWorkbenchAdapter() {
106 public Object[] getChildren(Object o) {
107 Object[] children = null;
108 IThread[] threads = getThreads();
109 if (null != threads) {
110 children = new Object[threads.length];
111 for (int i = 0; i < threads.length; ++i)
112 children[i] = threads[i];
117 public ImageDescriptor getImageDescriptor(Object object) {
121 public String getLabel(Object o) {
122 String label = "(Unable to look up name... check error log)";
125 } catch (DebugException x) {
126 PHPeclipsePlugin.log(label, x);
131 public Object getParent(Object o) {
132 return XDebugTarget.this.getLaunch();
137 if (arg0 == XDebugElement.class) {
141 return super.getAdapter(arg0);
146 * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
148 public IProcess getProcess() {
153 * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
155 public IThread[] getThreads() {
160 * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
162 public boolean hasThreads() throws DebugException {
163 return (fThreads.length > 0);
167 * @see org.eclipse.debug.core.model.IDebugTarget#getName()
169 public String getName() throws DebugException {
170 return "PHP XDebug Client at localhost:" + fDebugPort;
174 * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
176 public boolean supportsBreakpoint(IBreakpoint breakpoint) {
177 if (breakpoint.getModelIdentifier().equals(IXDebugConstants.ID_PHP_BREAKPOINT_MODEL)) {
184 * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
186 public IDebugTarget getDebugTarget() {
191 * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
193 public ILaunch getLaunch() {
198 * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
200 public boolean canTerminate() {
201 if (getProcess()!=null) // ther is no running Process in remote debugging
202 return getProcess().canTerminate();
207 * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
209 public boolean isTerminated() {
210 // return getProcess().isTerminated();
215 * @see org.eclipse.debug.core.model.ITerminate#terminate()
217 public void terminate() throws DebugException {
222 if (XDebugCorePlugin.getDefault() != null) {
223 XDebugProxy proxy = XDebugCorePlugin.getDefault().getXDebugProxy();
224 proxy.removeProxyEventListener(this, fIdeKey);
226 System.out.println("XDebug.Target: ProxyEventlistener removed");
231 XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this);
232 fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
233 DebugPlugin.getDefault().removeDebugEventListener(this);
238 * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
240 public boolean canResume() {
245 * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
247 public boolean canSuspend() {
252 * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
254 public boolean isSuspended() {
259 * @see org.eclipse.debug.core.model.ISuspendResume#resume()
261 public void resume() throws DebugException {
262 if (fDebugConnection != null) {
263 fThread.setBreakpoints(null);
264 resumed(DebugEvent.RESUME);
265 fDebugConnection.run();
270 * Notification the target has resumed for the given reason
272 * @param detail reason for the resume
274 private void resumed(int detail) {
276 fThread.fireResumeEvent(detail);
280 * Notification the target has suspended for the given reason
282 * @param detail reason for the suspend
284 public void suspended(int detail) {
286 fThread.fireSuspendEvent(detail);
290 * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
292 public void suspend() throws DebugException {
296 * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
298 public void breakpointAdded(IBreakpoint breakpoint) {
299 IMarker marker = breakpoint.getMarker(); // Get the breakpoints marker info (It's the local workspace path)
300 IPath path = marker.getResource().getLocation(); // Get the full path + file for the given breakpoint (It's the local real path)
301 IPath cp = path.removeLastSegments(1); // Get the full path only (without the file name)
305 pathMap = fLaunch.getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_PHP_PATHMAP,(List)null);
306 } catch (CoreException e2) {
307 // TODO Auto-generated catch block
308 e2.printStackTrace();
311 if ((fDebugConnection != null) && // If there is a connection to XDebug
312 (!fDebugConnection.isClosed ()) && // and this connection is not closed
313 (fProcess == null)) { //
314 PathMapItem pmi = null;
316 for (int i = 0; i < pathMap.size(); i++) { // For every path map pair the user have set
317 pmi = new PathMapItem((String) pathMap.get(i)); // Get the path map pair
318 IPath local = (IPath)pmi.getLocalPath().clone(); // Get the local
319 local = local.makeAbsolute();
320 int matchedSegments = local.segmentCount();
322 if (local.matchingFirstSegments(cp) == matchedSegments) {
323 IPath newPath = pmi.getRemotePath();
324 newPath = newPath.append(path.removeFirstSegments(matchedSegments));
325 newPath = newPath.makeAbsolute();
327 if (supportsBreakpoint(breakpoint)) {
329 if (breakpoint.isEnabled()) {
330 if (marker != null) {
331 int id = fDebugConnection.breakpointSet (newPath.toString(),
332 ((ILineBreakpoint)breakpoint).getLineNumber(),
333 marker.getAttribute (XDebugBreakpoint.HIT_COUNT, -1),
334 marker.getAttribute (XDebugBreakpoint.CONDITION_ENABLED, false),
335 marker.getAttribute (XDebugBreakpoint.CONDITION, ""));
336 XDebugResponse dr = getResponse(id);
338 String bpid = dr.getAttributeValue("id");
340 if (!"".equals(bpid))
341 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid));
344 } catch (DebugException e) {
346 } catch (CoreException e) {
356 * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
358 public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
359 IMarker marker = breakpoint.getMarker(); // Get the breakpoints marker info (It's the local workspace path)
361 if (supportsBreakpoint(breakpoint)) {
362 int id = marker.getAttribute (XDebugLineBreakpoint.BREAKPOINT_ID, -1);
365 fDebugConnection.breakpointRemove(id);
371 * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
373 public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
374 IMarker oldmarker = breakpoint.getMarker ();
376 if (supportsBreakpoint(breakpoint)) {
378 if (breakpoint.isEnabled () && // Check if breakpoint state changed from disabled to enabled
379 !delta.getAttribute ("org.eclipse.debug.core.enabled", false)) {
380 breakpointAdded (breakpoint);
382 else if (!breakpoint.isEnabled () && // Check if breakpoint state changed from enabled to disabled
383 delta.getAttribute ("org.eclipse.debug.core.enabled", true)) {
384 breakpointRemoved (breakpoint, null);
386 else if (oldmarker.getAttribute (XDebugLineBreakpoint.CHANGE_ID, 1) !=
387 delta.getAttribute (XDebugLineBreakpoint.CHANGE_ID, 0)) {
388 if (breakpoint.isEnabled ()) { // If the breakpoint is already enabled
389 breakpointRemoved (breakpoint, null); // we remove this breakpoint first
390 breakpointAdded (breakpoint); // and then we add again (else XDebug would have two breakpoints!).
393 breakpointRemoved (breakpoint, null);
396 } catch (CoreException e) {
403 * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
405 public boolean canDisconnect() {
410 * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
412 public void disconnect() throws DebugException {
416 * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
418 public boolean isDisconnected() {
423 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
425 public boolean supportsStorageRetrieval() {
430 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
432 public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
437 * Notification we have connected to the PHP debugger and it has been started.
438 * Resume the the debugger.
440 public void started() throws DebugException {
441 fThread.setBreakpoints(null);
442 fThread.setStepping(false);
444 int id = fDebugConnection.featureGet("detach");
446 XDebugResponse response = getResponse(id);
448 Integer.parseInt(response.getValue());
449 System.out.println("in Target.started()");
452 // Need to refactory plugin to get variables in lazy mode.
453 int id1 = fDebugConnection.featureSet("max_depth", "1024" );
454 XDebugResponse response1 = getResponse(id1);
455 if (response1.getAttributeValue("success").equals("1") ) {
456 System.out.println("Set depth to 1024 (hack)");
458 int id2 = fDebugConnection.featureSet("max_children", "1024" );
459 XDebugResponse response2 = getResponse(id2);
460 if (response2.getAttributeValue("success").equals("1") ) {
461 System.out.println("Set children to 1024 (hack)");
464 installDeferredBreakpoints();
467 } catch (DebugException e) {
473 * Install breakpoints that are already registered with the breakpoint
476 private void installDeferredBreakpoints() {
477 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
478 for (int i = 0; i < breakpoints.length; i++) {
479 breakpointAdded(breakpoints[i]);
484 * Returns the current stack frames in the target.
486 * @return the current stack frames in the target
487 * @throws DebugException if unable to perform the request
489 public XDebugResponse getStackFrames() throws DebugException {
490 int id = fDebugConnection.stackGet();
491 XDebugResponse lastResponse = getResponse(id);
496 * Single step the interpreter.
498 * @throws DebugException if the request fails
500 protected void step_over() throws DebugException {
501 fThread.setStepping(true);
502 resumed(DebugEvent.STEP_OVER);
503 fDebugConnection.stepOver();
507 * Single step the interpreter.
509 * @throws DebugException if the request fails
511 protected void step_into() throws DebugException {
512 fThread.setStepping(true);
513 resumed(DebugEvent.STEP_INTO);
514 fDebugConnection.stepInto();
518 * Single step the interpreter.
520 * @throws DebugException if the request fails
522 protected void step_out() throws DebugException {
523 fThread.setStepping(true);
524 resumed(DebugEvent.STEP_RETURN);
525 fDebugConnection.stepOut();
528 public boolean setVarValue(String name, String value) {
529 int id = fDebugConnection.setVarValue(name,value);
530 XDebugResponse response = getResponse(id);
532 if ((response.getAttributeValue("success")).equals("1")) {
539 public Node eval(String expression) throws DebugException {
540 Node evalProperty = null;
541 if (fDebugConnection != null) {
542 int id = fDebugConnection.eval(expression);
543 //Node evalProperty = new Node("");
545 XDebugResponse response = getResponse(id);
547 Node evalResponse = response.getParentNode();
548 /*Node*/ evalProperty = evalResponse.getFirstChild();
553 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
554 DocumentBuilder builder = null;
558 builder = factory.newDocumentBuilder();
559 } catch (ParserConfigurationException e) {
563 doc = builder.newDocument(); // .parse("");
564 evalProperty = doc.createElement("value");
565 /*} catch (SAXException e) {
567 } catch (IOException e) {
575 public void handleDebugEvents(DebugEvent[] events) {
576 for (int i = 0; i < events.length; i++) {
577 DebugEvent event = events[i];
579 if (fResponseListener != null) {
581 s = event.getSource();
582 if (s instanceof ResponseListener) {
583 if (!fResponseListener.equals((ResponseListener) s)) {
591 if (event.getKind() == DebugEvent.MODEL_SPECIFIC) {
592 switch (event.getDetail()) {
593 case IPHPDebugEvent.BREAKPOINT_HIT:
594 int id = fDebugConnection.stackGet();
595 XDebugResponse lastResponse = getResponse(id);
597 IBreakpoint breakpoint = breakpointHit(lastResponse.getParentNode());
599 if (breakpoint != null) {
600 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
601 fThread.incrementStepCounter();
602 suspended(DebugEvent.BREAKPOINT);
606 } catch (DebugException e ) {
611 case IPHPDebugEvent.STEP_END:
612 fThread.incrementStepCounter();
613 suspended(DebugEvent.STEP_END);
615 case IPHPDebugEvent.STOPPED:
623 public void stopped() {
624 if(fDebugConnection == null) {
628 resumed(DebugEvent.TERMINATE);
631 fDebugConnection.close();
635 // Dirty hack to check debugging mode (remote or local)
636 if (fProcess != null) {
639 } catch (DebugException e) {
643 fDebugConnection = null;
644 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
647 fThread.removeEventListeners();
649 fThreads = new IThread[0];
652 public void handleProxyEvent(XDebugConnection connection) {
653 //System.out.println("* New Connection - XDebug.Target: " + fDebugConnection.getSessionID());
655 if (setDebugConnection(connection)) {
656 fThread = new XDebugThread(this);
657 fThreads = new IThread[] {fThread};
658 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CHANGE));
661 } catch( DebugException e ){
667 private boolean setDebugConnection(XDebugConnection connection) {
668 if (connection != null && fDebugConnection == null) {
669 fDebugConnection = connection;
670 fResponseListener = new ResponseListener(connection);
682 * @return Returns the fDebugConnection.
684 public XDebugConnection getDebugConnection() {
685 return fDebugConnection;
688 public void addProcess(IProcess p) {
692 public Node getLocalVariables(int level) throws DebugException {
693 int id = fDebugConnection.contextGet(level, 0);
694 XDebugResponse response = getResponse(id);
696 return response.getParentNode();
699 public Node getGlobalVariables(int level) throws DebugException {
700 int id = fDebugConnection.contextGet(level, 1);
701 XDebugResponse response = getResponse(id);
703 return response.getParentNode();
707 fDebugConnection.stop();
710 protected IBreakpoint breakpointHit(Node node) {
711 Node child = node.getFirstChild();
712 if (child.getNodeName().equals("stack")) {
713 int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "lineno"));
714 String filename = PHPDebugUtils.getAttributeValue(child, "filename");
715 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
716 for (int i = 0; i < breakpoints.length; i++) {
717 IBreakpoint breakpoint = breakpoints[i];
718 if (supportsBreakpoint(breakpoint)) {
719 if (breakpoint instanceof ILineBreakpoint) {
720 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
722 if (breakpoint.isEnabled()) {
723 IMarker marker = breakpoint.getMarker();
724 if (marker != null) {
727 if (getProcess() == null) {
728 endfilename = marker.getResource().getLocation().lastSegment();
730 endfilename = marker.getResource().getLocation().toOSString();
733 int id = fDebugConnection.breakpointGet(marker.getAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,-1));
734 XDebugResponse dr = getResponse(id);
736 Node hitCo = dr.getParentNode().getFirstChild();
738 if (hitCo.hasAttributes()) {
739 NamedNodeMap listAttribute = hitCo.getAttributes();
740 Node attribute = listAttribute.getNamedItem("hit_count");
741 if (attribute !=null) {
742 hitCount = Integer.parseInt(attribute.getNodeValue());
746 Path path1 = new Path (PHPDebugUtils.unescapeString (filename));
747 Path path2 = new Path (endfilename);
749 if (path1.toString ().endsWith (path2.toString ())
750 // if (strPath1.endsWith (strPath2)
751 //if(PHPDebugUtils.unescapeString(filename).endsWith(endfilename)
752 && (lineBreakpoint.getLineNumber() == lineNumber) ) {
753 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) > 0) {
754 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) == hitCount) {
763 } catch (CoreException e) {
773 public void startListener() {
774 fResponseListener.schedule();
777 public void stopListener() {
778 fResponseListener.cancel();
780 public XDebugResponse getResponse(int id) {
781 XDebugResponse response = fResponseListener.getResponse(id);