995df98d11fcbb0742b5b03422a832917c520d5a
[phpeclipse.git] / net.sourceforge.phpeclipse.xdebug.core / src / net / sourceforge / phpeclipse / xdebug / core / XDebugTarget.java
1 /*
2  * Created on 23.11.2004
3  *
4  * TODO To change the template for this generated file go to
5  * Window - Preferences - Java - Code Style - Code Templates
6  */
7 package net.sourceforge.phpeclipse.xdebug.core;
8
9 import java.io.BufferedReader;
10 import java.io.IOException;
11 import java.io.InputStreamReader;
12 import java.net.Socket;
13 import java.net.ServerSocket;
14 import java.net.UnknownHostException;
15 import java.text.MessageFormat;
16 import org.w3c.dom.*;
17 import javax.xml.parsers.*;
18 import javax.xml.parsers.DocumentBuilder;
19 import org.xml.sax.SAXException;
20 import java.io.StringBufferInputStream;
21 import java.io.OutputStreamWriter;
22
23 import org.eclipse.core.resources.IMarkerDelta;
24 import org.eclipse.core.runtime.CoreException;
25 import org.eclipse.core.runtime.IProgressMonitor;
26 import org.eclipse.core.runtime.IStatus;
27 import org.eclipse.core.runtime.Status;
28 import org.eclipse.core.runtime.jobs.Job;
29 import org.eclipse.debug.core.DebugEvent;
30 import org.eclipse.debug.core.DebugException;
31 import org.eclipse.debug.core.DebugPlugin;
32 import org.eclipse.debug.core.ILaunch;
33 import org.eclipse.debug.core.model.IBreakpoint;
34 import org.eclipse.debug.core.model.IDebugTarget;
35 import org.eclipse.debug.core.model.ILineBreakpoint;
36 import org.eclipse.debug.core.model.IMemoryBlock;
37 import org.eclipse.debug.core.model.IProcess;
38 import org.eclipse.debug.core.model.IStackFrame;
39 import org.eclipse.debug.core.model.IThread;
40 import org.eclipse.debug.core.model.IValue;
41 import net.sourceforge.phpdt.internal.debug.core.PHPDebugCorePlugin;
42
43 /**
44  * @author Axel
45  *
46  * TODO To change the template for this generated type comment go to
47  * Window - Preferences - Java - Code Style - Code Templates
48  */
49 public class XDebugTarget  extends XDebugElement implements IDebugTarget {
50         
51         // associated system process (VM)
52         private IProcess fProcess;
53         
54         // containing launch object
55         private ILaunch fLaunch;
56         
57         // program name
58         private String fName;
59         
60         // sockets to communicate with XDebug
61         private ServerSocket fDebugServerSocket;
62         private Socket fDebugSocket;
63         private OutputStreamWriter fDebugWriter;
64         private BufferedReader fDebugReader;
65         
66         // suspend state
67         private boolean fSuspended = true;
68         
69         // terminated state
70         private boolean fTerminated = false;
71         
72         // threads
73         private XDebugThread fThread;
74         private IThread[] fThreads;
75         
76         // event dispatch job
77         private EventDispatchJob fEventDispatch;
78         
79         // Settings for Debug Process
80         private String transaction_id = "";
81         private String fileuri = "";
82         
83         /**
84          * Listens to events from the XDebug and fires corresponding 
85          * debug events.
86          */
87         class EventDispatchJob extends Job {
88                 
89                 public EventDispatchJob() {
90                         super("XDebug Event Dispatch");
91                         setSystem(true);
92                 }
93
94                 public String getAttributeValue (Node CurrentNode, String AttributeName) {
95                         String strValue = "";
96                         if (CurrentNode.hasAttributes()) {
97                                 NamedNodeMap listAttribute = CurrentNode.getAttributes();
98                                 Node nodeTransactionID = listAttribute.getNamedItem(AttributeName);
99                                 strValue = nodeTransactionID.getNodeValue();
100                         }
101                         return strValue;
102                 }
103
104                 /* (non-Javadoc)
105                  * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
106                  */
107                 protected IStatus run(IProgressMonitor monitor) {
108                         String event = "";
109                         int CurrentByte;
110                         boolean ZeroByteFound;
111                         int BytesToRead = 0;
112                         String InputXML;
113                         while (!isTerminated() && event != null) {
114                                 try {
115                                         ZeroByteFound = false;
116                                         event = "";
117                                         while (!ZeroByteFound){
118                                                 CurrentByte = fDebugReader.read();
119                                                 if (CurrentByte == 0) {
120                                                         ZeroByteFound = true;
121                                                     try {
122                                                         BytesToRead = Integer.parseInt(event);
123                                                       } catch (NumberFormatException e) {
124                                                         BytesToRead = 0;
125                                                       } 
126                                                 } else {
127                                                         event = event + (char)CurrentByte;
128                                                 }
129                                         }
130                                         if (BytesToRead > 0) {
131                                                 InputXML = "";
132                                                 for (int i = 0; i < BytesToRead; i++) {
133                                                         CurrentByte = fDebugReader.read();
134                                                         InputXML = InputXML + (char)CurrentByte;
135                                                 }
136                                                 CurrentByte = fDebugReader.read(); // Das Null Byte nach dem String lesen.
137                                                 try {
138                                                         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
139                                                         DocumentBuilder builder = factory.newDocumentBuilder();
140                                                         StringBufferInputStream InputXMLStream = new StringBufferInputStream(InputXML);
141                                                         Document doc = builder.parse(InputXMLStream);
142                                                         Node myNode = doc.getFirstChild();
143                                                         if (myNode.getNodeName() == "init") {
144                                                                 transaction_id = getAttributeValue(myNode, "appid");
145                                                                 fileuri = getAttributeValue(myNode, "fileuri");
146                                                                 fThread.setBreakpoints(null);
147                                                                 fThread.setStepping(false);
148                                                                 started();      
149                                                         } else if (myNode.getNodeName() == "response") {
150                                                                  
151                                                         }
152                                                         
153                                                 } catch (ParserConfigurationException e) {
154
155                                                 } catch (SAXException e) {
156                                                         
157                                                 }
158                                         }
159 /*
160                                         event = fDebugReader.readLine();
161                                         if (event != null) {
162                                                 fThread.setBreakpoints(null);
163                                                 fThread.setStepping(false);
164                                                 if (event.equals("started")) {
165                                                         
166                                                 } else if (event.equals("terminated")) {
167                                                         terminated();
168                                                 } else if (event.startsWith("resumed")) {
169                                                         if (event.endsWith("step")) {
170                                                                 fThread.setStepping(true);
171                                                                 resumed(DebugEvent.STEP_OVER);
172                                                         } else if (event.endsWith("client")) {
173                                                                 resumed(DebugEvent.CLIENT_REQUEST);
174                                                         }
175                                                 } else if (event.startsWith("suspended")) {
176                                                         if (event.endsWith("client")) {
177                                                                 suspended(DebugEvent.CLIENT_REQUEST);
178                                                         } else if (event.endsWith("step")) {
179                                                                 suspended(DebugEvent.STEP_END);
180                                                         } else if (event.indexOf("breakpoint") >= 0) {
181                                                                 breakpointHit(event);
182                                                         }
183                                                 }
184                                         }
185 */                                      
186                                 } catch (IOException e) {
187                                         terminated();
188                                 }
189                         }
190                         return Status.OK_STATUS;
191                 }
192                 
193         }
194         
195         /**
196          * Constructs a new debug target in the given launch for the 
197          * associated PDA VM process.
198          * 
199          * @param launch containing launch
200          * @param debugPort port to read events from
201          * @exception CoreException if unable to connect to host
202          */
203         public XDebugTarget(ILaunch launch, int debugPort) throws CoreException {
204                 super(null);
205                 fLaunch = launch;
206                 fTarget = this;
207                 try {
208                         fDebugServerSocket = new ServerSocket(debugPort);
209                         fDebugSocket = fDebugServerSocket.accept();
210                         fDebugWriter = new OutputStreamWriter(fDebugSocket.getOutputStream(), "UTF8");
211                         fDebugReader = new BufferedReader(new InputStreamReader(fDebugSocket.getInputStream()));
212                         
213                 } catch (UnknownHostException e) {
214                         abort("Unable to connect to PDA VM", e);
215                 } catch (IOException e) {
216                         abort("Unable to connect to PDA VM", e);
217                 }
218                 fThread = new XDebugThread(this);
219                 fThreads = new IThread[] {fThread};
220                 fEventDispatch = new EventDispatchJob();
221                 fEventDispatch.schedule();
222                 DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
223         }
224         /* (non-Javadoc)
225          * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
226          */
227         public IProcess getProcess() {
228                 return fProcess;
229         }
230         /* (non-Javadoc)
231          * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
232          */
233         public IThread[] getThreads() throws DebugException {
234                 return fThreads;
235         }
236         /* (non-Javadoc)
237          * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
238          */
239         public boolean hasThreads() throws DebugException {
240                 return false;
241         }
242         /* (non-Javadoc)
243          * @see org.eclipse.debug.core.model.IDebugTarget#getName()
244          */
245         public String getName() throws DebugException {
246                 if (fName == null) {
247                         fName = "XDebug Core";
248                         try {
249                                 fName = getLaunch().getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_XDEBUG_PROGRAM, "PDA VM");
250                         } catch (CoreException e) {
251                         }
252                 }
253                 return fName;
254         }
255         /* (non-Javadoc)
256          * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
257          */
258         public boolean supportsBreakpoint(IBreakpoint breakpoint) {
259                 if (breakpoint.getModelIdentifier().equals(PHPDebugCorePlugin.PLUGIN_ID)) {
260                         /* Axel: Weiß nicht ob das wichtig ist.                         
261                         try {
262                                 String program = getLaunch().getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_XDEBUG_PROGRAM, (String)null);
263                                 if (program != null) {
264                                         IMarker marker = breakpoint.getMarker();
265                                         if (marker != null) {
266                                                 IPath p = new Path(program);
267                                                 return marker.getResource().getFullPath().equals(p);
268                                         }
269                                 }
270                         } catch (CoreException e) {
271                         }                       
272 */                              
273                         return true;
274                 }
275                 return false;
276         }
277         /* (non-Javadoc)
278          * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
279          */
280         public IDebugTarget getDebugTarget() {
281                 return this;
282         }
283         /* (non-Javadoc)
284          * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
285          */
286         public ILaunch getLaunch() {
287                 return fLaunch;
288         }
289         /* (non-Javadoc)
290          * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
291          */
292         public boolean canTerminate() {
293 //              return getProcess().canTerminate();
294                 return false;
295         }
296         /* (non-Javadoc)
297          * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
298          */
299         public boolean isTerminated() {
300 //              return getProcess().isTerminated();
301                 return false;
302         }
303         /* (non-Javadoc)
304          * @see org.eclipse.debug.core.model.ITerminate#terminate()
305          */
306         public void terminate() throws DebugException {
307                 sendRequest ("stop -i " + transaction_id);
308         }
309         /* (non-Javadoc)
310          * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
311          */
312         public boolean canResume() {
313                 return !isTerminated() && isSuspended();
314         }
315         /* (non-Javadoc)
316          * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
317          */
318         public boolean canSuspend() {
319                 return !isTerminated() && !isSuspended();
320         }
321         /* (non-Javadoc)
322          * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
323          */
324         public boolean isSuspended() {
325                 return fSuspended;
326         }
327         /* (non-Javadoc)
328          * @see org.eclipse.debug.core.model.ISuspendResume#resume()
329          */
330         public void resume() throws DebugException {
331                 sendRequest("run -i " + transaction_id);
332         }
333         
334         /**
335          * Notification the target has resumed for the given reason
336          * 
337          * @param detail reason for the resume
338          */
339         private void resumed(int detail) {
340                 fSuspended = false;
341                 fThread.fireResumeEvent(detail);
342         }
343         
344         /**
345          * Notification the target has suspended for the given reason
346          * 
347          * @param detail reason for the suspend
348          */
349         private void suspended(int detail) {
350                 fSuspended = true;
351                 fThread.fireSuspendEvent(detail);
352         }       
353         
354         /* (non-Javadoc)
355          * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
356          */
357         public void suspend() throws DebugException {
358         }
359         /* (non-Javadoc)
360          * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
361          */
362         public void breakpointAdded(IBreakpoint breakpoint) {
363                 if (supportsBreakpoint(breakpoint)) {
364                         try {
365                                 if (breakpoint.isEnabled()) {
366                                         try {
367                                                 sendRequest("breakpoint_set -i " + transaction_id +" -t line -n " + (((ILineBreakpoint)breakpoint).getLineNumber() - 1));
368                                         } catch (CoreException e) {
369                                         }
370                                 }
371                         } catch (CoreException e) {
372                         }
373                 }
374         }
375         /* (non-Javadoc)
376          * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
377          */
378         public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
379                 if (supportsBreakpoint(breakpoint)) {
380                         try {
381                                 sendRequest("clear " + ((ILineBreakpoint)breakpoint).getLineNumber());
382                         } catch (CoreException e) {
383                         }
384                 }
385         }
386         /* (non-Javadoc)
387          * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
388          */
389         public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
390                 if (supportsBreakpoint(breakpoint)) {
391                         try {
392                                 if (breakpoint.isEnabled()) {
393                                         breakpointAdded(breakpoint);
394                                 } else {
395                                         breakpointRemoved(breakpoint, null);
396                                 }
397                         } catch (CoreException e) {
398                         }
399                 }
400         }
401         /* (non-Javadoc)
402          * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
403          */
404         public boolean canDisconnect() {
405                 return false;
406         }
407         /* (non-Javadoc)
408          * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
409          */
410         public void disconnect() throws DebugException {
411         }
412         /* (non-Javadoc)
413          * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
414          */
415         public boolean isDisconnected() {
416                 return false;
417         }
418         /* (non-Javadoc)
419          * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
420          */
421         public boolean supportsStorageRetrieval() {
422                 return false;
423         }
424         /* (non-Javadoc)
425          * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
426          */
427         public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
428                 return null;
429         }
430
431         /**
432          * Notification we have connected to the VM and it has started.
433          * Resume the VM.
434          */
435         private void started() {
436                 fireCreationEvent();
437                 installDeferredBreakpoints();
438                 try {
439                         resume();
440                 } catch (DebugException e) {
441                 }
442         }
443         
444         /**
445          * Install breakpoints that are already registered with the breakpoint
446          * manager.
447          */
448         private void installDeferredBreakpoints() {
449                 IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(PHPDebugCorePlugin.PLUGIN_ID);
450                 for (int i = 0; i < breakpoints.length; i++) {
451                         breakpointAdded(breakpoints[i]);
452                 }
453         }
454         
455         /**
456          * Called when this debug target terminates.
457          */
458         private void terminated() {
459                 fTerminated = true;
460                 fSuspended = false;
461                 DebugPlugin.getDefault().getBreakpointManager().removeBreakpointListener(this);
462                 fireTerminateEvent();
463         }
464         
465         /**
466          * Returns the current stack frames in the target.
467          * 
468          * @return the current stack frames in the target
469          * @throws DebugException if unable to perform the request
470          */
471         protected IStackFrame[] getStackFrames() throws DebugException {
472                 synchronized (fDebugSocket) {
473                         sendRequest("stack -i " + transaction_id);
474                         try {
475                                 String framesData = fDebugReader.readLine();
476                                 if (framesData != null) {
477                                         String[] frames = framesData.split("#");
478                                         IStackFrame[] theFrames = new IStackFrame[frames.length];
479                                         for (int i = 0; i < frames.length; i++) {
480                                                 String data = frames[i];
481                                                 theFrames[frames.length - i - 1] = new XDebugStackFrame(fThread, data, i);
482                                         }
483                                         return theFrames;
484                                 }
485                         } catch (IOException e) {
486                                 abort("Unable to retrieve stack frames", e);
487                         }
488                 }
489                 return new IStackFrame[0];
490         }
491         
492         /**
493          * Single step the interpreter.
494          * 
495          * @throws DebugException if the request fails
496          */
497         protected void step() throws DebugException {
498                 sendRequest("step_into -i " + transaction_id);
499         }
500         
501         /**
502          * Returns the current value of the given variable.
503          * 
504          * @param variable
505          * @return variable value
506          * @throws DebugException if the request fails
507          */
508         protected IValue getVariableValue(XDebugVariable variable) throws DebugException {
509                 synchronized (fDebugSocket) {
510                         sendRequest("var " + variable.getStackFrame().getIdentifier() + " " + variable.getName());
511                         try {
512                                 String value = fDebugReader.readLine();
513                                 return new XDebugValue(this, value);
514                         } catch (IOException e) {
515                                 abort(MessageFormat.format("Unable to retrieve value for variable {0}", new String[]{variable.getName()}), e);
516                         }
517                 }
518                 return null;
519         }
520         
521         /**
522          * Returns the values on the data stack (top down)
523          * 
524          * @return the values on the data stack (top down)
525          */
526         public IValue[] getDataStack() throws DebugException {
527                 synchronized (fDebugSocket) {
528                         sendRequest ("data");
529                         try {
530                                 String valueString = fDebugReader.readLine();
531                                 if (valueString != null && valueString.length() > 0) {
532                                         String[] values = valueString.split("\\|");
533                                         IValue[] theValues = new IValue[values.length];
534                                         for (int i = 0; i < values.length; i++) {
535                                                 String value = values[values.length - i - 1];
536                                                 theValues[i] = new XDebugValue(this, value);
537                                         }
538                                         return theValues;
539                                 }
540                         } catch (IOException e) {
541                                 abort("Unable to retrieve data stack", e);
542                         }
543                 }
544                 return new IValue[0];           
545         }
546         
547         /**
548          * Sends a request to the PDA VM and waits for an OK.
549          * 
550          * @param request debug command
551          * @throws DebugException if the request fails
552          */
553         private void sendRequest(String request) throws DebugException {
554                 synchronized (fDebugSocket) {
555                         try {
556                                 fDebugWriter.write(request);
557                                 fDebugWriter.write(0);
558                                 fDebugWriter.flush();
559                         } catch (IOException e) {
560                                 e.printStackTrace();
561                 }
562                 }               
563         }
564         
565         /**
566          * Notification a breakpoint was encountered. Determine
567          * which breakpoint was hit and fire a suspend event.
568          * 
569          * @param event debug event
570          */
571         private void breakpointHit(String event) {
572                 // determine which breakpoint was hit, and set the thread's breakpoint
573                 int lastSpace = event.lastIndexOf(' ');
574                 if (lastSpace > 0) {
575                         String line = event.substring(lastSpace + 1);
576                         int lineNumber = Integer.parseInt(line);
577                         IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(PHPDebugCorePlugin.PLUGIN_ID);
578                         for (int i = 0; i < breakpoints.length; i++) {
579                                 IBreakpoint breakpoint = breakpoints[i];
580                                 if (supportsBreakpoint(breakpoint)) {
581                                         if (breakpoint instanceof ILineBreakpoint) {
582                                                 ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint;
583                                                 try {
584                                                         if (lineBreakpoint.getLineNumber() == lineNumber) {
585                                                                 fThread.setBreakpoints(new IBreakpoint[]{breakpoint});
586                                                                 break;
587                                                         }
588                                                 } catch (CoreException e) {
589                                                 }
590                                         }
591                                 }
592                         }
593                 }
594                 suspended(DebugEvent.BREAKPOINT);
595         }       
596 }
597