4 package net.sourceforge.phpeclipse.xdebug.php.model;
6 //import java.io.ByteArrayInputStream;
7 //import java.io.IOException;
10 import javax.xml.parsers.DocumentBuilder;
11 import javax.xml.parsers.DocumentBuilderFactory;
12 import javax.xml.parsers.ParserConfigurationException;
14 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
15 import net.sourceforge.phpeclipse.xdebug.core.IPHPDebugEvent;
16 import net.sourceforge.phpeclipse.xdebug.core.IProxyEventListener;
17 import net.sourceforge.phpeclipse.xdebug.core.IXDebugPreferenceConstants;
18 import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils;
19 import net.sourceforge.phpeclipse.xdebug.core.PathMapItem;
20 import net.sourceforge.phpeclipse.xdebug.core.XDebugCorePlugin;
21 import net.sourceforge.phpeclipse.xdebug.core.XDebugProxy;
22 import net.sourceforge.phpeclipse.xdebug.php.launching.IXDebugConstants;
24 import org.eclipse.core.resources.IMarker;
25 import org.eclipse.core.resources.IMarkerDelta;
26 import org.eclipse.core.runtime.CoreException;
27 import org.eclipse.core.runtime.IPath;
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(), ((ILineBreakpoint)breakpoint).getLineNumber(), marker.getAttribute(XDebugBreakpoint.HIT_COUNT,-1));
332 XDebugResponse dr = getResponse(id);
334 String bpid = dr.getAttributeValue("id");
336 if (!"".equals(bpid))
337 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid));
340 } catch (DebugException e) {
342 } catch (CoreException e) {
349 if (supportsBreakpoint(breakpoint)) {
351 if (breakpoint.isEnabled()) {
352 if (marker != null) {
353 int id = fDebugConnection.breakpointSet (path.toString(),
354 ((ILineBreakpoint) breakpoint).getLineNumber(),
355 marker.getAttribute (XDebugBreakpoint.HIT_COUNT, -1));
356 XDebugResponse dr = getResponse(id);
357 String bpid = dr.getAttributeValue("id");
359 if (!"".equals(bpid)) {
360 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid));
364 } catch (DebugException e) {
366 } catch (CoreException e) {
374 * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
376 public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
377 if (supportsBreakpoint(breakpoint)) {
379 int id =((XDebugLineBreakpoint)breakpoint).getID();
381 fDebugConnection.breakpointRemove(id);
382 } catch (CoreException e) {
388 * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
390 public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
391 // if (supportsBreakpoint(breakpoint)) {
393 // if (breakpoint.isEnabled()) {
394 // breakpointAdded(breakpoint);
396 // breakpointRemoved(breakpoint, null);
398 // } catch (CoreException e) {
404 * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
406 public boolean canDisconnect() {
411 * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
413 public void disconnect() throws DebugException {
417 * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
419 public boolean isDisconnected() {
424 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
426 public boolean supportsStorageRetrieval() {
431 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
433 public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
438 * Notification we have connected to the PHP debugger and it has been started.
439 * Resume the the debugger.
441 public void started() throws DebugException {
442 fThread.setBreakpoints(null);
443 fThread.setStepping(false);
445 int id = fDebugConnection.featureGet("detach");
447 XDebugResponse response = getResponse(id);
449 Integer.parseInt(response.getValue());
450 System.out.println("in Target.started()");
453 // Need to refactory plugin to get variables in lazy mode.
454 int id1 = fDebugConnection.featureSet("max_depth", "1024" );
455 XDebugResponse response1 = getResponse(id1);
456 if (response1.getAttributeValue("success").equals("1") ) {
457 System.out.println("Set depth to 1024 (hack)");
459 int id2 = fDebugConnection.featureSet("max_children", "1024" );
460 XDebugResponse response2 = getResponse(id2);
461 if (response2.getAttributeValue("success").equals("1") ) {
462 System.out.println("Set children to 1024 (hack)");
465 installDeferredBreakpoints();
468 } catch (DebugException e) {
474 * Install breakpoints that are already registered with the breakpoint
477 private void installDeferredBreakpoints() {
478 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
479 for (int i = 0; i < breakpoints.length; i++) {
480 breakpointAdded(breakpoints[i]);
485 * Returns the current stack frames in the target.
487 * @return the current stack frames in the target
488 * @throws DebugException if unable to perform the request
490 public XDebugResponse getStackFrames() throws DebugException {
491 int id = fDebugConnection.stackGet();
492 XDebugResponse lastResponse = getResponse(id);
497 * Single step the interpreter.
499 * @throws DebugException if the request fails
501 protected void step_over() throws DebugException {
502 fThread.setStepping(true);
503 resumed(DebugEvent.STEP_OVER);
504 fDebugConnection.stepOver();
508 * Single step the interpreter.
510 * @throws DebugException if the request fails
512 protected void step_into() throws DebugException {
513 fThread.setStepping(true);
514 resumed(DebugEvent.STEP_INTO);
515 fDebugConnection.stepInto();
519 * Single step the interpreter.
521 * @throws DebugException if the request fails
523 protected void step_out() throws DebugException {
524 fThread.setStepping(true);
525 resumed(DebugEvent.STEP_RETURN);
526 fDebugConnection.stepOut();
529 public boolean setVarValue(String name, String value) {
530 int id = fDebugConnection.setVarValue(name,value);
531 XDebugResponse response = getResponse(id);
533 if ((response.getAttributeValue("success")).equals("1")) {
540 public Node eval(String expression) throws DebugException {
541 Node evalProperty = null;
542 if (fDebugConnection != null) {
543 int id = fDebugConnection.eval(expression);
544 //Node evalProperty = new Node("");
546 XDebugResponse response = getResponse(id);
548 Node evalResponse = response.getParentNode();
549 /*Node*/ evalProperty = evalResponse.getFirstChild();
554 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
555 DocumentBuilder builder = null;
559 builder = factory.newDocumentBuilder();
560 } catch (ParserConfigurationException e) {
564 doc = builder.newDocument(); // .parse("");
565 evalProperty = doc.createElement("value");
566 /*} catch (SAXException e) {
568 } catch (IOException e) {
576 public void handleDebugEvents(DebugEvent[] events) {
577 for (int i = 0; i < events.length; i++) {
578 DebugEvent event = events[i];
580 if (fResponseListener != null) {
582 s = event.getSource();
583 if (s instanceof ResponseListener) {
584 if (!fResponseListener.equals((ResponseListener) s)) {
592 if (event.getKind() == DebugEvent.MODEL_SPECIFIC) {
593 switch (event.getDetail()) {
594 case IPHPDebugEvent.BREAKPOINT_HIT:
595 int id = fDebugConnection.stackGet();
596 XDebugResponse lastResponse = getResponse(id);
598 IBreakpoint breakpoint = breakpointHit(lastResponse.getParentNode());
600 if (breakpoint != null) {
601 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
602 fThread.incrementStepCounter();
603 suspended(DebugEvent.BREAKPOINT);
607 } catch (DebugException e ) {
612 case IPHPDebugEvent.STEP_END:
613 fThread.incrementStepCounter();
614 suspended(DebugEvent.STEP_END);
616 case IPHPDebugEvent.STOPPED:
624 public void stopped() {
625 if(fDebugConnection == null) {
629 resumed(DebugEvent.TERMINATE);
632 fDebugConnection.close();
636 // Dirty hack to check debugging mode (remote or local)
637 if (fProcess != null) {
640 } catch (DebugException e) {
644 fDebugConnection = null;
645 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
648 fThread.removeEventListeners();
650 fThreads = new IThread[0];
653 public void handleProxyEvent(XDebugConnection connection) {
654 //System.out.println("* New Connection - XDebug.Target: " + fDebugConnection.getSessionID());
656 if (setDebugConnection(connection)) {
657 fThread = new XDebugThread(this);
658 fThreads = new IThread[] {fThread};
659 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CHANGE));
662 } catch( DebugException e ){
668 private boolean setDebugConnection(XDebugConnection connection) {
669 if (connection != null && fDebugConnection == null) {
670 fDebugConnection = connection;
671 fResponseListener = new ResponseListener(connection);
683 * @return Returns the fDebugConnection.
685 public XDebugConnection getDebugConnection() {
686 return fDebugConnection;
689 public void addProcess(IProcess p) {
693 public Node getLocalVariables(int level) throws DebugException {
694 int id = fDebugConnection.contextGet(level, 0);
695 XDebugResponse response = getResponse(id);
697 return response.getParentNode();
700 public Node getGlobalVariables(int level) throws DebugException {
701 int id = fDebugConnection.contextGet(level, 1);
702 XDebugResponse response = getResponse(id);
704 return response.getParentNode();
708 fDebugConnection.stop();
711 protected IBreakpoint breakpointHit(Node node) {
712 Node child = node.getFirstChild();
713 if (child.getNodeName().equals("stack")) {
714 int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "lineno"));
715 String filename = PHPDebugUtils.getAttributeValue(child, "filename");
716 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
717 for (int i = 0; i < breakpoints.length; i++) {
718 IBreakpoint breakpoint = breakpoints[i];
719 if (supportsBreakpoint(breakpoint)) {
720 if (breakpoint instanceof ILineBreakpoint) {
721 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
723 if (breakpoint.isEnabled()) {
724 IMarker marker = breakpoint.getMarker();
725 if (marker != null) {
728 if (getProcess() == null) {
729 endfilename = marker.getResource().getLocation().lastSegment();
731 endfilename = marker.getResource().getLocation().toOSString();
734 int id = fDebugConnection.breakpointGet(marker.getAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,-1));
735 XDebugResponse dr = getResponse(id);
737 Node hitCo = dr.getParentNode().getFirstChild();
739 if (hitCo.hasAttributes()) {
740 NamedNodeMap listAttribute = hitCo.getAttributes();
741 Node attribute = listAttribute.getNamedItem("hit_count");
742 if (attribute !=null) {
743 hitCount = Integer.parseInt(attribute.getNodeValue());
747 if(PHPDebugUtils.unescapeString(filename).endsWith(endfilename)
748 && (lineBreakpoint.getLineNumber() == lineNumber) ) {
749 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) > 0) {
750 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) == hitCount) {
759 } catch (CoreException e) {
769 public void startListener() {
770 fResponseListener.schedule();
773 public void stopListener() {
774 fResponseListener.cancel();
776 public XDebugResponse getResponse(int id) {
777 XDebugResponse response = fResponseListener.getResponse(id);