4 package net.sourceforge.phpeclipse.xdebug.php.model;
6 import net.sourceforge.phpeclipse.xdebug.core.Base64;
7 import net.sourceforge.phpeclipse.xdebug.core.DebugConnection;
8 import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils;
9 import net.sourceforge.phpeclipse.xdebug.core.XDebugCorePlugin;
10 import net.sourceforge.phpeclipse.xdebug.core.DebugConnection.DebugResponse;
11 import net.sourceforge.phpeclipse.xdebug.php.launching.IXDebugConstants;
13 import org.eclipse.core.resources.IMarker;
14 import org.eclipse.core.resources.IMarkerDelta;
15 import org.eclipse.core.runtime.CoreException;
16 import org.eclipse.debug.core.DebugEvent;
17 import org.eclipse.debug.core.DebugException;
18 import org.eclipse.debug.core.DebugPlugin;
19 import org.eclipse.debug.core.IDebugEventSetListener;
20 import org.eclipse.debug.core.ILaunch;
21 import org.eclipse.debug.core.model.IBreakpoint;
22 import org.eclipse.debug.core.model.IDebugTarget;
23 import org.eclipse.debug.core.model.ILineBreakpoint;
24 import org.eclipse.debug.core.model.IMemoryBlock;
25 import org.eclipse.debug.core.model.IProcess;
26 import org.eclipse.debug.core.model.IStackFrame;
27 import org.eclipse.debug.core.model.IThread;
28 import org.eclipse.debug.core.model.IValue;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NodeList;
36 public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugEventSetListener{
37 // associated system process (VM)
38 private IProcess fProcess;
40 // containing launch object
41 private ILaunch fLaunch;
44 private int fDebugPort;
47 // private String fName;
51 private boolean fSuspended = true;
54 private boolean fTerminated = false;
57 private XDebugThread fThread;
58 private IThread[] fThreads;
61 // private EventDispatchJob fEventDispatch;
64 private DebugConnection fDebugConnection;
65 // private DebugResponse lastResponse;
70 * Constructs a new debug target in the given launch for the
71 * associated PDA VM process.
73 * @param launch containing launch
74 * @param debugPort port to read events from
75 * @exception CoreException if unable to connect to host
77 public XDebugTarget(ILaunch launch, IProcess process, int debugPort) throws CoreException {
82 fDebugConnection= new DebugConnection(this,debugPort);
83 fThread = new XDebugThread(this);
84 fThreads = new IThread[] {fThread};
85 DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
86 DebugPlugin.getDefault().addDebugEventListener(this);
89 * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
91 public IProcess getProcess() {
95 * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
97 public IThread[] getThreads() throws DebugException {
101 * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
103 public boolean hasThreads() throws DebugException {
104 return (fThreads.length>0);
107 * @see org.eclipse.debug.core.model.IDebugTarget#getName()
109 public String getName() throws DebugException {
110 return "PHP XDebug Client at localhost:" + fDebugPort;
113 * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
115 public boolean supportsBreakpoint(IBreakpoint breakpoint) {
116 if (breakpoint.getModelIdentifier().equals(IXDebugConstants.ID_PHP_DEBUG_MODEL)) {
122 * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
124 public IDebugTarget getDebugTarget() {
128 * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
130 public ILaunch getLaunch() {
134 * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
136 public boolean canTerminate() {
137 return getProcess().canTerminate();
141 * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
143 public boolean isTerminated() {
144 // return getProcess().isTerminated();
148 * @see org.eclipse.debug.core.model.ITerminate#terminate()
150 public void terminate() throws DebugException {
151 fDebugConnection.sendRequest ("stop");
154 * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
156 public boolean canResume() {
157 return !isTerminated() && isSuspended();
160 * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
162 public boolean canSuspend() {
163 return !isTerminated() && !isSuspended();
166 * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
168 public boolean isSuspended() {
172 * @see org.eclipse.debug.core.model.ISuspendResume#resume()
174 public void resume() throws DebugException {
175 fDebugConnection.sendRequest("run");
179 * Notification the target has resumed for the given reason
181 * @param detail reason for the resume
183 private void resumed(int detail) {
185 fThread.fireResumeEvent(detail);
189 * Notification the target has suspended for the given reason
191 * @param detail reason for the suspend
193 public void suspended(int detail) {
195 fThread.fireSuspendEvent(detail);
199 * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
201 public void suspend() throws DebugException {
204 * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
206 public void breakpointAdded(IBreakpoint breakpoint) {
208 if (supportsBreakpoint(breakpoint)) {
210 if (breakpoint.isEnabled()) {
211 IMarker marker = breakpoint.getMarker();
212 if (marker != null) {
214 String fileName = PHPDebugUtils.escapeString(marker.getResource().getLocation().toString());
215 String arg="-t line -f file:///"+fileName+" -n "+((ILineBreakpoint)breakpoint).getLineNumber();
216 int id =fDebugConnection.sendRequest("breakpoint_set",arg);
217 // set the marker Attribute to make later idetification possible
218 // TODO: make sure that attribute is set before response from debugger is beeing prosessed
219 marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,id);
221 } catch (CoreException e) {
225 } catch (CoreException e) {
231 * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
233 public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
234 if (supportsBreakpoint(breakpoint)) {
236 int id =((XDebugLineBreakpoint)breakpoint).getID();
238 fDebugConnection.sendRequest("breakpoint_remove","-d "+id);
239 } catch (CoreException e) {
244 * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
246 public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
247 // if (supportsBreakpoint(breakpoint)) {
249 // if (breakpoint.isEnabled()) {
250 // breakpointAdded(breakpoint);
252 // breakpointRemoved(breakpoint, null);
254 // } catch (CoreException e) {
259 * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
261 public boolean canDisconnect() {
265 * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
267 public void disconnect() throws DebugException {
270 * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
272 public boolean isDisconnected() {
276 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
278 public boolean supportsStorageRetrieval() {
282 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
284 public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
289 * Notification we have connected to the PHP debugger and it has started.
290 * Resume the the debugger.
292 public void started() {
294 fThread.setBreakpoints(null);
295 fThread.setStepping(false);
297 installDeferredBreakpoints();
301 } catch (DebugException e) {
306 * Install breakpoints that are already registered with the breakpoint
309 private void installDeferredBreakpoints() {
310 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
311 for (int i = 0; i < breakpoints.length; i++) {
312 breakpointAdded(breakpoints[i]);
317 * Called when this debug target terminates.
319 public void terminated() {
322 XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this);
323 fireTerminateEvent();
324 DebugPlugin.getDefault().removeDebugEventListener(this);
325 fThread.removeEventListeners();
329 * Returns the current stack frames in the target.
331 * @return the current stack frames in the target
332 * @throws DebugException if unable to perform the request
334 protected IStackFrame[] getStackFrames() throws DebugException {
335 int id=fDebugConnection.sendRequest("stack_get");
336 DebugResponse lastResponse=fDebugConnection.waitforTransID(id);
337 if (lastResponse.isError())
338 return new IStackFrame[0];
339 Node response = lastResponse.getParentNode();
340 NodeList frames = response.getChildNodes();
341 IStackFrame[] theFrames = new IStackFrame[frames.getLength()];
342 for (int i = 0; i < frames.getLength(); i++) {
343 Node stackNode = frames.item(i);
344 theFrames[i] = new XDebugStackFrame(fThread, stackNode, i);
350 * Single step the interpreter.
352 * @throws DebugException if the request fails
354 protected void step_over() throws DebugException {
355 fThread.setStepping(true);
356 resumed(DebugEvent.STEP_OVER);
357 fDebugConnection.sendRequest("step_over");
361 * Single step the interpreter.
363 * @throws DebugException if the request fails
365 protected void step_into() throws DebugException {
366 fThread.setStepping(true);
367 resumed(DebugEvent.STEP_INTO);
368 fDebugConnection.sendRequest("step_into");
372 * Single step the interpreter.
374 * @throws DebugException if the request fails
376 protected void step_out() throws DebugException {
377 fThread.setStepping(true);
378 resumed(DebugEvent.STEP_RETURN);
379 fDebugConnection.sendRequest("step_out");
385 * Returns the current value of the given variable.
388 * @return variable value
389 * @throws DebugException if the request fails
391 protected IValue getVariableValue(XDebugVariable variable) throws DebugException {
392 // synchronized (fDebugSocket) {
393 // fDebugConnection.sendRequest("var","" + variable.getStackFrame().getIdentifier() + " " + variable.getName());
395 // String value = fDebugReader.readLine();
396 // //return new XDebugValue(this, value);
398 // } catch (IOException e) {
399 // abort(MessageFormat.format("Unable to retrieve value for variable {0}", new String[]{variable.getName()}), e);
406 * Returns the values on the data stack (top down)
408 * @return the values on the data stack (top down)
410 public IValue[] getDataStack() throws DebugException {
411 // synchronized (fDebugSocket) {
412 // fDebugConnection.sendRequest ("data");
414 // String valueString = fDebugReader.readLine();
415 // if (valueString != null && valueString.length() > 0) {
416 // String[] values = valueString.split("\\|");
417 // IValue[] theValues = new IValue[values.length];
418 // for (int i = 0; i < values.length; i++) {
419 // String value = values[values.length - i - 1];
420 //// theValues[i] = new XDebugValue(this, value);
424 // } catch (IOException e) {
425 // abort("Unable to retrieve data stack", e);
428 return new IValue[0];
431 public boolean setVarValue(String name, String value) {
433 String str=Base64.encodeBytes(value.getBytes());
434 int len=str.length();
437 id =fDebugConnection.sendRequest("property_set","-n "+name+" -l "+len + " -- "+str);
438 } catch (DebugException e) {
439 // TODO Auto-generated catch block
442 DebugResponse dr=getResponse(id);
443 if ((dr.getAttributeValue("success")).equals("1"))
449 public DebugResponse getResponse(int id) {
450 return fDebugConnection.waitforTransID(id);
454 * Sends a request to the Debugengine and waits for an OK.
456 * @param command debug command
457 * @throws DebugException if the request fails
460 public int sendRequest(String command) throws DebugException {
461 return fDebugConnection.sendRequest(command,"");
466 * Sends a request to the Debugengine and waits for an OK.
468 * @param command debug command
469 * @arguments arguments for the command
470 * @throws DebugException if the request fails
473 public int sendRequest(String command, String arguments) throws DebugException {
474 return fDebugConnection.sendRequest(command,arguments);
478 * Notification a breakpoint was encountered. Determine
479 * which breakpoint was hit and fire a suspend event.
481 * @param event debug event
483 public void breakpointHit(Node node) {
484 // determine which breakpoint was hit, and set the thread's breakpoint
485 Node child=node.getFirstChild();
486 if (child.getNodeName().equals("stack")) {
487 int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "lineno"));
488 String filename=PHPDebugUtils.getAttributeValue(child, "filename").substring(8); // remove file:///
489 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
490 for (int i = 0; i < breakpoints.length; i++) {
491 IBreakpoint breakpoint = breakpoints[i];
492 if (supportsBreakpoint(breakpoint)) {
493 if (breakpoint instanceof ILineBreakpoint) {
494 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
496 if (breakpoint.isEnabled()) {
497 IMarker marker = breakpoint.getMarker();
498 if (marker != null) {
500 String name = marker.getResource().getLocation().toOSString();
501 if(name.equals(PHPDebugUtils.unescapeString(filename)) && (lineBreakpoint.getLineNumber() == lineNumber)) {
502 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
508 } catch (CoreException e) {
514 suspended(DebugEvent.BREAKPOINT);
516 public void handleDebugEvents(DebugEvent[] events) {
517 for (int i=0;i<events.length;i++) {
518 DebugEvent event=events[i];
519 if((event.getKind()==DebugEvent.CREATE) && (event.getSource() instanceof XDebugElement)) {
520 if(((XDebugElement)event.getSource()).getModelIdentifier()==IXDebugConstants.ID_PHP_DEBUG_MODEL) {
521 if (event.getKind()==DebugEvent.CREATE)