4 package net.sourceforge.phpeclipse.xdebug.php.model;
6 import java.net.MalformedURLException;
10 import net.sourceforge.phpeclipse.xdebug.core.Base64;
11 import net.sourceforge.phpeclipse.xdebug.core.IPHPDebugEvent;
12 import net.sourceforge.phpeclipse.xdebug.core.IProxyEventListener;
13 import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils;
14 import net.sourceforge.phpeclipse.xdebug.core.PathMapItem;
15 import net.sourceforge.phpeclipse.xdebug.core.XDebugCorePlugin;
16 import net.sourceforge.phpeclipse.xdebug.core.XDebugProxy;
17 import net.sourceforge.phpeclipse.xdebug.php.launching.IXDebugConstants;
19 import org.eclipse.core.resources.IMarker;
20 import org.eclipse.core.resources.IMarkerDelta;
21 import org.eclipse.core.runtime.CoreException;
22 import org.eclipse.core.runtime.IPath;
23 import org.eclipse.debug.core.DebugEvent;
24 import org.eclipse.debug.core.DebugException;
25 import org.eclipse.debug.core.DebugPlugin;
26 import org.eclipse.debug.core.IDebugEventSetListener;
27 import org.eclipse.debug.core.ILaunch;
29 import org.eclipse.debug.core.ILaunchListener;
31 import org.eclipse.debug.core.model.IBreakpoint;
32 import org.eclipse.debug.core.model.IDebugTarget;
33 import org.eclipse.debug.core.model.ILineBreakpoint;
34 import org.eclipse.debug.core.model.IMemoryBlock;
35 import org.eclipse.debug.core.model.IProcess;
36 import org.eclipse.debug.core.model.IStackFrame;
37 import org.eclipse.debug.core.model.IThread;
38 //import org.eclipse.debug.core.model.IValue;
39 import org.eclipse.debug.core.model.IVariable;
40 import org.w3c.dom.Node;
41 import org.w3c.dom.NodeList;
43 import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugConnection;
44 import net.sourceforge.phpeclipse.xdebug.core.xdebug.ResponseListener.DebugResponse;
50 public class XDebugTarget extends XDebugElement implements IDebugTarget, ILaunchListener, IDebugEventSetListener, IProxyEventListener {
51 private IProcess fProcess;
53 private ILaunch fLaunch;
55 private int fDebugPort;
57 private boolean fSuspended = false;
59 private boolean fTerminated = false;
61 private XDebugThread fThread;
62 private IThread[] fThreads;
64 private XDebugConnection fDebugConnection;
66 private String fIdeKey;
70 * Constructs a new debug target in the given launch and waits until
71 * someone with the ideKey connects to the Debugproxy
74 * @param launch containing launch
75 * @param process process of the interpreter
77 * @param pathMap Pathmap for the debug session
78 * @exception CoreException if unable to connect to host
80 public XDebugTarget(ILaunch launch, IProcess process, String ideKey) throws CoreException {
84 fDebugConnection = null;
86 fThreads = new IThread[0];
89 XDebugProxy proxy = XDebugCorePlugin.getDefault().getXDebugProxy();
90 proxy.addProxyEventListener(this,ideKey);
91 if (!proxy.isRunning())
94 proxy.setTarget(this);
96 fDebugPort = proxy.getProxyPort();
98 DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
99 DebugPlugin.getDefault().addDebugEventListener(this);
103 * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
105 public IProcess getProcess() {
110 * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
112 public IThread[] getThreads() throws DebugException {
117 * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
119 public boolean hasThreads() throws DebugException {
120 return (fThreads.length>0);
124 * @see org.eclipse.debug.core.model.IDebugTarget#getName()
126 public String getName() throws DebugException {
127 return "PHP XDebug Client at localhost:" + fDebugPort;
131 * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
133 public boolean supportsBreakpoint(IBreakpoint breakpoint) {
134 if (breakpoint.getModelIdentifier().equals(IXDebugConstants.ID_PHP_DEBUG_MODEL)) {
141 * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
143 public IDebugTarget getDebugTarget() {
148 * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
150 public ILaunch getLaunch() {
155 * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
157 public boolean canTerminate() {
158 if (getProcess()!=null) // ther is no running Process in remote debugging
159 return getProcess().canTerminate();
164 * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
166 public boolean isTerminated() {
167 // return getProcess().isTerminated();
172 * @see org.eclipse.debug.core.model.ITerminate#terminate()
174 public void terminate() throws DebugException {
175 XDebugProxy proxy=XDebugCorePlugin.getDefault().getXDebugProxy();
177 proxy.removeProxyEventListener(this,fIdeKey);
178 System.out.println("XDebug.Target: ProxyEventlistener removed");
180 //fDisconnected = true;
182 XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this);
183 fireTerminateEvent();
184 DebugPlugin.getDefault().removeDebugEventListener(this);
186 fThread.removeEventListeners();
190 * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
192 public boolean canResume() {
197 * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
199 public boolean canSuspend() {
204 * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
206 public boolean isSuspended() {
211 * @see org.eclipse.debug.core.model.ISuspendResume#resume()
213 public void resume() throws DebugException {
214 if (fDebugConnection != null) {
215 fThread.setBreakpoints(null);
216 fDebugConnection.run();
221 * Notification the target has resumed for the given reason
223 * @param detail reason for the resume
225 private void resumed(int detail) {
227 fThread.fireResumeEvent(detail);
231 * Notification the target has suspended for the given reason
233 * @param detail reason for the suspend
235 public void suspended(int detail) {
237 fThread.fireSuspendEvent(detail);
241 * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
243 public void suspend() throws DebugException {
247 * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
249 public void breakpointAdded(IBreakpoint breakpoint) {
250 IMarker marker = breakpoint.getMarker();
251 IPath path = marker.getResource().getFullPath();
252 IPath cp = path.removeLastSegments(1);
255 pathMap = fLaunch.getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_PHP_PATHMAP,(List)null);
256 } catch (CoreException e2) {
257 // TODO Auto-generated catch block
258 e2.printStackTrace();
261 if (!fDebugConnection.isClosed()) {
262 if (fProcess == null) {
263 PathMapItem pmi = null;
264 for (int i = 0; i < pathMap.size(); i++) {
265 pmi = new PathMapItem((String) pathMap.get(i));
266 IPath local = (IPath)pmi.getLocalPath().clone();
267 local = local.makeAbsolute();
268 int matchedSegments = local.segmentCount();
269 if (local.matchingFirstSegments(cp) == matchedSegments) {
270 IPath newPath = pmi.getRemotePath();
271 newPath = newPath.append(path.removeFirstSegments(matchedSegments));
272 newPath = newPath.makeAbsolute();
273 if (supportsBreakpoint(breakpoint)) {
275 if (breakpoint.isEnabled()) {
276 if (marker != null) {
277 //fDebugConnection.addBreakpoint(breakpoint, newPath);
278 DebugResponse dr = fDebugConnection.breakpointSet(newPath.toString(), ((ILineBreakpoint)breakpoint).getLineNumber());
279 String bpid = dr.getAttributeValue("id");
281 if (!"".equals(bpid))
282 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid));
285 } catch (DebugException e) {
287 } catch (CoreException e) {
294 if (supportsBreakpoint(breakpoint)) {
296 if (breakpoint.isEnabled()) {
297 if (marker != null) {
298 DebugResponse dr = fDebugConnection.breakpointSet(path.toString(), ((ILineBreakpoint)breakpoint).getLineNumber());
299 String bpid = dr.getAttributeValue("id");
301 if (!"".equals(bpid))
302 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid));
305 } catch (DebugException e) {
307 } catch (CoreException e) {
316 * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
318 public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
319 if (supportsBreakpoint(breakpoint)) {
321 int id =((XDebugLineBreakpoint)breakpoint).getID();
323 fDebugConnection.breakpointRemove(id);
324 } catch (CoreException e) {
330 * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
332 public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
333 // if (supportsBreakpoint(breakpoint)) {
335 // if (breakpoint.isEnabled()) {
336 // breakpointAdded(breakpoint);
338 // breakpointRemoved(breakpoint, null);
340 // } catch (CoreException e) {
346 * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
348 public boolean canDisconnect() {
353 * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
355 public void disconnect() throws DebugException {
359 * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
361 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 Integer.parseInt(fDebugConnection.featureGet("detach").getValue());
389 System.out.println("in Target.started()");
390 DebugResponse response = fDebugConnection.featureGet("max_children");
391 String a1 = response.getValue();
392 System.out.println("max children:"+a1);
393 DebugResponse response1 = fDebugConnection.featureGet("max_children");
394 String a2 = response1.getValue();
395 System.out.println("max depth:"+a2);
398 boolean res = fDebugConnection.featureSet("max_depth", "100" );
400 System.out.println("Set depth to 100 (hack)");
403 installDeferredBreakpoints();
406 } catch (DebugException e) {
412 * Install breakpoints that are already registered with the breakpoint
415 private void installDeferredBreakpoints() {
416 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
417 for (int i = 0; i < breakpoints.length; i++) {
418 breakpointAdded(breakpoints[i]);
423 * Called when this debug target terminates.
425 public void terminated() {
428 XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this);
429 fireTerminateEvent();
430 DebugPlugin.getDefault().removeDebugEventListener(this);
431 fThread.removeEventListeners();
435 * Returns the current stack frames in the target.
437 * @return the current stack frames in the target
438 * @throws DebugException if unable to perform the request
440 public IStackFrame[] getStackFrames() throws DebugException {
441 DebugResponse lastResponse = fDebugConnection.stackGet(0);
443 if (lastResponse.isError())
444 return new IStackFrame[0];
445 Node response = lastResponse.getParentNode();
446 NodeList frames = response.getChildNodes();
447 IStackFrame[] theFrames = new IStackFrame[frames.getLength()];
448 for (int i = 0; i < frames.getLength(); i++) {
449 Node stackNode = frames.item(i);
450 XDebugStackFrame frame = new XDebugStackFrame(fThread, i);
451 String level =PHPDebugUtils.getAttributeValue(stackNode,"level");
452 if (!"".equals(level))
453 frame.setLevel(Integer.parseInt(level));
455 frame.setType(PHPDebugUtils.getAttributeValue(stackNode,"type"));
456 String fileName=PHPDebugUtils.unescapeString(PHPDebugUtils.getAttributeValue(stackNode,"filename"));
457 String lineNo=PHPDebugUtils.getAttributeValue(stackNode,"lineno");
459 if (!"".equals(lineNo))
460 frame.setLineNumber(Integer.parseInt(lineNo));
462 frame.setWhere(PHPDebugUtils.getAttributeValue(stackNode,"where"));
465 frame.setFullName(new URL(fileName));
466 } catch (MalformedURLException e) {
470 frame.incrementStepCounter();
472 theFrames[i] = frame;
479 * Single step the interpreter.
481 * @throws DebugException if the request fails
483 protected void step_over() throws DebugException {
484 fThread.setStepping(true);
485 resumed(DebugEvent.STEP_OVER);
486 fDebugConnection.stepOver();
490 * Single step the interpreter.
492 * @throws DebugException if the request fails
494 protected void step_into() throws DebugException {
495 fThread.setStepping(true);
496 resumed(DebugEvent.STEP_INTO);
497 fDebugConnection.stepInto();
501 * Single step the interpreter.
503 * @throws DebugException if the request fails
505 protected void step_out() throws DebugException {
506 fThread.setStepping(true);
507 resumed(DebugEvent.STEP_RETURN);
508 fDebugConnection.stepOut();
512 * Returns the current value of the given variable.
515 * @return variable value
516 * @throws DebugException if the request fails
518 /*protected IValue getVariableValue(XDebugVariable variable) throws DebugException {
523 * Returns the values on the data stack (top down)
525 * @return the values on the data stack (top down)
527 /*public IValue[] getDataStack() throws DebugException {
528 return new IValue[0];
531 public boolean setVarValue(String name, String value) {
532 return fDebugConnection.setVarValue(name,value);
535 public void handleDebugEvents(DebugEvent[] events) {
536 for (int i = 0; i < events.length; i++) {
537 DebugEvent event = events[i];
538 if (event.getKind() == DebugEvent.MODEL_SPECIFIC) {
539 switch (event.getDetail()) {
540 case IPHPDebugEvent.BREAKPOINT_HIT:
541 DebugResponse lastResponse = ((XDebugConnection) fDebugConnection).stackGet(0);
543 IBreakpoint breakpoint = breakpointHit(lastResponse.getParentNode());
545 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
546 fThread.incrementStepCounter();
547 suspended(DebugEvent.BREAKPOINT);
549 case IPHPDebugEvent.STEP_END:
550 fThread.incrementStepCounter();
551 suspended(DebugEvent.STEP_END);
553 case IPHPDebugEvent.STOPPED:
554 fThread.removeEventListeners();
556 fThreads = new IThread[0];
558 fDebugConnection.close();
562 // Dirty hack to check debugging mode (remote or local)
563 if (fProcess!=null) {
566 } catch (DebugException e) {
570 fDebugConnection = null;
571 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
579 public void handleProxyEvent(String ideKey, String initString, /*AbstractDebugConnection*/ XDebugConnection connection) {
580 System.out.println("* New Connection - XDebug.Target: "+ideKey);
581 setDebugConnection(connection);
583 XDebugProxy proxy=XDebugCorePlugin.getDefault().getXDebugProxy();
584 fDebugPort=proxy.getProxyPort();
585 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CHANGE));
587 fThread = new XDebugThread(this);
588 fThreads = new IThread[] {fThread};
591 } catch( DebugException e ){
596 private void setDebugConnection(XDebugConnection connection) {
597 if (connection != null) {
598 fDebugConnection = connection;
599 fDebugConnection.startListener();
604 * @return Returns the fDebugConnection.
606 public XDebugConnection getDebugConnection() {
607 return fDebugConnection;
610 public void addProcess(IProcess p) {
615 public void launchRemoved(ILaunch launch) {
619 * Notifies this listener that the specified launch
622 * @param launch the newly added launch
625 public void launchAdded(ILaunch launch){
629 * Notifies this listener that the specified launch
630 * has changed. For example, a process or debug target
631 * has been added to the launch.
633 * @param launch the changed launch
636 public void launchChanged(ILaunch launch) {
639 public IVariable[] getVariables(XDebugStackFrame frame, int level) throws DebugException {
640 IVariable[] variables = null;
642 DebugResponse response = fDebugConnection.contextGet(level, 0);
643 Node responseNode = response.getParentNode();
644 NodeList property = responseNode.getChildNodes();
646 DebugResponse responseGlobal = fDebugConnection.contextGet(level, 1);
647 Node responseGlobalNode = responseGlobal.getParentNode();
648 NodeList propertyGlobal = responseGlobalNode.getChildNodes();
650 variables = new IVariable[property.getLength() + propertyGlobal.getLength()];
651 // variables = new IVariable[property.getLength()]; // + propertyGlobal.getLength()];
653 int length = property.getLength();
654 for (int i = 0; i < length; i++) {
655 Node propertyNode = property.item(i);
656 XDebugVariable var=/*fDebugConnection.*/getVariableFromNode(frame,propertyNode);
660 int globalLength = propertyGlobal.getLength();
661 for (int k = 0; k < globalLength; k++) {
662 Node propertyGlobalNode = propertyGlobal.item(k);
663 XDebugVariable var=/*fDebugConnection.*/getVariableFromNode(frame,propertyGlobalNode);
664 variables[k + length]=var;
670 public XDebugVariable getVariableFromNode(XDebugStackFrame frame, Node property) {
671 String varFullName = PHPDebugUtils.getAttributeValue(property, "fullname");
672 String varName = PHPDebugUtils.getAttributeValue(property, "name");
673 String varEncoding = PHPDebugUtils.getAttributeValue(property, "encoding");
675 int varNumChildren = 0;
676 if (PHPDebugUtils.getAttributeValue(property, "numchildren").equals("")) {
679 varNumChildren = Integer.parseInt(PHPDebugUtils.getAttributeValue(property, "numchildren"));
682 String typeName = PHPDebugUtils.getAttributeValue(property,"type");
684 XDebugVariable variable = new XDebugVariable(frame,varFullName,varName,typeName);
685 variable.setEncoding(varEncoding);
686 variable.setNumChildren(varNumChildren);
687 XDebugAbstractValue val=null;
689 val = (XDebugAbstractValue) variable.getValue();
690 } catch (DebugException e1) {
691 // TODO Auto-generated catch block
692 e1.printStackTrace();
694 if (val.getType()!= XDebugAbstractValue.VALUETYPE_UNINITIALIZED) {
695 if (variable.hasChildren()) {
696 NodeList varNodes = property.getChildNodes();
697 val.renderValueString(""+varNodes.getLength());
698 IVariable[] variables = new IVariable[varNodes.getLength()];
699 for (int i = 0; i<varNodes.getLength(); i++) {
700 Node propertyNode = varNodes.item(i);
701 variables[i] = getVariableFromNode(frame, propertyNode);
703 val.setChildVariables(variables);
707 str=property.getFirstChild().getNodeValue();
708 } catch (NullPointerException e) {
711 if (variable.getEncoding().equals("base64")) {
713 str=new String(Base64.decode(str));
717 val.renderValueString(str);
720 String className=PHPDebugUtils.getAttributeValue(property,"classname");
721 if(!"".equals(className))
722 val.renderValueString(className);
728 protected IBreakpoint breakpointHit(Node node) {
729 Node child=node.getFirstChild();
730 if (child.getNodeName().equals("stack")) {
731 int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "lineno"));
732 String filename=PHPDebugUtils.getAttributeValue(child, "filename");
733 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
734 for (int i = 0; i < breakpoints.length; i++) {
735 IBreakpoint breakpoint = breakpoints[i];
736 if (supportsBreakpoint(breakpoint)) {
737 if (breakpoint instanceof ILineBreakpoint) {
738 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
740 if (breakpoint.isEnabled()) {
741 IMarker marker = breakpoint.getMarker();
742 if (marker != null) {
745 if (getProcess() == null) {
746 endfilename = marker.getResource().getFullPath().lastSegment();
748 endfilename = marker.getResource().getFullPath().toOSString();
751 if(PHPDebugUtils.unescapeString(filename).endsWith(endfilename) && (lineBreakpoint.getLineNumber() == lineNumber) ) {
756 } catch (CoreException e) {