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