Initial implementation of the new Debug Plugin
[phpeclipse.git] / net.sourceforge.phpeclipse.xdebug.core / src / net / sourceforge / phpeclipse / xdebug / php / model / XDebugTarget.java
1 /**
2  * 
3  */
4 package net.sourceforge.phpeclipse.xdebug.php.model;
5
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;
12
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;
31
32 /**
33  * @author Christian
34  *
35  */
36 public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugEventSetListener{
37         // associated system process (VM)
38         private IProcess fProcess;
39         
40         // containing launch object
41         private ILaunch fLaunch;
42         
43         // debugPort
44         private int fDebugPort;
45         
46         // program name
47 //      private String fName;
48         
49         
50         // suspend state
51         private boolean fSuspended = true;
52         
53         // terminated state
54         private boolean fTerminated = false;
55         
56         // threads
57         private XDebugThread fThread;
58         private IThread[] fThreads;
59         
60         // event dispatch job
61 //      private EventDispatchJob fEventDispatch;
62         
63
64         private DebugConnection fDebugConnection;
65 //      private DebugResponse lastResponse;
66
67
68         
69         /**
70          * Constructs a new debug target in the given launch for the 
71          * associated PDA VM process.
72          * 
73          * @param launch containing launch
74          * @param debugPort port to read events from
75          * @exception CoreException if unable to connect to host
76          */
77         public XDebugTarget(ILaunch launch, IProcess process, int debugPort) throws CoreException {
78                 super(null);
79                 fLaunch = launch;
80                 fProcess = process;
81                 fTarget = this;
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);
87         }
88         /* (non-Javadoc)
89          * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
90          */
91         public IProcess getProcess() {
92                 return fProcess;
93         }
94         /* (non-Javadoc)
95          * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
96          */
97         public IThread[] getThreads() throws DebugException {
98                 return fThreads;
99         }
100         /* (non-Javadoc)
101          * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
102          */
103         public boolean hasThreads() throws DebugException {
104                 return (fThreads.length>0);
105         }
106         /* (non-Javadoc)
107          * @see org.eclipse.debug.core.model.IDebugTarget#getName()
108          */
109         public String getName() throws DebugException {
110                 return "PHP XDebug Client at localhost:" + fDebugPort;
111         }
112         /* (non-Javadoc)
113          * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
114          */
115         public boolean supportsBreakpoint(IBreakpoint breakpoint) {
116                 if (breakpoint.getModelIdentifier().equals(IXDebugConstants.ID_PHP_DEBUG_MODEL)) {
117                         return true;
118                 }
119                 return false;
120         }
121         /* (non-Javadoc)
122          * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
123          */
124         public IDebugTarget getDebugTarget() {
125                 return this;
126         }
127         /* (non-Javadoc)
128          * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
129          */
130         public ILaunch getLaunch() {
131                 return fLaunch;
132         }
133         /* (non-Javadoc)
134          * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
135          */
136         public boolean canTerminate() {
137                 return getProcess().canTerminate();
138 //              return false;
139         }
140         /* (non-Javadoc)
141          * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
142          */
143         public boolean isTerminated() {
144 //              return getProcess().isTerminated();
145                 return fTerminated;
146         }
147         /* (non-Javadoc)
148          * @see org.eclipse.debug.core.model.ITerminate#terminate()
149          */
150         public void terminate() throws DebugException {
151                 fDebugConnection.sendRequest ("stop");
152         }
153         /* (non-Javadoc)
154          * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
155          */
156         public boolean canResume() {
157                 return !isTerminated() && isSuspended();
158         }
159         /* (non-Javadoc)
160          * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
161          */
162         public boolean canSuspend() {
163                 return !isTerminated() && !isSuspended();
164         }
165         /* (non-Javadoc)
166          * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
167          */
168         public boolean isSuspended() {
169                 return fSuspended;
170         }
171         /* (non-Javadoc)
172          * @see org.eclipse.debug.core.model.ISuspendResume#resume()
173          */
174         public void resume() throws DebugException {
175                 fDebugConnection.sendRequest("run");
176         }
177         
178         /**
179          * Notification the target has resumed for the given reason
180          * 
181          * @param detail reason for the resume
182          */
183         private void resumed(int detail) {
184                 fSuspended = false;
185                 fThread.fireResumeEvent(detail);
186         }
187         
188         /**
189          * Notification the target has suspended for the given reason
190          * 
191          * @param detail reason for the suspend
192          */
193         public void suspended(int detail) {
194                 fSuspended = true;
195                 fThread.fireSuspendEvent(detail);
196         }       
197         
198         /* (non-Javadoc)
199          * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
200          */
201         public void suspend() throws DebugException {
202         }
203         /* (non-Javadoc)
204          * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
205          */
206         public void breakpointAdded(IBreakpoint breakpoint) {
207                 
208                 if (supportsBreakpoint(breakpoint)) {
209                         try {
210                                 if (breakpoint.isEnabled()) {
211                                         IMarker marker = breakpoint.getMarker();
212                                         if (marker != null) {
213                                                 try {
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);
220                                                         
221                                                 } catch (CoreException e) {
222                                                 }
223                                         }
224                                 }
225                         } catch (CoreException e) {
226
227                         }
228                 }
229         }
230         /* (non-Javadoc)
231          * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
232          */
233         public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
234                 if (supportsBreakpoint(breakpoint)) {
235                         try {
236                                 int id =((XDebugLineBreakpoint)breakpoint).getID();
237                                 if (id >0)
238                                         fDebugConnection.sendRequest("breakpoint_remove","-d "+id);
239                         } catch (CoreException e) {
240                         }
241                 }
242         }
243         /* (non-Javadoc)
244          * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
245          */
246         public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
247 //              if (supportsBreakpoint(breakpoint)) {
248 //                      try {
249 //                              if (breakpoint.isEnabled()) {
250 //                                      breakpointAdded(breakpoint);
251 //                              } else {
252 //                                      breakpointRemoved(breakpoint, null);
253 //                              }
254 //                      } catch (CoreException e) {
255 //                      }
256 //              }
257         }
258         /* (non-Javadoc)
259          * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
260          */
261         public boolean canDisconnect() {
262                 return false;
263         }
264         /* (non-Javadoc)
265          * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
266          */
267         public void disconnect() throws DebugException {
268         }
269         /* (non-Javadoc)
270          * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
271          */
272         public boolean isDisconnected() {
273                 return false;
274         }
275         /* (non-Javadoc)
276          * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
277          */
278         public boolean supportsStorageRetrieval() {
279                 return false;
280         }
281         /* (non-Javadoc)
282          * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
283          */
284         public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
285                 return null;
286         }
287
288         /**
289          * Notification we have connected to the PHP debugger and it has started.
290          * Resume the the debugger.
291          */
292         public void started() {
293
294                 fThread.setBreakpoints(null);
295                 fThread.setStepping(false);
296
297                 installDeferredBreakpoints();
298                 try {
299                         resume();
300 //                      step();
301                 } catch (DebugException e) {
302                 }
303         }
304         
305         /**
306          * Install breakpoints that are already registered with the breakpoint
307          * manager.
308          */
309         private void installDeferredBreakpoints() {
310                 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
311                 for (int i = 0; i < breakpoints.length; i++) {
312                         breakpointAdded(breakpoints[i]);
313                 }
314         }
315         
316         /**
317          * Called when this debug target terminates.
318          */
319         public void terminated() {
320                 fTerminated = true;
321                 fSuspended = false;
322                 XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this);
323                 fireTerminateEvent();
324                 DebugPlugin.getDefault().removeDebugEventListener(this);
325                 fThread.removeEventListeners();
326         }
327         
328         /**
329          * Returns the current stack frames in the target.
330          * 
331          * @return the current stack frames in the target
332          * @throws DebugException if unable to perform the request
333          */
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);
345                 }
346                 return theFrames;
347         }
348         
349         /**
350          * Single step the interpreter.
351          * 
352          * @throws DebugException if the request fails
353          */
354         protected void step_over() throws DebugException {
355                 fThread.setStepping(true);
356                 resumed(DebugEvent.STEP_OVER);
357                 fDebugConnection.sendRequest("step_over");
358         }
359         
360         /**
361          * Single step the interpreter.
362          * 
363          * @throws DebugException if the request fails
364          */
365         protected void step_into() throws DebugException {
366                 fThread.setStepping(true);
367                 resumed(DebugEvent.STEP_INTO);
368                 fDebugConnection.sendRequest("step_into");
369         }
370         
371         /**
372          * Single step the interpreter.
373          * 
374          * @throws DebugException if the request fails
375          */
376         protected void step_out() throws DebugException {
377                 fThread.setStepping(true);
378                 resumed(DebugEvent.STEP_RETURN);
379                 fDebugConnection.sendRequest("step_out");
380         }
381
382
383         
384         /**
385          * Returns the current value of the given variable.
386          * 
387          * @param variable
388          * @return variable value
389          * @throws DebugException if the request fails
390          */
391         protected IValue getVariableValue(XDebugVariable variable) throws DebugException {
392 //              synchronized (fDebugSocket) {
393 //                      fDebugConnection.sendRequest("var","" + variable.getStackFrame().getIdentifier() + " " + variable.getName());
394 //                      try {
395 //                              String value = fDebugReader.readLine();
396 //                              //return new XDebugValue(this, value);
397 //                              
398 //                      } catch (IOException e) {
399 //                              abort(MessageFormat.format("Unable to retrieve value for variable {0}", new String[]{variable.getName()}), e);
400 //                      }
401 //              }
402                 return null;
403         }
404         
405         /**
406          * Returns the values on the data stack (top down)
407          * 
408          * @return the values on the data stack (top down)
409          */
410         public IValue[] getDataStack() throws DebugException {
411 //              synchronized (fDebugSocket) {
412 //                      fDebugConnection.sendRequest ("data");
413 //                      try {
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);
421 //                                      }
422 //                                      return theValues;
423 //                              }
424 //                      } catch (IOException e) {
425 //                              abort("Unable to retrieve data stack", e);
426 //                      }
427 //              }
428                 return new IValue[0];           
429         }
430         
431         public boolean setVarValue(String name, String value) {
432                 int id=-1;
433                 String str=Base64.encodeBytes(value.getBytes());
434                 int len=str.length();
435
436                 try {
437                         id =fDebugConnection.sendRequest("property_set","-n "+name+" -l "+len + " -- "+str);
438                 } catch (DebugException e) {
439                         // TODO Auto-generated catch block
440                         e.printStackTrace();
441                 }
442                 DebugResponse dr=getResponse(id);
443                 if ((dr.getAttributeValue("success")).equals("1"))
444                         return true;
445                 
446                 return false;
447         }
448         
449         public DebugResponse getResponse(int id) {
450                 return fDebugConnection.waitforTransID(id);
451         }
452
453         /**
454          * Sends a request to the Debugengine and waits for an OK.
455          * 
456          * @param command debug command
457          * @throws DebugException if the request fails
458          */
459         
460         public int sendRequest(String command) throws DebugException {  
461                 return fDebugConnection.sendRequest(command,"");
462         }
463
464         
465         /**
466          * Sends a request to the Debugengine and waits for an OK.
467          * 
468          * @param command debug command
469          * @arguments arguments for the command
470          * @throws DebugException if the request fails
471          */
472         
473         public int sendRequest(String command, String arguments) throws DebugException {
474                 return fDebugConnection.sendRequest(command,arguments);
475         }
476         
477         /**
478          * Notification a breakpoint was encountered. Determine
479          * which breakpoint was hit and fire a suspend event.
480          * 
481          * @param event debug event
482          */
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;
495                                                 try {                                           
496                                                         if (breakpoint.isEnabled()) {
497                                                                 IMarker marker = breakpoint.getMarker();
498                                                                 if (marker != null) {
499                                                                         
500                                                                         String name = marker.getResource().getLocation().toOSString();
501                                                                         if(name.equals(PHPDebugUtils.unescapeString(filename)) && (lineBreakpoint.getLineNumber() == lineNumber)) {
502                                                                                 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
503                                                                                 break;
504                                                                         }
505                                                                 }
506                                                         
507                                                         }
508                                                 } catch (CoreException e) {
509                                                 }
510                                         }
511                                 }
512                         }
513                 }
514                 suspended(DebugEvent.BREAKPOINT);
515         }
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)
522                                                 started();
523                                 }
524                         }
525                 }
526                 
527         }       
528 }