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.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(), ((ILineBreakpoint)breakpoint).getLineNumber(), marker.getAttribute(XDebugBreakpoint.HIT_COUNT,-1));
333 XDebugResponse dr = getResponse(id);
335 String bpid = dr.getAttributeValue("id");
337 if (!"".equals(bpid))
338 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid));
341 } catch (DebugException e) {
343 } catch (CoreException e) {
350 if (supportsBreakpoint(breakpoint)) {
352 if (breakpoint.isEnabled()) {
353 if (marker != null) {
354 int id = fDebugConnection.breakpointSet (path.toString(),
355 ((ILineBreakpoint) breakpoint).getLineNumber(),
356 marker.getAttribute (XDebugBreakpoint.HIT_COUNT, -1));
357 XDebugResponse dr = getResponse(id);
358 String bpid = dr.getAttributeValue("id");
360 if (!"".equals(bpid)) {
361 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid));
365 } catch (DebugException e) {
367 } catch (CoreException e) {
375 * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
377 public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
378 if (supportsBreakpoint(breakpoint)) {
380 int id =((XDebugLineBreakpoint)breakpoint).getID();
382 fDebugConnection.breakpointRemove(id);
383 } catch (CoreException e) {
389 * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
391 public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
392 // if (supportsBreakpoint(breakpoint)) {
394 // if (breakpoint.isEnabled()) {
395 // breakpointAdded(breakpoint);
397 // breakpointRemoved(breakpoint, null);
399 // } catch (CoreException e) {
405 * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
407 public boolean canDisconnect() {
412 * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
414 public void disconnect() throws DebugException {
418 * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
420 public boolean isDisconnected() {
425 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
427 public boolean supportsStorageRetrieval() {
432 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
434 public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
439 * Notification we have connected to the PHP debugger and it has been started.
440 * Resume the the debugger.
442 public void started() throws DebugException {
443 fThread.setBreakpoints(null);
444 fThread.setStepping(false);
446 int id = fDebugConnection.featureGet("detach");
448 XDebugResponse response = getResponse(id);
450 Integer.parseInt(response.getValue());
451 System.out.println("in Target.started()");
454 // Need to refactory plugin to get variables in lazy mode.
455 int id1 = fDebugConnection.featureSet("max_depth", "1024" );
456 XDebugResponse response1 = getResponse(id1);
457 if (response1.getAttributeValue("success").equals("1") ) {
458 System.out.println("Set depth to 1024 (hack)");
460 int id2 = fDebugConnection.featureSet("max_children", "1024" );
461 XDebugResponse response2 = getResponse(id2);
462 if (response2.getAttributeValue("success").equals("1") ) {
463 System.out.println("Set children to 1024 (hack)");
466 installDeferredBreakpoints();
469 } catch (DebugException e) {
475 * Install breakpoints that are already registered with the breakpoint
478 private void installDeferredBreakpoints() {
479 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
480 for (int i = 0; i < breakpoints.length; i++) {
481 breakpointAdded(breakpoints[i]);
486 * Returns the current stack frames in the target.
488 * @return the current stack frames in the target
489 * @throws DebugException if unable to perform the request
491 public XDebugResponse getStackFrames() throws DebugException {
492 int id = fDebugConnection.stackGet();
493 XDebugResponse lastResponse = getResponse(id);
498 * Single step the interpreter.
500 * @throws DebugException if the request fails
502 protected void step_over() throws DebugException {
503 fThread.setStepping(true);
504 resumed(DebugEvent.STEP_OVER);
505 fDebugConnection.stepOver();
509 * Single step the interpreter.
511 * @throws DebugException if the request fails
513 protected void step_into() throws DebugException {
514 fThread.setStepping(true);
515 resumed(DebugEvent.STEP_INTO);
516 fDebugConnection.stepInto();
520 * Single step the interpreter.
522 * @throws DebugException if the request fails
524 protected void step_out() throws DebugException {
525 fThread.setStepping(true);
526 resumed(DebugEvent.STEP_RETURN);
527 fDebugConnection.stepOut();
530 public boolean setVarValue(String name, String value) {
531 int id = fDebugConnection.setVarValue(name,value);
532 XDebugResponse response = getResponse(id);
534 if ((response.getAttributeValue("success")).equals("1")) {
541 public Node eval(String expression) throws DebugException {
542 Node evalProperty = null;
543 if (fDebugConnection != null) {
544 int id = fDebugConnection.eval(expression);
545 //Node evalProperty = new Node("");
547 XDebugResponse response = getResponse(id);
549 Node evalResponse = response.getParentNode();
550 /*Node*/ evalProperty = evalResponse.getFirstChild();
555 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
556 DocumentBuilder builder = null;
560 builder = factory.newDocumentBuilder();
561 } catch (ParserConfigurationException e) {
565 doc = builder.newDocument(); // .parse("");
566 evalProperty = doc.createElement("value");
567 /*} catch (SAXException e) {
569 } catch (IOException e) {
577 public void handleDebugEvents(DebugEvent[] events) {
578 for (int i = 0; i < events.length; i++) {
579 DebugEvent event = events[i];
581 if (fResponseListener != null) {
583 s = event.getSource();
584 if (s instanceof ResponseListener) {
585 if (!fResponseListener.equals((ResponseListener) s)) {
593 if (event.getKind() == DebugEvent.MODEL_SPECIFIC) {
594 switch (event.getDetail()) {
595 case IPHPDebugEvent.BREAKPOINT_HIT:
596 int id = fDebugConnection.stackGet();
597 XDebugResponse lastResponse = getResponse(id);
599 IBreakpoint breakpoint = breakpointHit(lastResponse.getParentNode());
601 if (breakpoint != null) {
602 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
603 fThread.incrementStepCounter();
604 suspended(DebugEvent.BREAKPOINT);
608 } catch (DebugException e ) {
613 case IPHPDebugEvent.STEP_END:
614 fThread.incrementStepCounter();
615 suspended(DebugEvent.STEP_END);
617 case IPHPDebugEvent.STOPPED:
625 public void stopped() {
626 if(fDebugConnection == null) {
630 resumed(DebugEvent.TERMINATE);
633 fDebugConnection.close();
637 // Dirty hack to check debugging mode (remote or local)
638 if (fProcess != null) {
641 } catch (DebugException e) {
645 fDebugConnection = null;
646 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
649 fThread.removeEventListeners();
651 fThreads = new IThread[0];
654 public void handleProxyEvent(XDebugConnection connection) {
655 //System.out.println("* New Connection - XDebug.Target: " + fDebugConnection.getSessionID());
657 if (setDebugConnection(connection)) {
658 fThread = new XDebugThread(this);
659 fThreads = new IThread[] {fThread};
660 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CHANGE));
663 } catch( DebugException e ){
669 private boolean setDebugConnection(XDebugConnection connection) {
670 if (connection != null && fDebugConnection == null) {
671 fDebugConnection = connection;
672 fResponseListener = new ResponseListener(connection);
684 * @return Returns the fDebugConnection.
686 public XDebugConnection getDebugConnection() {
687 return fDebugConnection;
690 public void addProcess(IProcess p) {
694 public Node getLocalVariables(int level) throws DebugException {
695 int id = fDebugConnection.contextGet(level, 0);
696 XDebugResponse response = getResponse(id);
698 return response.getParentNode();
701 public Node getGlobalVariables(int level) throws DebugException {
702 int id = fDebugConnection.contextGet(level, 1);
703 XDebugResponse response = getResponse(id);
705 return response.getParentNode();
709 fDebugConnection.stop();
712 protected IBreakpoint breakpointHit(Node node) {
713 Node child = node.getFirstChild();
714 if (child.getNodeName().equals("stack")) {
715 int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "lineno"));
716 String filename = PHPDebugUtils.getAttributeValue(child, "filename");
717 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
718 for (int i = 0; i < breakpoints.length; i++) {
719 IBreakpoint breakpoint = breakpoints[i];
720 if (supportsBreakpoint(breakpoint)) {
721 if (breakpoint instanceof ILineBreakpoint) {
722 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
724 if (breakpoint.isEnabled()) {
725 IMarker marker = breakpoint.getMarker();
726 if (marker != null) {
729 if (getProcess() == null) {
730 endfilename = marker.getResource().getLocation().lastSegment();
732 endfilename = marker.getResource().getLocation().toOSString();
735 int id = fDebugConnection.breakpointGet(marker.getAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,-1));
736 XDebugResponse dr = getResponse(id);
738 Node hitCo = dr.getParentNode().getFirstChild();
740 if (hitCo.hasAttributes()) {
741 NamedNodeMap listAttribute = hitCo.getAttributes();
742 Node attribute = listAttribute.getNamedItem("hit_count");
743 if (attribute !=null) {
744 hitCount = Integer.parseInt(attribute.getNodeValue());
748 Path path1 = new Path (PHPDebugUtils.unescapeString (filename));
749 Path path2 = new Path (endfilename);
751 if (path1.toString ().endsWith (path2.toString ())
752 // if (strPath1.endsWith (strPath2)
753 //if(PHPDebugUtils.unescapeString(filename).endsWith(endfilename)
754 && (lineBreakpoint.getLineNumber() == lineNumber) ) {
755 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) > 0) {
756 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) == hitCount) {
765 } catch (CoreException e) {
775 public void startListener() {
776 fResponseListener.schedule();
779 public void stopListener() {
780 fResponseListener.cancel();
782 public XDebugResponse getResponse(int id) {
783 XDebugResponse response = fResponseListener.getResponse(id);