04099c437471c2ab18d488f74efc5af3703fd660
[phpeclipse.git] /
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,
37                 IDebugEventSetListener {
38         // associated system process (VM)
39         private IProcess fProcess;
40
41         // containing launch object
42         private ILaunch fLaunch;
43
44         // debugPort
45         private int fDebugPort;
46
47         // program name
48         // private String fName;
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
59         private IThread[] fThreads;
60
61         // event dispatch job
62         // private EventDispatchJob fEventDispatch;
63
64         private DebugConnection fDebugConnection;
65
66         // private DebugResponse lastResponse;
67
68         /**
69          * Constructs a new debug target in the given launch for the associated PDA
70          * VM process.
71          * 
72          * @param launch
73          *            containing launch
74          * @param debugPort
75          *            port to read events from
76          * @exception CoreException
77          *                if unable to connect to host
78          */
79         public XDebugTarget(ILaunch launch, IProcess process, int debugPort)
80                         throws CoreException {
81                 super(null);
82                 fLaunch = launch;
83                 fProcess = process;
84                 fTarget = this;
85                 fDebugConnection = new DebugConnection(this, debugPort);
86                 fThread = new XDebugThread(this);
87                 fThreads = new IThread[] { fThread };
88                 DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(
89                                 this);
90                 DebugPlugin.getDefault().addDebugEventListener(this);
91         }
92
93         /*
94          * (non-Javadoc)
95          * 
96          * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
97          */
98         public IProcess getProcess() {
99                 return fProcess;
100         }
101
102         /*
103          * (non-Javadoc)
104          * 
105          * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
106          */
107         public IThread[] getThreads() throws DebugException {
108                 return fThreads;
109         }
110
111         /*
112          * (non-Javadoc)
113          * 
114          * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
115          */
116         public boolean hasThreads() throws DebugException {
117                 return (fThreads.length > 0);
118         }
119
120         /*
121          * (non-Javadoc)
122          * 
123          * @see org.eclipse.debug.core.model.IDebugTarget#getName()
124          */
125         public String getName() throws DebugException {
126                 return "PHP XDebug Client at localhost:" + fDebugPort;
127         }
128
129         /*
130          * (non-Javadoc)
131          * 
132          * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
133          */
134         public boolean supportsBreakpoint(IBreakpoint breakpoint) {
135                 if (breakpoint.getModelIdentifier().equals(
136                                 IXDebugConstants.ID_PHP_DEBUG_MODEL)) {
137                         return true;
138                 }
139                 return false;
140         }
141
142         /*
143          * (non-Javadoc)
144          * 
145          * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
146          */
147         public IDebugTarget getDebugTarget() {
148                 return this;
149         }
150
151         /*
152          * (non-Javadoc)
153          * 
154          * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
155          */
156         public ILaunch getLaunch() {
157                 return fLaunch;
158         }
159
160         /*
161          * (non-Javadoc)
162          * 
163          * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
164          */
165         public boolean canTerminate() {
166                 return getProcess().canTerminate();
167                 // return false;
168         }
169
170         /*
171          * (non-Javadoc)
172          * 
173          * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
174          */
175         public boolean isTerminated() {
176                 // return getProcess().isTerminated();
177                 return fTerminated;
178         }
179
180         /*
181          * (non-Javadoc)
182          * 
183          * @see org.eclipse.debug.core.model.ITerminate#terminate()
184          */
185         public void terminate() throws DebugException {
186                 fDebugConnection.sendRequest("stop");
187         }
188
189         /*
190          * (non-Javadoc)
191          * 
192          * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
193          */
194         public boolean canResume() {
195                 return !isTerminated() && isSuspended();
196         }
197
198         /*
199          * (non-Javadoc)
200          * 
201          * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
202          */
203         public boolean canSuspend() {
204                 return !isTerminated() && !isSuspended();
205         }
206
207         /*
208          * (non-Javadoc)
209          * 
210          * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
211          */
212         public boolean isSuspended() {
213                 return fSuspended;
214         }
215
216         /*
217          * (non-Javadoc)
218          * 
219          * @see org.eclipse.debug.core.model.ISuspendResume#resume()
220          */
221         public void resume() throws DebugException {
222                 fDebugConnection.sendRequest("run");
223         }
224
225         /**
226          * Notification the target has resumed for the given reason
227          * 
228          * @param detail
229          *            reason for the resume
230          */
231         private void resumed(int detail) {
232                 fSuspended = false;
233                 fThread.fireResumeEvent(detail);
234         }
235
236         /**
237          * Notification the target has suspended for the given reason
238          * 
239          * @param detail
240          *            reason for the suspend
241          */
242         public void suspended(int detail) {
243                 fSuspended = true;
244                 fThread.fireSuspendEvent(detail);
245         }
246
247         /*
248          * (non-Javadoc)
249          * 
250          * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
251          */
252         public void suspend() throws DebugException {
253         }
254
255         /*
256          * (non-Javadoc)
257          * 
258          * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
259          */
260         public void breakpointAdded(IBreakpoint breakpoint) {
261
262                 if (supportsBreakpoint(breakpoint)) {
263                         try {
264                                 if (breakpoint.isEnabled()) {
265                                         IMarker marker = breakpoint.getMarker();
266                                         if (marker != null) {
267                                                 try {
268                                                         String fileName = PHPDebugUtils.escapeString(marker
269                                                                         .getResource().getLocation().toString());
270                                                         String arg = "-t line -f file:///"
271                                                                         + fileName
272                                                                         + " -n "
273                                                                         + ((ILineBreakpoint) breakpoint)
274                                                                                         .getLineNumber();
275                                                         int id = fDebugConnection.sendRequest(
276                                                                         "breakpoint_set", arg);
277                                                         // set the marker Attribute to make later
278                                                         // idetification possible
279                                                         // TODO: make sure that attribute is set before
280                                                         // response from debugger is beeing prosessed
281                                                         marker.setAttribute(
282                                                                         XDebugLineBreakpoint.BREAKPOINT_ID, id);
283
284                                                 } catch (CoreException e) {
285                                                 }
286                                         }
287                                 }
288                         } catch (CoreException e) {
289
290                         }
291                 }
292         }
293
294         /*
295          * (non-Javadoc)
296          * 
297          * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint,
298          *      org.eclipse.core.resources.IMarkerDelta)
299          */
300         public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
301                 if (supportsBreakpoint(breakpoint)) {
302                         try {
303                                 int id = ((XDebugLineBreakpoint) breakpoint).getID();
304                                 if (id > 0)
305                                         fDebugConnection.sendRequest("breakpoint_remove", "-d "
306                                                         + id);
307                         } catch (CoreException e) {
308                         }
309                 }
310         }
311
312         /*
313          * (non-Javadoc)
314          * 
315          * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint,
316          *      org.eclipse.core.resources.IMarkerDelta)
317          */
318         public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
319                 // if (supportsBreakpoint(breakpoint)) {
320                 // try {
321                 // if (breakpoint.isEnabled()) {
322                 // breakpointAdded(breakpoint);
323                 // } else {
324                 // breakpointRemoved(breakpoint, null);
325                 // }
326                 // } catch (CoreException e) {
327                 // }
328                 // }
329         }
330
331         /*
332          * (non-Javadoc)
333          * 
334          * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
335          */
336         public boolean canDisconnect() {
337                 return false;
338         }
339
340         /*
341          * (non-Javadoc)
342          * 
343          * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
344          */
345         public void disconnect() throws DebugException {
346         }
347
348         /*
349          * (non-Javadoc)
350          * 
351          * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
352          */
353         public boolean isDisconnected() {
354                 return false;
355         }
356
357         /*
358          * (non-Javadoc)
359          * 
360          * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
361          */
362         public boolean supportsStorageRetrieval() {
363                 return false;
364         }
365
366         /*
367          * (non-Javadoc)
368          * 
369          * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long,
370          *      long)
371          */
372         public IMemoryBlock getMemoryBlock(long startAddress, long length)
373                         throws DebugException {
374                 return null;
375         }
376
377         /**
378          * Notification we have connected to the PHP debugger and it has started.
379          * Resume the the debugger.
380          */
381         public void started() {
382
383                 fThread.setBreakpoints(null);
384                 fThread.setStepping(false);
385
386                 installDeferredBreakpoints();
387                 try {
388                         resume();
389                         // step();
390                 } catch (DebugException e) {
391                 }
392         }
393
394         /**
395          * Install breakpoints that are already registered with the breakpoint
396          * manager.
397          */
398         private void installDeferredBreakpoints() {
399                 IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
400                 for (int i = 0; i < breakpoints.length; i++) {
401                         breakpointAdded(breakpoints[i]);
402                 }
403         }
404
405         /**
406          * Called when this debug target terminates.
407          */
408         public void terminated() {
409                 fTerminated = true;
410                 fSuspended = false;
411                 XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this);
412                 fireTerminateEvent();
413                 DebugPlugin.getDefault().removeDebugEventListener(this);
414                 fThread.removeEventListeners();
415         }
416
417         /**
418          * Returns the current stack frames in the target.
419          * 
420          * @return the current stack frames in the target
421          * @throws DebugException
422          *             if unable to perform the request
423          */
424         protected IStackFrame[] getStackFrames() throws DebugException {
425                 int id = fDebugConnection.sendRequest("stack_get");
426                 DebugResponse lastResponse = fDebugConnection.waitforTransID(id);
427                 if (lastResponse.isError())
428                         return new IStackFrame[0];
429                 Node response = lastResponse.getParentNode();
430                 NodeList frames = response.getChildNodes();
431                 IStackFrame[] theFrames = new IStackFrame[frames.getLength()];
432                 for (int i = 0; i < frames.getLength(); i++) {
433                         Node stackNode = frames.item(i);
434                         theFrames[i] = new XDebugStackFrame(fThread, stackNode, i);
435                 }
436                 return theFrames;
437         }
438
439         /**
440          * Single step the interpreter.
441          * 
442          * @throws DebugException
443          *             if the request fails
444          */
445         protected void step_over() throws DebugException {
446                 fThread.setStepping(true);
447                 resumed(DebugEvent.STEP_OVER);
448                 fDebugConnection.sendRequest("step_over");
449         }
450
451         /**
452          * Single step the interpreter.
453          * 
454          * @throws DebugException
455          *             if the request fails
456          */
457         protected void step_into() throws DebugException {
458                 fThread.setStepping(true);
459                 resumed(DebugEvent.STEP_INTO);
460                 fDebugConnection.sendRequest("step_into");
461         }
462
463         /**
464          * Single step the interpreter.
465          * 
466          * @throws DebugException
467          *             if the request fails
468          */
469         protected void step_out() throws DebugException {
470                 fThread.setStepping(true);
471                 resumed(DebugEvent.STEP_RETURN);
472                 fDebugConnection.sendRequest("step_out");
473         }
474
475         /**
476          * Returns the current value of the given variable.
477          * 
478          * @param variable
479          * @return variable value
480          * @throws DebugException
481          *             if the request fails
482          */
483         protected IValue getVariableValue(XDebugVariable variable)
484                         throws DebugException {
485                 // synchronized (fDebugSocket) {
486                 // fDebugConnection.sendRequest("var","" +
487                 // variable.getStackFrame().getIdentifier() + " " + variable.getName());
488                 // try {
489                 // String value = fDebugReader.readLine();
490                 // //return new XDebugValue(this, value);
491                 //                              
492                 // } catch (IOException e) {
493                 // abort(MessageFormat.format("Unable to retrieve value for variable
494                 // {0}", new String[]{variable.getName()}), e);
495                 // }
496                 // }
497                 return null;
498         }
499
500         /**
501          * Returns the values on the data stack (top down)
502          * 
503          * @return the values on the data stack (top down)
504          */
505         public IValue[] getDataStack() throws DebugException {
506                 // synchronized (fDebugSocket) {
507                 // fDebugConnection.sendRequest ("data");
508                 // try {
509                 // String valueString = fDebugReader.readLine();
510                 // if (valueString != null && valueString.length() > 0) {
511                 // String[] values = valueString.split("\\|");
512                 // IValue[] theValues = new IValue[values.length];
513                 // for (int i = 0; i < values.length; i++) {
514                 // String value = values[values.length - i - 1];
515                 // // theValues[i] = new XDebugValue(this, value);
516                 // }
517                 // return theValues;
518                 // }
519                 // } catch (IOException e) {
520                 // abort("Unable to retrieve data stack", e);
521                 // }
522                 // }
523                 return new IValue[0];
524         }
525
526         public boolean setVarValue(String name, String value) {
527                 int id = -1;
528                 String str = Base64.encodeBytes(value.getBytes());
529                 int len = str.length();
530
531                 try {
532                         id = fDebugConnection.sendRequest("property_set", "-n " + name
533                                         + " -l " + len + " -- " + str);
534                 } catch (DebugException e) {
535                         // TODO Auto-generated catch block
536                         e.printStackTrace();
537                 }
538                 DebugResponse dr = getResponse(id);
539                 if ((dr.getAttributeValue("success")).equals("1"))
540                         return true;
541
542                 return false;
543         }
544
545         public DebugResponse getResponse(int id) {
546                 return fDebugConnection.waitforTransID(id);
547         }
548
549         /**
550          * Sends a request to the Debugengine and waits for an OK.
551          * 
552          * @param command
553          *            debug command
554          * @throws DebugException
555          *             if the request fails
556          */
557
558         public int sendRequest(String command) throws DebugException {
559                 return fDebugConnection.sendRequest(command, "");
560         }
561
562         /**
563          * Sends a request to the Debugengine and waits for an OK.
564          * 
565          * @param command
566          *            debug command
567          * @arguments arguments for the command
568          * @throws DebugException
569          *             if the request fails
570          */
571
572         public int sendRequest(String command, String arguments)
573                         throws DebugException {
574                 return fDebugConnection.sendRequest(command, arguments);
575         }
576
577         /**
578          * Notification a breakpoint was encountered. Determine which breakpoint was
579          * hit and fire a suspend event.
580          * 
581          * @param event
582          *            debug event
583          */
584         public void breakpointHit(Node node) {
585                 // determine which breakpoint was hit, and set the thread's breakpoint
586                 Node child = node.getFirstChild();
587                 if (child.getNodeName().equals("stack")) {
588                         int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(
589                                         child, "lineno"));
590                         String filename = PHPDebugUtils
591                                         .getAttributeValue(child, "filename").substring(8); // remove
592                                                                                                                                                 // file:///
593                         IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints();
594                         for (int i = 0; i < breakpoints.length; i++) {
595                                 IBreakpoint breakpoint = breakpoints[i];
596                                 if (supportsBreakpoint(breakpoint)) {
597                                         if (breakpoint instanceof ILineBreakpoint) {
598                                                 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
599                                                 try {
600                                                         if (breakpoint.isEnabled()) {
601                                                                 IMarker marker = breakpoint.getMarker();
602                                                                 if (marker != null) {
603
604                                                                         String name = marker.getResource()
605                                                                                         .getLocation().toOSString();
606                                                                         if (name.equals(PHPDebugUtils
607                                                                                         .unescapeString(filename))
608                                                                                         && (lineBreakpoint.getLineNumber() == lineNumber)) {
609                                                                                 fThread
610                                                                                                 .setBreakpoints(new IBreakpoint[] { breakpoint });
611                                                                                 break;
612                                                                         }
613                                                                 }
614
615                                                         }
616                                                 } catch (CoreException e) {
617                                                 }
618                                         }
619                                 }
620                         }
621                 }
622                 suspended(DebugEvent.BREAKPOINT);
623         }
624
625         public void handleDebugEvents(DebugEvent[] events) {
626                 for (int i = 0; i < events.length; i++) {
627                         DebugEvent event = events[i];
628                         if ((event.getKind() == DebugEvent.CREATE)
629                                         && (event.getSource() instanceof XDebugElement)) {
630                                 if (((XDebugElement) event.getSource()).getModelIdentifier() == IXDebugConstants.ID_PHP_DEBUG_MODEL) {
631                                         if (event.getKind() == DebugEvent.CREATE)
632                                                 started();
633                                 }
634                         }
635                 }
636
637         }
638 }