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 fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
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() {
365 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
367 public boolean supportsStorageRetrieval() {
372 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
374 public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
379 * Notification we have connected to the PHP debugger and it has been started.
380 * Resume the the debugger.
382 public void started() throws DebugException {
383 fThread.setBreakpoints(null);
384 fThread.setStepping(false);
386 int id = fDebugConnection.featureGet("detach");
388 XDebugResponse response = getResponse(id);
390 Integer.parseInt(response.getValue());
391 System.out.println("in Target.started()");
394 // Need to refactory plugin to get variables in lazy mode.
395 int id1 = fDebugConnection.featureSet("max_depth", "1024" );
396 XDebugResponse response1 = getResponse(id1);
397 if (response1.getAttributeValue("success").equals("1") ) {
398 System.out.println("Set depth to 1024 (hack)");
400 int id2 = fDebugConnection.featureSet("max_children", "1024" );
401 XDebugResponse response2 = getResponse(id2);
402 if (response2.getAttributeValue("success").equals("1") ) {
403 System.out.println("Set children to 1024 (hack)");
406 installDeferredBreakpoints();
409 } catch (DebugException e) {
415 * Install breakpoints that are already registered with the breakpoint
418 private void installDeferredBreakpoints() {
419 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
420 for (int i = 0; i < breakpoints.length; i++) {
421 breakpointAdded(breakpoints[i]);
426 * Returns the current stack frames in the target.
428 * @return the current stack frames in the target
429 * @throws DebugException if unable to perform the request
431 public XDebugResponse getStackFrames() throws DebugException {
432 int id = fDebugConnection.stackGet();
433 XDebugResponse lastResponse = getResponse(id);
438 * Single step the interpreter.
440 * @throws DebugException if the request fails
442 protected void step_over() throws DebugException {
443 fThread.setStepping(true);
444 resumed(DebugEvent.STEP_OVER);
445 fDebugConnection.stepOver();
449 * Single step the interpreter.
451 * @throws DebugException if the request fails
453 protected void step_into() throws DebugException {
454 fThread.setStepping(true);
455 resumed(DebugEvent.STEP_INTO);
456 fDebugConnection.stepInto();
460 * Single step the interpreter.
462 * @throws DebugException if the request fails
464 protected void step_out() throws DebugException {
465 fThread.setStepping(true);
466 resumed(DebugEvent.STEP_RETURN);
467 fDebugConnection.stepOut();
470 public boolean setVarValue(String name, String value) {
471 int id = fDebugConnection.setVarValue(name,value);
472 XDebugResponse response = getResponse(id);
474 if ((response.getAttributeValue("success")).equals("1")) {
481 public Node eval(String expression) {
482 int id = fDebugConnection.eval(expression);
483 XDebugResponse response = getResponse(id);
485 Node evalResponse = response.getParentNode();
486 Node evalProperty = evalResponse.getFirstChild();
491 public void handleDebugEvents(DebugEvent[] events) {
492 for (int i = 0; i < events.length; i++) {
493 DebugEvent event = events[i];
495 if (fResponseListener != null) {
497 s = event.getSource();
498 if (s instanceof ResponseListener) {
499 if (!fResponseListener.equals((ResponseListener) s)) {
507 if (event.getKind() == DebugEvent.MODEL_SPECIFIC) {
508 switch (event.getDetail()) {
509 case IPHPDebugEvent.BREAKPOINT_HIT:
510 int id = fDebugConnection.stackGet();
511 XDebugResponse lastResponse = getResponse(id);
513 IBreakpoint breakpoint = breakpointHit(lastResponse.getParentNode());
515 if (breakpoint != null) {
516 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
517 fThread.incrementStepCounter();
518 suspended(DebugEvent.BREAKPOINT);
522 } catch (DebugException e ) {
527 case IPHPDebugEvent.STEP_END:
528 fThread.incrementStepCounter();
529 suspended(DebugEvent.STEP_END);
531 case IPHPDebugEvent.STOPPED:
539 public void stopped() {
540 if(fDebugConnection == null) {
544 resumed(DebugEvent.TERMINATE);
547 fDebugConnection.close();
551 // Dirty hack to check debugging mode (remote or local)
552 if (fProcess != null) {
555 } catch (DebugException e) {
559 fDebugConnection = null;
560 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
563 fThread.removeEventListeners();
565 fThreads = new IThread[0];
568 public void handleProxyEvent(XDebugConnection connection) {
569 setDebugConnection(connection);
570 //System.out.println("* New Connection - XDebug.Target: " + fDebugConnection.getSessionID());
572 fThread = new XDebugThread(this);
573 fThreads = new IThread[] {fThread};
574 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CHANGE));
577 } catch( DebugException e ){
582 private void setDebugConnection(XDebugConnection connection) {
583 if (connection != null) {
584 fDebugConnection = connection;
585 fResponseListener = new ResponseListener(connection);
591 * @return Returns the fDebugConnection.
593 public XDebugConnection getDebugConnection() {
594 return fDebugConnection;
597 public void addProcess(IProcess p) {
601 public Node getLocalVariables(int level) throws DebugException {
602 int id = fDebugConnection.contextGet(level, 0);
603 XDebugResponse response = getResponse(id);
605 return response.getParentNode();
608 public Node getGlobalVariables(int level) throws DebugException {
609 int id = fDebugConnection.contextGet(level, 1);
610 XDebugResponse response = getResponse(id);
612 return response.getParentNode();
616 fDebugConnection.stop();
619 protected IBreakpoint breakpointHit(Node node) {
620 Node child = node.getFirstChild();
621 if (child.getNodeName().equals("stack")) {
622 int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "lineno"));
623 String filename = PHPDebugUtils.getAttributeValue(child, "filename");
624 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
625 for (int i = 0; i < breakpoints.length; i++) {
626 IBreakpoint breakpoint = breakpoints[i];
627 if (supportsBreakpoint(breakpoint)) {
628 if (breakpoint instanceof ILineBreakpoint) {
629 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
631 if (breakpoint.isEnabled()) {
632 IMarker marker = breakpoint.getMarker();
633 if (marker != null) {
636 if (getProcess() == null) {
637 endfilename = marker.getResource().getLocation().lastSegment();
639 endfilename = marker.getResource().getLocation().toOSString();
642 int id = fDebugConnection.breakpointGet(marker.getAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,-1));
643 XDebugResponse dr = getResponse(id);
645 Node hitCo = dr.getParentNode().getFirstChild();
647 if (hitCo.hasAttributes()) {
648 NamedNodeMap listAttribute = hitCo.getAttributes();
649 Node attribute = listAttribute.getNamedItem("hit_count");
650 if (attribute !=null) {
651 hitCount = Integer.parseInt(attribute.getNodeValue());
655 if(PHPDebugUtils.unescapeString(filename).endsWith(endfilename)
656 && (lineBreakpoint.getLineNumber() == lineNumber) ) {
657 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) > 0) {
658 if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) == hitCount) {
667 } catch (CoreException e) {
677 public void startListener() {
678 fResponseListener.schedule();
681 public void stopListener() {
682 fResponseListener.cancel();
684 public XDebugResponse getResponse(int id) {
685 XDebugResponse response = fResponseListener.getResponse(id);