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
 
   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;
 
  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;
 
  18 import java.util.Vector;
 
  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;*/
 
  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;
 
  38 public class PHPDBGProxy {
 
  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;
 
  49         private boolean                         remote;
 
  50         private boolean                         pathtranslation;
 
  52         private IPath                           remoteSourcePath;
 
  56         public PHPDBGProxy () {
 
  62          * Clean up the view, but leave the Debug session running.
 
  65 //      public void updateView(){
 
  66 //        getDebugTarget().updateThreads(PHPMainThread);
 
  71          * @param remoteSourcePath
 
  72          * @param pathTranslate
 
  75         public PHPDBGProxy (boolean remote, String remoteSourcePath, boolean pathTranslate, Map paths) {
 
  78                 this.remoteSourcePath = new Path (remoteSourcePath);
 
  80                 this.pathtranslation  = pathTranslate;
 
  86         public void start () {
 
  87                 createServerSocket ();                                      // Create a server socket for communicatio with DBG
 
  89                 this.startPHPLoop ();                                                                           //
 
  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
 
 101 //              if (!remote) {                                              // If it's not a remote proxy session
 
 103 //                      getDebugTarget ().getProcess ().terminate ();       //
 
 104 //              } catch (DebugException e) {
 
 105 //                      e.printStackTrace ();
 
 109                 phpLoop.notifyWait ();
 
 112         public void setTerminated () {
 
 114                         PHPMainThread.terminate ();
 
 116                 catch (DebugException e) {
 
 121          * TODO Is this method called from anywhere?
 
 123          * Returns a already created server socket, or
 
 124          * creates a server socket if none exists, and
 
 125          * returns the newly created one.
 
 127          * @return A server socket
 
 129 //      protected ServerSocket getServerSocket () throws IOException {
 
 130 //              if (server == null) {                                                                           // Do we have already a server socket
 
 131 //              createServerSocket ();                                                                  //  No, then create one
 
 134 //              return server;                                                                                          // Return the server socket
 
 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.
 
 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.
 
 146          * @note: The free dbg version doesn't allow to set the appropriate port within php.ini! 
 
 150         protected void createServerSocket () {
 
 152                         port = SocketUtil.findUnusedLocalPort ("localhost", 10001, 10101);      // Get the first free port in the range from 10001 to 10101
 
 155                         port = SocketUtil.findUnusedLocalPort ("localhost", 7869, 7869);        // Get the first free port in the range from 7869 to 7869
 
 158                 if (port == -1) {                                                   // Did we get a free port?
 
 159                 PHPDebugCorePlugin.log (5, "Cannot find free port!!!!");        //  No, output a error message
 
 161                 return;                                                         //  And return
 
 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);
 
 168                 } catch (IOException e) {
 
 169                 PHPDebugCorePlugin.log (e);
 
 177         public Socket getSocket () throws IOException {
 
 178                 return socket;                                                          // Return the socket
 
 182          * Set the DBG interface which is linked to this proxy
 
 184          * @paran DBGInt The DGB interface which is linked with this proxy
 
 186 //      protected void setDBGInterface (PHPDBGInterface DBGInt) {
 
 187 //              this.DBGInt = DBGInt;
 
 191          * Get the DBG interface which is linked to this proxy
 
 193          * @paran DBGInt The DGB interface which is linked with this proxy
 
 195         public PHPDBGInterface getDBGInterface () {
 
 200          * Give back a buffered input stream for the socket which is
 
 201          * linked with this proxy
 
 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 (),
 
 209 //        return reader;                                                      // Return the buffered input stream
 
 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
 
 221                 return null;                                                      // Without a socket we can't create a input stream
 
 227          * @return The output stream for this proxy's socket
 
 229         public OutputStream getOutputStream () throws IOException {
 
 230                 return this.getSocket ().getOutputStream ();
 
 236         protected void setBreakPoints () throws IOException, CoreException {
 
 237                 IBreakpoint[] breakpoints = DebugPlugin.getDefault ().getBreakpointManager ().getBreakpoints ();
 
 239                 for (int i = 0; i < breakpoints.length; i++) {
 
 240                         if (breakpoints[i].isEnabled ()) {
 
 241                                 addBreakpoint (breakpoints[i]);
 
 249         private String MapPath (PHPLineBreakpoint phpLBP) {
 
 257                         filename = phpLBP.getMarker().getResource().getProjectRelativePath();
 
 258                         filename = remoteSourcePath.append (filename);
 
 260                         filename = phpLBP.getMarker().getResource().getFullPath();
 
 263                 String path = filename.toOSString();
 
 265                 if ((pathmap != null) && remote) {
 
 266                         java.util.Iterator i = pathmap.keySet().iterator();
 
 268                         while (i.hasNext()) {
 
 269                                 String k = (String) i.next();
 
 270                                 if (path.startsWith(k)) {
 
 271                                         path = pathmap.get(k) + path.substring(k.length());
 
 277                 if (remoteSourcePath.isEmpty ()) {
 
 278                         if ((pathmap != null) && remote) {
 
 279                                 java.util.Iterator iterator = pathmap.keySet().iterator();
 
 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
 
 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 ();
 
 292                                                 if (path.substring (0, 1).equals ("/")) {
 
 293                                                         path = path.replace ('\\', '/');
 
 296                                                         path = path.replace ('/', '\\');
 
 305                         if (pathtranslation && remote) {
 
 306                                 if (remoteSourcePath.toString ().substring (0, 1).equals ("/")) {
 
 307                                         path = path.replace ('\\', '/');
 
 310                                         path = path.replace ('/', '\\');
 
 321         public void addBreakpoint (IBreakpoint breakpoint) {
 
 322                 if (DBGInt == null) {
 
 329                 PHPLineBreakpoint phpLBP;
 
 331                 if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier()) {
 
 332                         phpLBP = (PHPLineBreakpoint) breakpoint;
 
 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 ());
 
 342                                 bpNo = DBGInt.addBreakpoint (MapPath(phpLBP),
 
 343                                                                                          phpLBP.getLineNumber(), 
 
 344                                                                  phpLBP.getHitCount(),
 
 348                         phpLBP.setDBGBpNo(bpNo);
 
 350                 } catch (IOException e) {
 
 351                     PHPDebugCorePlugin.log(e);
 
 353                 } catch (CoreException e) {
 
 354                 PHPDebugCorePlugin.log(e);
 
 362         public void removeBreakpoint (IBreakpoint breakpoint) {
 
 363                 if (DBGInt == null) {
 
 368                 PHPLineBreakpoint phpLBP;
 
 370                 if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier ()) {
 
 371                         phpLBP = (PHPLineBreakpoint) breakpoint;
 
 373                         //      bpNo= DBGInt.addBreakpoint(filename.toOSString(), phpLBP.getLineNumber());
 
 375                         DBGInt.removeBreakpoint(MapPath(phpLBP), phpLBP.getLineNumber(), phpLBP.getDBGBpNo());
 
 377                 } catch (IOException e) {
 
 378                 PHPDebugCorePlugin.log (e);
 
 380                 } catch (CoreException e) {
 
 381                 PHPDebugCorePlugin.log (e);
 
 389 //      public void phpLoopNotify () {
 
 390 //              phpLoop.notifyWait ();
 
 396         public void startPHPLoop () {
 
 397                 phpLoop = new PHPLoop ();                                                                       // Create a DBG communication loop object
 
 399                 phpLoop.start ();                                                                                       // And start the communication loop
 
 405         public void resume () {
 
 407                 DBGInt.continueExecution();
 
 408                 phpLoop.notifyWait();
 
 409                 } catch (IOException e) {
 
 410                 PHPeclipsePlugin.log("Debugging session ended.", e);
 
 418         public void pause () {
 
 420                 if (null != DBGInt) {
 
 421                                 DBGInt.pauseExecution();
 
 424                         // TODO Make sure the Suspend action is grayed out
 
 425                         // when DBGInt is null
 
 427                 } catch (IOException e) {
 
 428                 PHPDebugCorePlugin.log (e);
 
 436         protected PHPDebugTarget getDebugTarget() {
 
 441          * Is called by the DebuggerRunner
 
 445         public void setDebugTarget (PHPDebugTarget debugTarget) {
 
 446                 this.debugTarget = debugTarget;
 
 447                 debugTarget.setPHPDBGProxy(this);
 
 451          * This method is called by a stackframe.
 
 452          * It reads the variables from PHP via DBG
 
 454          * @param frame The stackframe which wants the variables.
 
 455          * @return      The list of variables for this stackframe.
 
 457         public Vector readVariables (PHPStackFrame frame) {
 
 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 ());
 
 475         public PHPVariable[] eval (PHPStackFrame frame, String evalString) {
 
 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());
 
 488         public void readStepOverEnd (PHPStackFrame stackFrame) {
 
 491                 phpLoop.notifyWait();
 
 492                 } catch (Exception e) {
 
 493                 PHPDebugCorePlugin.log(e);
 
 497         public void readStepReturnEnd (PHPStackFrame stackFrame) {
 
 500                     phpLoop.notifyWait();
 
 501                 } catch (Exception e) {
 
 502                 PHPDebugCorePlugin.log(e);
 
 506         public void readStepIntoEnd (PHPStackFrame stackFrame) {
 
 509                 phpLoop.notifyWait();
 
 510                 } catch (Exception e) {
 
 511                 PHPDebugCorePlugin.log(e);
 
 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; //}
 
 522         public void closeSocket() throws IOException {
 
 523                 if (socket != null) {
 
 528         public void closeServerSocket() throws IOException {
 
 529                 if (server != null) {
 
 534         public int getPort() {
 
 542         class PHPLoop extends Thread {
 
 543                 private boolean shouldStop;
 
 547                 this.setName ("PHPDebuggerLoop");
 
 553                 public synchronized void setShouldStop () {
 
 554                         shouldStop = true;                                                                                      // The run loop should stop
 
 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);
 
 570                 public synchronized void notifyWait () {
 
 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;
 
 587                         //                              synchronized (this) {
 
 591                         PHPMainThread = new PHPThread (getDebugTarget (), getPort ());
 
 592                         PHPMainThread.setName ("Thread [main]");
 
 594                         //                              while ((getDebugTarget() == null) && (timeout < 100)) {
 
 598                         // Be sure debug target is set
 
 599                         //                              PHPMainThread.setDebugTarget(getDebugTarget());
 
 601                                 getDebugTarget ().addThread (PHPMainThread);
 
 603                         //System.out.println("Waiting for breakpoints.");
 
 605                         while (!shouldStop) {                                                           // As long as nobody will stop us
 
 606                                 newconnect = true;                              // The first time
 
 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);
 
 618                                 if (newconnect) {                                                               // Is it just after a new connection
 
 619                                         if (DBGInt == null) {                                           // Do we have a DBG interface?
 
 620                                         server.setSoTimeout(1);                                 // ???
 
 623                                         newDBGInt = new PHPDBGInterface (getReader (newSocket),                 // Create a new interface
 
 624                                                                                                                  newSocket.getOutputStream (),
 
 626                                         newDBGInt.waitResponse (1000);                          // Wait for the initial DBG response
 
 627                                         newDBGInt.flushAllPackets ();               // Read and process the DBG response
 
 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
 
 637                                                         catch (IOException e) {
 
 638                                                 PHPDebugCorePlugin.log (e);
 
 644                                         DBGInt.continueExecution ();                    // Notify DBG that PHP should continue
 
 647                                         newDBGInt.continueExecution ();                         // Notify DBG that PHP should continue
 
 652                                 if (DBGInt.waitResponse (interval)) {                                           // Wait for a DBG response (200 ms)
 
 653                                         DBGInt.flushAllPackets ();                                                              // If we got something, read and process it
 
 655                                         if (DBGInt.BPUnderHit != 0) {                                                   // ???
 
 656                                         StackList = DBGInt.getStackList ();                 // Get the stack list from DBGInterface
 
 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
 
 662                                                         if (DBGInt.getModByNo (StackList[i].getModNo ()).equals ("")) {
 
 663                                                                 DBGInt.getSourceTree ();
 
 666                                                         StackList[i].setFile (DBGInt.getModByNo (StackList[i].getModNo ()));
 
 669                                                 PHPMainThread.setStackFrames (StackList);
 
 672                                         PHPMainThread.suspend ();                             // Fire debug event
 
 674                                             synchronized (this) {
 
 681                                         if (PHPMainThread.isTerminated ()) {
 
 684                                             break;                                                // Go for terminating the thread
 
 687                                         if (PHPMainThread.isTerminated () ||
 
 688                                                 getDebugTarget ().getProcess ().isTerminated ()) {
 
 691                                         break;                                                // Go for terminating the thread
 
 695                 } catch (Exception ex) {
 
 696                         PHPDebugCorePlugin.log (ex);
 
 697                         System.out.println (ex);
 
 700                                 getDebugTarget ().terminate ();
 
 702                                 closeServerSocket ();
 
 703                         } catch (IOException e) {
 
 704                                 PHPDebugCorePlugin.log (e);
 
 709                         //System.out.println("Socket loop finished.");