4 package net.sourceforge.phpeclipse.xdebug.php.model;
8 import net.sourceforge.phpeclipse.xdebug.core.IPHPDebugEvent;
9 import net.sourceforge.phpeclipse.xdebug.core.IProxyEventListener;
10 import net.sourceforge.phpeclipse.xdebug.core.IXDebugPreferenceConstants;
11 import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils;
12 import net.sourceforge.phpeclipse.xdebug.core.PathMapItem;
13 import net.sourceforge.phpeclipse.xdebug.core.XDebugCorePlugin;
14 import net.sourceforge.phpeclipse.xdebug.core.XDebugProxy;
15 import net.sourceforge.phpeclipse.xdebug.php.launching.IXDebugConstants;
17 import org.eclipse.core.resources.IMarker;
18 import org.eclipse.core.resources.IMarkerDelta;
19 import org.eclipse.core.runtime.CoreException;
20 import org.eclipse.core.runtime.IPath;
21 import org.eclipse.debug.core.DebugEvent;
22 import org.eclipse.debug.core.DebugException;
23 import org.eclipse.debug.core.DebugPlugin;
24 import org.eclipse.debug.core.IDebugEventSetListener;
25 import org.eclipse.debug.core.ILaunch;
27 import org.eclipse.debug.core.model.IBreakpoint;
28 import org.eclipse.debug.core.model.IDebugTarget;
29 import org.eclipse.debug.core.model.ILineBreakpoint;
30 import org.eclipse.debug.core.model.IMemoryBlock;
31 import org.eclipse.debug.core.model.IProcess;
32 import org.eclipse.debug.core.model.IThread;
33 import org.w3c.dom.NamedNodeMap;
34 import org.w3c.dom.Node;
36 import net.sourceforge.phpeclipse.xdebug.core.xdebug.ResponseListener;
37 import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugConnection;
38 import net.sourceforge.phpeclipse.xdebug.core.xdebug.ResponseListener.XDebugResponse;
44 public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugEventSetListener, IProxyEventListener {
45 private IProcess fProcess;
47 private ILaunch fLaunch;
49 private int fDebugPort;
51 private boolean fSuspended = false;
53 private boolean fTerminated = false;
55 private XDebugThread fThread;
56 private IThread[] fThreads;
58 private XDebugConnection fDebugConnection;
60 private ResponseListener fResponseListener;
62 private String fIdeKey;
66 * Constructs a new debug target in the given launch and waits until
67 * someone with the ideKey connects to the Debugproxy
70 * @param launch containing launch
71 * @param process process of the interpreter
73 * @exception CoreException if unable to connect to host
75 public XDebugTarget(ILaunch launch, IProcess process, String ideKey) throws CoreException {
78 fDebugConnection = null;
80 fThreads = new IThread[0];
83 fDebugPort = XDebugCorePlugin.getDefault().getPreferenceStore().getInt(IXDebugPreferenceConstants.DEBUGPORT_PREFERENCE);
84 if (fDebugPort == 0) {
85 fDebugPort = IXDebugPreferenceConstants.DEFAULT_DEBUGPORT;
88 DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
89 DebugPlugin.getDefault().addDebugEventListener(this);
93 * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
95 public IProcess getProcess() {
100 * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
102 public IThread[] getThreads() throws DebugException {
107 * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
109 public boolean hasThreads() throws DebugException {
110 return (fThreads.length > 0);
114 * @see org.eclipse.debug.core.model.IDebugTarget#getName()
116 public String getName() throws DebugException {
117 return "PHP XDebug Client at localhost:" + fDebugPort;
121 * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
123 public boolean supportsBreakpoint(IBreakpoint breakpoint) {
124 if (breakpoint.getModelIdentifier().equals(IXDebugConstants.ID_PHP_DEBUG_MODEL)) {
131 * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
133 public IDebugTarget getDebugTarget() {
138 * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
140 public ILaunch getLaunch() {
145 * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
147 public boolean canTerminate() {
148 if (getProcess()!=null) // ther is no running Process in remote debugging
149 return getProcess().canTerminate();
154 * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
156 public boolean isTerminated() {
157 // return getProcess().isTerminated();
162 * @see org.eclipse.debug.core.model.ITerminate#terminate()
164 public void terminate() throws DebugException {
169 if (XDebugCorePlugin.getDefault() != null) {
170 XDebugProxy proxy = XDebugCorePlugin.getDefault().getXDebugProxy();
171 proxy.removeProxyEventListener(this, fIdeKey);
173 System.out.println("XDebug.Target: ProxyEventlistener removed");
178 XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this);
179 fThread.fireTerminateEvent();
180 DebugPlugin.getDefault().removeDebugEventListener(this);
185 * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
187 public boolean canResume() {
192 * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
194 public boolean canSuspend() {
199 * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
201 public boolean isSuspended() {
206 * @see org.eclipse.debug.core.model.ISuspendResume#resume()
208 public void resume() throws DebugException {
209 if (fDebugConnection != null) {
210 fThread.setBreakpoints(null);
211 resumed(DebugEvent.RESUME);
212 fDebugConnection.run();
217 * Notification the target has resumed for the given reason
219 * @param detail reason for the resume
221 private void resumed(int detail) {
223 fThread.fireResumeEvent(detail);
227 * Notification the target has suspended for the given reason
229 * @param detail reason for the suspend
231 public void suspended(int detail) {
233 fThread.fireSuspendEvent(detail);
237 * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
239 public void suspend() throws DebugException {
243 * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
245 public void breakpointAdded(IBreakpoint breakpoint) {
246 IMarker marker = breakpoint.getMarker();
247 IPath path = marker.getResource().getLocation();
248 IPath cp = path.removeLastSegments(1);
251 pathMap = fLaunch.getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_PHP_PATHMAP,(List)null);
252 } catch (CoreException e2) {
253 // TODO Auto-generated catch block
254 e2.printStackTrace();
257 if (fDebugConnection != null)
258 if (!fDebugConnection.isClosed()) {
259 if (fProcess == null) {
260 PathMapItem pmi = null;
261 for (int i = 0; i < pathMap.size(); i++) {
262 pmi = new PathMapItem((String) pathMap.get(i));
263 IPath local = (IPath)pmi.getLocalPath().clone();
264 local = local.makeAbsolute();
265 int matchedSegments = local.segmentCount();
266 if (local.matchingFirstSegments(cp) == matchedSegments) {
267 IPath newPath = pmi.getRemotePath();
268 newPath = newPath.append(path.removeFirstSegments(matchedSegments));
269 newPath = newPath.makeAbsolute();
270 if (supportsBreakpoint(breakpoint)) {
272 if (breakpoint.isEnabled()) {
273 if (marker != null) {
274 int id = fDebugConnection.breakpointSet(newPath.toString(), ((ILineBreakpoint)breakpoint).getLineNumber(), marker.getAttribute(XDebugBreakpoint.HIT_COUNT,-1));
275 XDebugResponse dr = getResponse(id);
277 String bpid = dr.getAttributeValue("id");
279 if (!"".equals(bpid))
280 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid));
283 } catch (DebugException e) {
285 } catch (CoreException e) {
292 if (supportsBreakpoint(breakpoint)) {
294 if (breakpoint.isEnabled()) {
295 if (marker != null) {
296 int id = fDebugConnection.breakpointSet(path.toString(), ((ILineBreakpoint)breakpoint).getLineNumber(), marker.getAttribute(XDebugBreakpoint.HIT_COUNT,-1));
297 XDebugResponse dr = getResponse(id);
298 String bpid = dr.getAttributeValue("id");
300 if (!"".equals(bpid))
301 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid));
304 } catch (DebugException e) {
306 } catch (CoreException e) {
315 * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
317 public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
318 if (supportsBreakpoint(breakpoint)) {
320 int id =((XDebugLineBreakpoint)breakpoint).getID();
322 fDebugConnection.breakpointRemove(id);
323 } catch (CoreException e) {
329 * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
331 public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
332 // if (supportsBreakpoint(breakpoint)) {
334 // if (breakpoint.isEnabled()) {
335 // breakpointAdded(breakpoint);
337 // breakpointRemoved(breakpoint, null);
339 // } catch (CoreException e) {
345 * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
347 public boolean canDisconnect() {
352 * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
354 public void disconnect() throws DebugException {
358 * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
360 public boolean isDisconnected() {
362 // return (fDebugConnection==null);
366 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
368 public boolean supportsStorageRetrieval() {
373 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
375 public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
380 * Notification we have connected to the PHP debugger and it has been started.
381 * Resume the the debugger.
383 public void started() throws DebugException {
384 fThread.setBreakpoints(null);
385 fThread.setStepping(false);
387 int id = fDebugConnection.featureGet("detach");
389 XDebugResponse response = getResponse(id);
391 Integer.parseInt(response.getValue());
392 System.out.println("in Target.started()");
395 // Need to refactory plugin to get variables in lazy mode.
396 int id1 = fDebugConnection.featureSet("max_depth", "1024" );
397 XDebugResponse response1 = getResponse(id1);
398 if (response1.getAttributeValue("success").equals("1") ) {
399 System.out.println("Set depth to 1024 (hack)");
401 int id2 = fDebugConnection.featureSet("max_children", "1024" );
402 XDebugResponse response2 = getResponse(id2);
403 if (response2.getAttributeValue("success").equals("1") ) {
404 System.out.println("Set children to 1024 (hack)");
407 installDeferredBreakpoints();
410 } catch (DebugException e) {
416 * Install breakpoints that are already registered with the breakpoint
419 private void installDeferredBreakpoints() {
420 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
421 for (int i = 0; i < breakpoints.length; i++) {
422 breakpointAdded(breakpoints[i]);
427 * Returns the current stack frames in the target.
429 * @return the current stack frames in the target
430 * @throws DebugException if unable to perform the request
432 public XDebugResponse getStackFrames() throws DebugException {
433 int id = fDebugConnection.stackGet();
434 XDebugResponse lastResponse = getResponse(id);
439 * Single step the interpreter.
441 * @throws DebugException if the request fails
443 protected void step_over() throws DebugException {
444 fThread.setStepping(true);
445 resumed(DebugEvent.STEP_OVER);
446 fDebugConnection.stepOver();
450 * Single step the interpreter.
452 * @throws DebugException if the request fails
454 protected void step_into() throws DebugException {
455 fThread.setStepping(true);
456 resumed(DebugEvent.STEP_INTO);
457 fDebugConnection.stepInto();
461 * Single step the interpreter.
463 * @throws DebugException if the request fails
465 protected void step_out() throws DebugException {
466 fThread.setStepping(true);
467 resumed(DebugEvent.STEP_RETURN);
468 fDebugConnection.stepOut();
471 public boolean setVarValue(String name, String value) {
472 int id = fDebugConnection.setVarValue(name,value);
473 XDebugResponse response = getResponse(id);
475 if ((response.getAttributeValue("success")).equals("1")) {
482 public Node eval(String expression) {
483 int id = fDebugConnection.eval(expression);
484 XDebugResponse response = getResponse(id);
486 Node evalResponse = response.getParentNode();
487 Node evalProperty = evalResponse.getFirstChild();
492 public void handleDebugEvents(DebugEvent[] events) {
493 for (int i = 0; i < events.length; i++) {
494 DebugEvent event = events[i];
496 if (fResponseListener != null) {
498 s = event.getSource();
499 if (s instanceof ResponseListener) {
500 if (!fResponseListener.equals((ResponseListener) s)) {
508 if (event.getKind() == DebugEvent.MODEL_SPECIFIC) {
509 switch (event.getDetail()) {
510 case IPHPDebugEvent.BREAKPOINT_HIT:
511 int id = fDebugConnection.stackGet();
512 XDebugResponse lastResponse = getResponse(id);
514 IBreakpoint breakpoint = breakpointHit(lastResponse.getParentNode());
516 if (breakpoint != null) {
517 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
518 fThread.incrementStepCounter();
519 suspended(DebugEvent.BREAKPOINT);
523 } catch (DebugException e ) {
528 case IPHPDebugEvent.STEP_END:
529 fThread.incrementStepCounter();
530 suspended(DebugEvent.STEP_END);
532 case IPHPDebugEvent.STOPPED:
540 public void stopped() {
541 if(fDebugConnection == null) {
545 resumed(DebugEvent.TERMINATE);
548 fDebugConnection.close();
552 // Dirty hack to check debugging mode (remote or local)
553 if (fProcess != null) {
556 } catch (DebugException e) {
560 fDebugConnection = null;
561 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
564 fThread.removeEventListeners();
566 fThreads = new IThread[0];
569 public void handleProxyEvent(/*String ideKey,*/ XDebugConnection connection) {
570 setDebugConnection(connection);
571 System.out.println("* New Connection - XDebug.Target: " + fDebugConnection.getSessionID());
573 fThread = new XDebugThread(this);
574 fThreads = new IThread[] {fThread};
575 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CHANGE));
578 } catch( DebugException e ){
583 private void setDebugConnection(XDebugConnection connection) {
584 if (connection != null) {
585 fDebugConnection = connection;
586 fResponseListener = new ResponseListener(connection);
592 * @return Returns the fDebugConnection.
594 public XDebugConnection getDebugConnection() {
595 return fDebugConnection;
598 public void addProcess(IProcess p) {
602 public Node getLocalVariables(int level) throws DebugException {
603 int id = fDebugConnection.contextGet(level, 0);
604 XDebugResponse response = getResponse(id);
606 return response.getParentNode();
609 public Node getGlobalVariables(int level) throws DebugException {
610 int id = fDebugConnection.contextGet(level, 1);
611 XDebugResponse response = getResponse(id);
613 return response.getParentNode();
617 fDebugConnection.stop();
620 protected IBreakpoint breakpointHit(Node node) {
621 Node child = node.getFirstChild();
622 if (child.getNodeName().equals("stack")) {
623 int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "lineno"));
624 String filename = PHPDebugUtils.getAttributeValue(child, "filename");
625 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
626 for (int i = 0; i < breakpoints.length; i++) {
627 IBreakpoint breakpoint = breakpoints[i];
628 if (supportsBreakpoint(breakpoint)) {
629 if (breakpoint instanceof ILineBreakpoint) {
630 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
632 if (breakpoint.isEnabled()) {
633 IMarker marker = breakpoint.getMarker();
634 if (marker != null) {
637 if (getProcess() == null) {
638 endfilename = marker.getResource().getLocation().lastSegment();
640 endfilename = marker.getResource().getLocation().toOSString();
643 int id = fDebugConnection.breakpointGet(marker.getAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,-1));
644 XDebugResponse dr = getResponse(id);
646 Node hitCo = dr.getParentNode().getFirstChild();
648 if (hitCo.hasAttributes()) {
649 NamedNodeMap listAttribute = hitCo.getAttributes();
650 Node attribute = listAttribute.getNamedItem("hit_count");
651 if (attribute !=null) {
652 hitCount = Integer.parseInt(attribute.getNodeValue());
656 if(PHPDebugUtils.unescapeString(filename).endsWith(endfilename)
657 && (lineBreakpoint.getLineNumber() == lineNumber) ) {
658 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) > 0) {
659 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) == hitCount) {
668 } catch (CoreException e) {
678 public void startListener() {
679 fResponseListener.schedule();
682 public void stopListener() {
683 fResponseListener.cancel();
685 public XDebugResponse getResponse(int id) {
686 XDebugResponse response = fResponseListener.getResponse(id);