388ddc957b650d9c064c464623af0cc2b1d2e26c
[phpeclipse.git] / net.sourceforge.phpeclipse.debug.core / src / net / sourceforge / phpdt / internal / debug / core / PHPDBGProxy.java
1 /***********************************************************************************************************************************
2  * Copyright (c) 2000, 2002 IBM Corp. and others. All rights reserved. This program and the accompanying materials are made
3  * available under the terms of the Common Public License v1.0 which accompanies this distribution, and is available at
4  * http://www.eclipse.org/legal/cpl-v10.html
5  *
6  * Contributors: IBM Corporation - Initial implementation Vicente Fernando - www.alfersoft.com.ar Christian Perkonig - remote debug
7  **********************************************************************************************************************************/
8 package net.sourceforge.phpdt.internal.debug.core;
9
10 import java.io.BufferedReader;
11 import java.io.IOException;
12 import java.io.InputStreamReader;
13 import java.io.OutputStream;
14 import java.net.ServerSocket;
15 import java.net.Socket;
16 import java.net.SocketTimeoutException;
17 import java.util.Map;
18 import java.util.Vector;
19
20 //import net.sourceforge.phpdt.internal.core.JavaProject;
21 import net.sourceforge.phpdt.internal.debug.core.breakpoints.PHPLineBreakpoint;
22 import net.sourceforge.phpdt.internal.debug.core.model.PHPDebugTarget;
23 import net.sourceforge.phpdt.internal.debug.core.model.PHPStackFrame;
24 import net.sourceforge.phpdt.internal.debug.core.model.PHPThread;
25 import net.sourceforge.phpdt.internal.debug.core.model.PHPVariable;
26 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
27 /*import net.sourceforge.phpeclipse.actions.PHPEclipseShowAction;*/
28
29 //import org.eclipse.core.internal.resources.Project;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.core.runtime.IPath;
32 import org.eclipse.core.runtime.Path;
33 import org.eclipse.debug.core.DebugException;
34 import org.eclipse.debug.core.DebugPlugin;
35 import org.eclipse.debug.core.model.IBreakpoint;
36 //import org.eclipse.swt.browser.Browser;
37
38 public class PHPDBGProxy {
39
40         private ServerSocket            server          = null;
41         private BufferedReader          reader          = null;
42         private PHPDBGInterface         DBGInt          = null;         // The DBG interface which is linked with the proxy
43         private PHPDebugTarget          debugTarget = null;
44         private PHPDBGProxy             thisProxy       = null;
45         private PHPLoop                         phpLoop;
46         private PHPThread                       PHPMainThread;
47         private Socket                          socket;
48         private int                             port;
49         private boolean                         remote;
50         private boolean                         pathtranslation;
51         private Map                             pathmap;
52         private IPath                           remoteSourcePath;
53
54         /**
55          */
56         public PHPDBGProxy () {
57           thisProxy = this;
58         }
59         
60         /**
61          * updateView
62          * Clean up the view, but leave the Debug session running.
63          * added by ed_mann
64          */
65         public void updateView(){
66           getDebugTarget().updateThreads(PHPMainThread);
67                 }
68         
69         /**
70          * @param remote
71          * @param remoteSourcePath
72          * @param pathTranslate
73          * @param paths
74          */
75         public PHPDBGProxy (boolean remote, String remoteSourcePath, boolean pathTranslate, Map paths) {
76                 thisProxy             = this;
77                 this.remote               = remote;
78                 this.remoteSourcePath = new Path (remoteSourcePath);
79                 this.pathmap          = paths;
80                 this.pathtranslation  = pathTranslate;
81         }
82
83         /**
84          *
85          */
86         public void start () {
87                 createServerSocket ();                                      // Create a server socket for communicatio with DBG
88
89                 this.startPHPLoop ();                                                                           //
90         }
91
92         /**
93          *
94          */
95         public void stop () {
96                 phpLoop.setShouldStop ();                                   // Notify the thread's 'run loop' to stop
97                 if (DBGInt != null) {                                       // If we have a DBG interface linked with this proxy
98                 DBGInt.setShouldStop ();                                //  Notify the DBG interface to stop the waiting for response
99                 }
100
101 //              if (!remote) {                                              // If it's not a remote proxy session
102 //              try {
103 //                      getDebugTarget ().getProcess ().terminate ();       //
104 //              } catch (DebugException e) {
105 //                      e.printStackTrace ();
106 //              }
107 //              }
108
109                 phpLoop.notifyWait ();
110         }
111         
112         public void setTerminated () {
113                 try {
114                         PHPMainThread.terminate ();
115                 }
116                 catch (DebugException e) {
117                 }
118         }
119
120         /**
121          * TODO Is this method called from anywhere?
122          *
123          * Returns a already created server socket, or
124          * creates a server socket if none exists, and
125          * returns the newly created one.
126          *
127          * @return A server socket
128          */
129         protected ServerSocket getServerSocket () throws IOException {
130                 if (server == null) {                                                                           // Do we have already a server socket
131                 createServerSocket ();                                                                  //  No, then create one
132                 }
133
134                 return server;                                                                                          // Return the server socket
135         }
136
137         /**
138          * Find a free unused port between 10001 and 10101 if the current debug session
139          * is for remote debugging, and a unused port 7869 if it is used as non remote debugging.
140          * 
141          * For remote debugging the used port is submitted with the URL.
142          * E.g. http://localhost/index.php?DBGSESSID=1@localhost:10001
143          * For non remote debugging (if PHPeclipse used e.g. php cli directly) no port
144          * can be submitted by parameter, and only the default port (7869) can be used.
145          * 
146          * @note: The free dbg version doesn't allow to set the appropriate port within php.ini! 
147          * 
148          * 
149          */
150         protected void createServerSocket () {
151                 if (this.remote) {
152                         port = SocketUtil.findUnusedLocalPort ("localhost", 10001, 10101);      // Get the first free port in the range from 10001 to 10101
153                 }
154                 else {
155                         port = SocketUtil.findUnusedLocalPort ("localhost", 7869, 7869);        // Get the first free port in the range from 7869 to 7869
156                 }
157                 
158                 if (port == -1) {                                                   // Did we get a free port?
159                 PHPDebugCorePlugin.log (5, "Cannot find free port!!!!");        //  No, output a error message
160
161                 return;                                                         //  And return
162                 }
163                 try {
164                 if (server == null) {                                           // If there is no server socket yet
165                         server = new ServerSocket (port);                           //  create a server socket for the free port
166                         //System.out.println("ServerSocket on port: " + port);
167                 }
168                 } catch (IOException e) {
169                 PHPDebugCorePlugin.log (e);
170                 stop ();
171                 }
172         }
173
174         /**
175          *
176          */
177         public Socket getSocket () throws IOException {
178                 return socket;                                                          // Return the socket
179         }
180
181         /**
182          * Set the DBG interface which is linked to this proxy
183          *
184          * @paran DBGInt The DGB interface which is linked with this proxy
185          */
186         protected void setDBGInterface (PHPDBGInterface DBGInt) {
187                 this.DBGInt = DBGInt;
188         }
189
190         /**
191          * Get the DBG interface which is linked to this proxy
192          *
193          * @paran DBGInt The DGB interface which is linked with this proxy
194          */
195         public PHPDBGInterface getDBGInterface () {
196                 return DBGInt;
197         }
198         
199         /**
200          * Give back a buffered input stream for the socket which is
201          * linked with this proxy
202          */
203         public BufferedReader getReader () throws IOException {
204                 if (reader == null) {                                               // Do we already have a buffered input stream
205                 reader = new BufferedReader (new InputStreamReader (this.getSocket ().getInputStream (),
206                                                                         "ISO8859_1"));
207                 }
208
209           return reader;                                                      // Return the buffered input stream
210         }
211
212         /**
213          *
214          */
215         public BufferedReader getReader (Socket socket) throws IOException {
216                 if (socket != null) {                                                                                           // Is a socket provided
217                 return new BufferedReader (new InputStreamReader (socket.getInputStream (),
218                                                                                                   "ISO8859_1"));  // Then create a buffered input stream
219                 }
220                 else {
221                 return null;                                                      // Without a socket we can't create a input stream
222                 }
223         }
224
225         /**
226          *
227          * @return The output stream for this proxy's socket
228          */
229         public OutputStream getOutputStream () throws IOException {
230                 return this.getSocket ().getOutputStream ();
231         }
232
233         /**
234          *
235          */
236         protected void setBreakPoints () throws IOException, CoreException {
237                 IBreakpoint[] breakpoints = DebugPlugin.getDefault ().getBreakpointManager ().getBreakpoints ();
238
239                 for (int i = 0; i < breakpoints.length; i++) {
240                         if (breakpoints[i].isEnabled ()) {
241                                 addBreakpoint (breakpoints[i]);
242                         }
243                 }
244         }
245
246         /**
247          *
248          */
249         private String MapPath (PHPLineBreakpoint phpLBP) {
250                 IPath    filename;
251                 IPath    remotePath;
252                 IPath    newpath;
253                 IPath    localPath;
254                 String   local;
255
256                 if (remote) {
257                         filename = phpLBP.getMarker().getResource().getProjectRelativePath();
258                         filename = remoteSourcePath.append (filename);
259                 } else {
260                         filename = phpLBP.getMarker().getResource().getFullPath();
261                 }
262
263                 String path = filename.toOSString();
264
265                 if ((pathmap != null) && remote) {
266                         java.util.Iterator i = pathmap.keySet().iterator();
267
268                         while (i.hasNext()) {
269                                 String k = (String) i.next();
270                                 if (path.startsWith(k)) {
271                                         path = pathmap.get(k) + path.substring(k.length());
272                                         break;
273                                 }
274                         }
275                 }
276
277                 if (remoteSourcePath.isEmpty ()) {
278                         if ((pathmap != null) && remote) {
279                                 java.util.Iterator iterator = pathmap.keySet().iterator();
280
281                                 while (iterator.hasNext ()) {
282                                         local      = (String) iterator.next ();                 // Get the local/client side path of the mapping
283                                         remotePath = new Path ((String) pathmap.get (local));   // Get the remote/server side path of the mapping
284                                         localPath  = new Path (local);                          // Get the remote/server side path of the mapping
285
286                                         if (localPath.isPrefixOf (filename)) {                  // Starts the remote/server side file path with the remote/server side mapping path
287                                                                                                                                                         // dann prefix abh�ngen und den remote path davorh�gen
288                                                 newpath = filename.removeFirstSegments (localPath.matchingFirstSegments (filename));
289                                                 newpath = remotePath.append (newpath);
290                                                 path    = newpath.toString ();
291
292                                                 if (path.substring (0, 1).equals ("/")) {
293                                                         path = path.replace ('\\', '/');
294                                                 }
295                                                 else {
296                                                         path = path.replace ('/', '\\');
297                                                 }
298
299                                                 return path;
300                                         }
301                                 }
302                         }
303                 }
304                 else {
305                         if (pathtranslation && remote) {
306                                 if (remoteSourcePath.toString ().substring (0, 1).equals ("/")) {
307                                         path = path.replace ('\\', '/');
308                                 }
309                                 else {
310                                         path = path.replace ('/', '\\');
311                                 }
312                         }
313                 }
314
315                 return path;
316         }
317
318         /**
319          *
320          */
321         public void addBreakpoint (IBreakpoint breakpoint) {
322                 if (DBGInt == null) {
323                 return;
324                 }
325
326                 int bpNo = 0;
327
328                 try {
329                 PHPLineBreakpoint phpLBP;
330
331                 if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier()) {
332                         phpLBP = (PHPLineBreakpoint) breakpoint;
333
334                         //      bpNo= DBGInt.addBreakpoint(phpLBP.getMarker().getResource().getLocation().toOSString(), phpLBP.getLineNumber());
335                         if (phpLBP.isConditionEnabled ()) {
336                                 bpNo = DBGInt.addBreakpoint (MapPath(phpLBP),
337                                                                                          phpLBP.getLineNumber(), 
338                                                                  phpLBP.getHitCount(),
339                                                                  phpLBP.getCondition ());
340                         }
341                         else {
342                                 bpNo = DBGInt.addBreakpoint (MapPath(phpLBP),
343                                                                                          phpLBP.getLineNumber(), 
344                                                                  phpLBP.getHitCount(),
345                                                                  "");
346                         }
347                         
348                         phpLBP.setDBGBpNo(bpNo);
349                 }
350                 } catch (IOException e) {
351                     PHPDebugCorePlugin.log(e);
352                     stop();
353                 } catch (CoreException e) {
354                 PHPDebugCorePlugin.log(e);
355                 stop();
356                 }
357         }
358
359         /**
360          *
361          */
362         public void removeBreakpoint (IBreakpoint breakpoint) {
363                 if (DBGInt == null) {
364                 return;
365                 }
366
367                 try {
368                 PHPLineBreakpoint phpLBP;
369
370                 if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier ()) {
371                         phpLBP = (PHPLineBreakpoint) breakpoint;
372
373                         //      bpNo= DBGInt.addBreakpoint(filename.toOSString(), phpLBP.getLineNumber());
374
375                         DBGInt.removeBreakpoint(MapPath(phpLBP), phpLBP.getLineNumber(), phpLBP.getDBGBpNo());
376                 }
377                 } catch (IOException e) {
378                 PHPDebugCorePlugin.log (e);
379                 stop ();
380                 } catch (CoreException e) {
381                 PHPDebugCorePlugin.log (e);
382                 stop ();
383                 }
384         }
385
386         /**
387          *
388          */
389         public void phpLoopNotify () {
390                 phpLoop.notifyWait ();
391         }
392
393         /**
394          *
395          */
396         public void startPHPLoop () {
397                 phpLoop = new PHPLoop ();                                                                       // Create a DBG communication loop object
398
399                 phpLoop.start ();                                                                                       // And start the communication loop
400         }
401
402         /**
403          *
404          */
405         public void resume () {
406                 try {
407                 DBGInt.continueExecution();
408                 phpLoop.notifyWait();
409                 } catch (IOException e) {
410                 PHPeclipsePlugin.log("Debugging session ended.", e);
411                 stop();
412                 }
413         }
414
415         /**
416          *
417          */
418         public void pause () {
419                 try {
420                 if (null != DBGInt) {
421                                 DBGInt.pauseExecution();
422                         }
423                 else {
424                         // TODO Make sure the Suspend action is grayed out
425                         // when DBGInt is null
426                 }
427                 } catch (IOException e) {
428                 PHPDebugCorePlugin.log (e);
429                 stop ();
430                 }
431         }
432
433         /**
434          *
435          */
436         protected PHPDebugTarget getDebugTarget() {
437                 return debugTarget;
438         }
439
440         /**
441          * Is called by the DebuggerRunner
442          * 
443          * @param debugTarget
444          */
445         public void setDebugTarget (PHPDebugTarget debugTarget) {
446                 this.debugTarget = debugTarget;
447                 debugTarget.setPHPDBGProxy(this);
448         }
449         
450         /**
451          * This method is called by a stackframe.
452          * It reads the variables from PHP via DBG
453          *
454          * @param frame The stackframe which wants the variables.
455          * @return      The list of variables for this stackframe.
456          */
457         public Vector readVariables (PHPStackFrame frame) {
458                 try {
459                 return DBGInt.getVariables (frame);                                             // Get the variables from DBG interface
460                 } catch (IOException ioex) {
461                 ioex.printStackTrace ();
462                 throw new RuntimeException (ioex.getMessage ());
463                 } catch (DebugException ex) {
464                     ex.printStackTrace ();
465                     throw new RuntimeException (ex.getMessage ());
466                 }
467         }
468
469         /**
470          *
471          * @param frame
472          * @param evalString
473          * @return
474          */
475         public PHPVariable[] eval (PHPStackFrame frame, String evalString) {
476                 try {
477                 return DBGInt.evalBlock (frame, evalString);
478                 //return DBGInt.getVariables(frame);
479                 } catch (IOException ioex) {
480                 ioex.printStackTrace();
481                 throw new RuntimeException(ioex.getMessage());
482                 } catch (DebugException ex) {
483                 ex.printStackTrace();
484                 throw new RuntimeException(ex.getMessage());
485                 }
486         }
487
488         public void readStepOverEnd (PHPStackFrame stackFrame) {
489                 try {
490                 DBGInt.stepOver();
491                 phpLoop.notifyWait();
492                 } catch (Exception e) {
493                 PHPDebugCorePlugin.log(e);
494                 }
495         }
496
497         public void readStepReturnEnd (PHPStackFrame stackFrame) {
498                 try {
499                 DBGInt.stepOut();
500                     phpLoop.notifyWait();
501                 } catch (Exception e) {
502                 PHPDebugCorePlugin.log(e);
503                 }
504         }
505
506         public void readStepIntoEnd (PHPStackFrame stackFrame) {
507                 try {
508                 DBGInt.stepInto();
509                 phpLoop.notifyWait();
510                 } catch (Exception e) {
511                 PHPDebugCorePlugin.log(e);
512                 }
513         }
514
515         /*
516          * public PHPStackFrame[] readFrames(PHPThread thread) { //try { //this.println("th " + thread.getId() + " ; f "); //return new
517          * FramesReader(getMultiReaderStrategy()).readFrames(thread); return null; //} catch (IOException e) { //
518          * PHPDebugCorePlugin.log(e); // return null; //}
519          *  }
520          */
521
522         public void closeSocket() throws IOException {
523                 if (socket != null) {
524                 socket.close();
525                 }
526         }
527
528         public void closeServerSocket() throws IOException {
529                 if (server != null) {
530                 server.close();
531                 }
532         }
533
534         public int getPort() {
535                 return port;
536         }
537
538         /**
539          *
540          *
541          */
542         class PHPLoop extends Thread {
543                 private boolean shouldStop;
544
545                 public PHPLoop () {
546                 shouldStop = false;
547                 this.setName ("PHPDebuggerLoop");
548                 }
549                 
550                 /**
551                  *
552                  */
553                 public synchronized void setShouldStop () {
554                         shouldStop = true;                                                                                      // The run loop should stop
555
556                         try {
557                                 // If the loop thread is blocked on the server socket,
558                                 // forcibly unblock it to avoid leaking the thread,
559                                 // the socket and the port
560                                 closeServerSocket ();
561                         } catch (IOException x) {
562                                 // Log this as a warning?
563                                 PHPDebugCorePlugin.log (x);
564                         }
565                 }
566
567                 /**
568                  *
569                  */
570                 public synchronized void notifyWait () {
571                 notify ();
572                 }
573
574                 /**
575                  *
576                  *
577                  */
578                 public void run () {
579                 try {
580                         int                     i;
581                         long                    interval        = 200;                                  // Wait 200 ms maximum for a DBG response
582                         boolean                 newconnect      = false;                                //
583                         Socket                  newSocket       = null;
584                         PHPStackFrame[] StackList;
585                         PHPDBGInterface newDBGInt;
586
587                         //                              synchronized (this) {
588                         //                                      wait();
589                         //                              }
590
591                         PHPMainThread = new PHPThread (getDebugTarget (), getPort ());
592                         PHPMainThread.setName ("Thread [main]");
593
594                         //                              while ((getDebugTarget() == null) && (timeout < 100)) {
595                         //                                      sleep(100);
596                         //                                      timeout++;
597                         //                              }
598                         // Be sure debug target is set
599                         //                              PHPMainThread.setDebugTarget(getDebugTarget());
600
601                                 getDebugTarget ().addThread (PHPMainThread);
602
603                         //System.out.println("Waiting for breakpoints.");
604
605                         while (!shouldStop) {                                                           // As long as nobody will stop us
606                                 newconnect = true;                              // The first time
607
608                                 try {
609                                         newSocket = server.accept();                            // Waits until DBG want to connect
610                                         //System.out.println("Accepted! : " + socket.toString());
611                                 } catch (SocketTimeoutException e) {
612                                         newconnect = false;                                                     // No one wants to connect (connection already done)
613                                 } catch (IOException e) {
614                                         PHPDebugCorePlugin.log(e);
615                                         return;
616                                 }
617
618                                 if (newconnect) {                                                               // Is it just after a new connection
619                                         if (DBGInt == null) {                                           // Do we have a DBG interface?
620                                         server.setSoTimeout(1);                                 // ???
621                                                 }
622
623                                         newDBGInt = new PHPDBGInterface (getReader (newSocket),                 // Create a new interface
624                                                                                                                  newSocket.getOutputStream (),
625                                                                                                                  thisProxy);
626                                         newDBGInt.waitResponse (1000);                          // Wait for the initial DBG response
627                                         newDBGInt.flushAllPackets ();               // Read and process the DBG response
628
629                                         // Check version and session ID
630                                         if ((DBGInt == null) ||                                 // If we have no interface
631                                                 (DBGInt.getSID () == newDBGInt.getSID ())) {// or the new session ID is different to the old one
632                                         DBGInt = newDBGInt;                                                     // Set the new interface as current one
633
634                                         try {
635                                                 closeSocket ();
636                                         }
637                                                         catch (IOException e) {
638                                                 PHPDebugCorePlugin.log (e);
639                                                 shouldStop = true;
640                                         }
641
642                                         socket = newSocket;
643                                         setBreakPoints ();
644                                         DBGInt.continueExecution ();                    // Notify DBG that PHP should continue
645                                         }
646                                                 else {
647                                         newDBGInt.continueExecution ();                         // Notify DBG that PHP should continue
648                                         newSocket.close ();
649                                         }
650                                 }
651
652                                 if (DBGInt.waitResponse (interval)) {                                           // Wait for a DBG response (200 ms)
653                                         DBGInt.flushAllPackets ();                                                              // If we got something, read and process it
654
655                                         if (DBGInt.BPUnderHit != 0) {                                                   // ???
656                                         StackList = DBGInt.getStackList ();                 // Get the stack list from DBGInterface
657
658                                             if (StackList.length > 0) {                         // If there is something in stack list
659                                                 for (i = 0; i < StackList.length; i++) {        // For all stack list
660                                                         StackList[i].setThread (PHPMainThread);     // Set the PHPTread for all PHPStackFrames
661
662                                                         if (DBGInt.getModByNo (StackList[i].getModNo ()).equals ("")) {
663                                                                 DBGInt.getSourceTree ();
664                                                         }
665
666                                                         StackList[i].setFile (DBGInt.getModByNo (StackList[i].getModNo ()));
667                                                 }
668
669                                                 PHPMainThread.setStackFrames (StackList);
670                                         }
671
672                                         PHPMainThread.suspend ();                             // Fire debug event
673
674                                             synchronized (this) {
675                                                 wait ();
676                                         }
677                                         }
678                                 }
679
680                                 if (remote) {
681                                         if (PHPMainThread.isTerminated ()) {
682                                         shouldStop = true;
683
684                                             break;                                                // Go for terminating the thread
685                                         }
686                                 } else {
687                                         if (PHPMainThread.isTerminated () ||
688                                                 getDebugTarget ().getProcess ().isTerminated ()) {
689                                         shouldStop = true;
690
691                                         break;                                                // Go for terminating the thread
692                                         }
693                                 }
694                         }
695                 } catch (Exception ex) {
696                         PHPDebugCorePlugin.log (ex);
697                         System.out.println (ex);
698                 } finally {
699                         try {
700                                 getDebugTarget ().terminate ();
701                                 closeSocket();
702                                 closeServerSocket ();
703                         } catch (IOException e) {
704                                 PHPDebugCorePlugin.log (e);
705
706                                 return;
707                         }
708
709                         //System.out.println("Socket loop finished.");
710                 }
711                 }
712         }
713 }