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.debug.core.breakpoints.PHPLineBreakpoint;
21 import net.sourceforge.phpdt.internal.debug.core.model.PHPDebugTarget;
22 import net.sourceforge.phpdt.internal.debug.core.model.PHPStackFrame;
23 import net.sourceforge.phpdt.internal.debug.core.model.PHPThread;
24 import net.sourceforge.phpdt.internal.debug.core.model.PHPVariable;
25 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.core.runtime.IPath;
29 import org.eclipse.core.runtime.Path;
30 import org.eclipse.debug.core.DebugException;
31 import org.eclipse.debug.core.DebugPlugin;
32 import org.eclipse.debug.core.model.IBreakpoint;
34 public class PHPDBGProxy {
36 private ServerSocket server = null;
37 private BufferedReader reader = null;
38 private PHPDBGInterface DBGInt = null; // The DBG interface which is linked with the proxy
39 private PHPDebugTarget debugTarget = null;
40 private PHPDBGProxy thisProxy = null;
41 private PHPLoop phpLoop;
42 private PHPThread PHPMainThread;
43 private Socket socket;
45 private boolean remote;
46 private boolean pathtranslation;
48 private IPath remoteSourcePath;
52 public PHPDBGProxy () {
58 * @param remoteSourcePath
59 * @param pathTranslate
62 public PHPDBGProxy (boolean remote, String remoteSourcePath, boolean pathTranslate, Map paths) {
65 this.remoteSourcePath = new Path (remoteSourcePath);
67 this.pathtranslation = pathTranslate;
73 public void start () {
74 createServerSocket (); // Create a server socket for communicatio with DBG
76 this.startPHPLoop (); //
83 phpLoop.setShouldStop (); // Notify the thread's 'run loop' to stop
85 if (DBGInt != null) { // If we have a DBG interface linked with this proxy
86 DBGInt.setShouldStop (); // Notify the DBG interface to stop the waiting for response
89 if (!remote) { // If it's not a remote proxy session
91 getDebugTarget ().getProcess ().terminate (); //
92 } catch (DebugException e) {
97 phpLoop.notifyWait ();
101 * TODO Is this method called from anywhere?
103 * Returns a already created server socket, or
104 * creates a server socket if none exists, and
105 * returns the newly created one.
107 * @return A server socket
109 protected ServerSocket getServerSocket () throws IOException {
110 if (server == null) { // Do we have already a server socket
111 createServerSocket (); // No, then create one
114 return server; // Return the server socket
119 * TODO The example for setting up DBG within PHP.ini shows ports from 10000 to 10016 ???
120 * if my interpretation is correct.
121 * How can we find the correct DBG port?
123 protected void createServerSocket () {
124 port = SocketUtil.findUnusedLocalPort ("localhost", 10001, 10101); // Get the first free port in the range from 10001 to 10101
127 if (port == -1) { // Did we get a free port?
128 PHPDebugCorePlugin.log (5, "Cannot find free port!!!!"); // No, output a error message
130 return; // And return
133 if (server == null) { // If there is no server socket yet
134 server = new ServerSocket (port); // create a server socket for the free port
135 //System.out.println("ServerSocket on port: " + port);
137 } catch (IOException e) {
139 PHPDebugCorePlugin.log (e);
147 public Socket getSocket () throws IOException {
148 return socket; // Return the socket
152 * Set the DBG interface which is linked to this proxy
154 * @paran DBGInt The DGB interface which is linked with this proxy
156 protected void setDBGInterface (PHPDBGInterface DBGInt) {
157 this.DBGInt = DBGInt;
161 * Give back a buffered input stream for the socket which is
162 * linked with this proxy
164 public BufferedReader getReader () throws IOException {
165 if (reader == null) { // Do we already have a buffered input stream
166 reader = new BufferedReader (new InputStreamReader (this.getSocket ().getInputStream (),
170 return reader; // Return the buffered input stream
176 public BufferedReader getReader (Socket socket) throws IOException {
177 if (socket != null) { // Is a socket provided
178 return new BufferedReader (new InputStreamReader (socket.getInputStream (),
179 "ISO8859_1")); // Then create a buffered input stream
182 return null; // Without a socket we can't create a input stream
188 * @return The output stream for this proxy's socket
190 public OutputStream getOutputStream () throws IOException {
191 return this.getSocket ().getOutputStream ();
197 protected void setBreakPoints () throws IOException, CoreException {
198 IBreakpoint[] breakpoints = DebugPlugin.getDefault ().getBreakpointManager ().getBreakpoints ();
200 for (int i = 0; i < breakpoints.length; i++) {
201 if (breakpoints[i].isEnabled ()) {
202 addBreakpoint (breakpoints[i]);
210 private String MapPath (PHPLineBreakpoint phpLBP) {
218 filename = phpLBP.getMarker().getResource().getProjectRelativePath();
219 filename = remoteSourcePath.append (filename);
221 filename = phpLBP.getMarker().getResource().getLocation();
224 String path = filename.toOSString();
226 if ((pathmap != null) && remote) {
227 java.util.Iterator i = pathmap.keySet().iterator();
229 while (i.hasNext()) {
230 String k = (String) i.next();
231 if (path.startsWith(k)) {
232 path = pathmap.get(k) + path.substring(k.length());
238 if (remoteSourcePath.isEmpty ()) {
239 if ((pathmap != null) && remote) {
240 java.util.Iterator iterator = pathmap.keySet().iterator();
242 while (iterator.hasNext ()) {
243 local = (String) iterator.next (); // Get the local/client side path of the mapping
244 remotePath = new Path ((String) pathmap.get (local)); // Get the remote/server side path of the mapping
245 localPath = new Path (local); // Get the remote/server side path of the mapping
247 if (localPath.isPrefixOf (filename)) { // Starts the remote/server side file path with the remote/server side mapping path
248 // dann prefix abhängen und den remote path davorhägen
249 newpath = filename.removeFirstSegments (localPath.matchingFirstSegments (filename));
250 newpath = remotePath.append (newpath);
251 path = newpath.toString ();
253 if (path.substring (0, 1).equals ("/")) {
254 path = path.replace ('\\', '/');
257 path = path.replace ('/', '\\');
266 if (pathtranslation && remote) {
267 if (remoteSourcePath.toString ().substring (0, 1).equals ("/")) {
268 path = path.replace ('\\', '/');
271 path = path.replace ('/', '\\');
282 public void addBreakpoint (IBreakpoint breakpoint) {
283 if (DBGInt == null) {
290 PHPLineBreakpoint phpLBP;
292 if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier()) {
293 phpLBP = (PHPLineBreakpoint) breakpoint;
295 // bpNo= DBGInt.addBreakpoint(phpLBP.getMarker().getResource().getLocation().toOSString(), phpLBP.getLineNumber());
296 if (phpLBP.isConditionEnabled ()) {
297 bpNo = DBGInt.addBreakpoint (MapPath(phpLBP),
298 phpLBP.getLineNumber(),
299 phpLBP.getHitCount(),
300 phpLBP.getCondition ());
303 bpNo = DBGInt.addBreakpoint (MapPath(phpLBP),
304 phpLBP.getLineNumber(),
305 phpLBP.getHitCount(),
309 phpLBP.setDBGBpNo(bpNo);
311 } catch (IOException e) {
312 PHPDebugCorePlugin.log(e);
314 } catch (CoreException e) {
315 PHPDebugCorePlugin.log(e);
323 public void removeBreakpoint (IBreakpoint breakpoint) {
324 if (DBGInt == null) {
329 PHPLineBreakpoint phpLBP;
331 if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier ()) {
332 phpLBP = (PHPLineBreakpoint) breakpoint;
334 // bpNo= DBGInt.addBreakpoint(filename.toOSString(), phpLBP.getLineNumber());
336 DBGInt.removeBreakpoint(MapPath(phpLBP), phpLBP.getLineNumber(), phpLBP.getDBGBpNo());
338 } catch (IOException e) {
339 PHPDebugCorePlugin.log (e);
341 } catch (CoreException e) {
342 PHPDebugCorePlugin.log (e);
350 public void phpLoopNotify () {
351 phpLoop.notifyWait ();
357 public void startPHPLoop () {
358 phpLoop = new PHPLoop (); // Create a DBG communication loop object
360 phpLoop.start (); // And start the communication loop
366 public void resume () {
368 DBGInt.continueExecution();
369 phpLoop.notifyWait();
370 } catch (IOException e) {
371 PHPeclipsePlugin.log("Debugging session ended.", e);
379 public void pause () {
381 if (null != DBGInt) {
382 DBGInt.pauseExecution();
385 // TODO Make sure the Suspend action is grayed out
386 // when DBGInt is null
388 } catch (IOException e) {
389 PHPDebugCorePlugin.log (e);
397 protected PHPDebugTarget getDebugTarget() {
405 public void setDebugTarget (PHPDebugTarget debugTarget) {
406 this.debugTarget = debugTarget;
407 debugTarget.setPHPDBGProxy(this);
411 * This method is called by a stackframe.
412 * It reads the variables from PHP via DBG
414 * @param frame The stackframe which wants the variables.
415 * @return The list of variables for this stackframe.
417 public Vector readVariables (PHPStackFrame frame) {
419 return DBGInt.getVariables (frame); // Get the variables from DBG interface
420 } catch (IOException ioex) {
421 ioex.printStackTrace ();
422 throw new RuntimeException (ioex.getMessage ());
423 } catch (DebugException ex) {
424 ex.printStackTrace ();
425 throw new RuntimeException (ex.getMessage ());
435 public PHPVariable[] eval (PHPStackFrame frame, String evalString) {
437 return DBGInt.evalBlock (frame, evalString);
438 //return DBGInt.getVariables(frame);
439 } catch (IOException ioex) {
440 ioex.printStackTrace();
441 throw new RuntimeException(ioex.getMessage());
442 } catch (DebugException ex) {
443 ex.printStackTrace();
444 throw new RuntimeException(ex.getMessage());
448 public void readStepOverEnd (PHPStackFrame stackFrame) {
451 phpLoop.notifyWait();
452 } catch (Exception e) {
453 PHPDebugCorePlugin.log(e);
457 public void readStepReturnEnd (PHPStackFrame stackFrame) {
460 phpLoop.notifyWait();
461 } catch (Exception e) {
462 PHPDebugCorePlugin.log(e);
466 public void readStepIntoEnd (PHPStackFrame stackFrame) {
469 phpLoop.notifyWait();
470 } catch (Exception e) {
471 PHPDebugCorePlugin.log(e);
476 * public PHPStackFrame[] readFrames(PHPThread thread) { //try { //this.println("th " + thread.getId() + " ; f "); //return new
477 * FramesReader(getMultiReaderStrategy()).readFrames(thread); return null; //} catch (IOException e) { //
478 * PHPDebugCorePlugin.log(e); // return null; //}
482 public void closeSocket() throws IOException {
483 if (socket != null) {
488 public void closeServerSocket() throws IOException {
489 if (server != null) {
494 public int getPort() {
502 class PHPLoop extends Thread {
503 private boolean shouldStop;
507 this.setName ("PHPDebuggerLoop");
513 public synchronized void setShouldStop () {
514 shouldStop = true; // The run loop should stop
517 // If the loop thread is blocked on the server socket,
518 // forcibly unblock it to avoid leaking the thread,
519 // the socket and the port
520 closeServerSocket ();
521 } catch (IOException x) {
522 // Log this as a warning?
523 PHPDebugCorePlugin.log (x);
530 public synchronized void notifyWait () {
542 long interval = 200; // Wait 200 ms maximum for a DBG response
543 boolean newconnect = false; //
544 Socket newSocket = null;
545 PHPStackFrame[] StackList;
546 PHPDBGInterface newDBGInt;
548 // synchronized (this) {
552 PHPMainThread = new PHPThread (getDebugTarget (), getPort ());
553 PHPMainThread.setName ("Thread [main]");
556 // while ((getDebugTarget() == null) && (timeout < 100)) {
560 // Be sure debug target is set
561 // PHPMainThread.setDebugTarget(getDebugTarget());
563 getDebugTarget ().addThread (PHPMainThread);
565 //System.out.println("Waiting for breakpoints.");
567 while (!shouldStop) { // As long as nobody will stop us
568 newconnect = true; // The first time
571 newSocket = server.accept(); // Waits until DBG want to connect
572 //System.out.println("Accepted! : " + socket.toString());
573 } catch (SocketTimeoutException e) {
574 newconnect = false; // No one wants to connect (connection already done)
575 } catch (IOException e) {
576 PHPDebugCorePlugin.log(e);
580 if (newconnect) { // Is it just after a new connection
581 if (DBGInt == null) { // Do we have a DBG interface?
582 server.setSoTimeout(1); // ???
585 newDBGInt = new PHPDBGInterface (getReader (newSocket), // Create a new interface
586 newSocket.getOutputStream (),
588 newDBGInt.waitResponse (1000); // Wait for the initial DBG response
589 newDBGInt.flushAllPackets (); // Read and process the DBG response
591 // Check version and session ID
592 if ((DBGInt == null) || // If we have no interface
593 (DBGInt.getSID () == newDBGInt.getSID ())) {// or the new session ID is different to the old one
594 DBGInt = newDBGInt; // Set the new interface as current one
599 catch (IOException e) {
600 PHPDebugCorePlugin.log (e);
606 DBGInt.continueExecution (); // Notify DBG that PHP should continue
609 newDBGInt.continueExecution (); // Notify DBG that PHP should continue
614 if (DBGInt.waitResponse (interval)) { // Wait for a DBG response (200 ms)
615 DBGInt.flushAllPackets (); // If we got something, read and process it
617 if (DBGInt.BPUnderHit != 0) { // ???
618 StackList = DBGInt.getStackList (); // Get the stack list from DBGInterface
620 if (StackList.length > 0) { // If there is something in stack list
621 for (i = 0; i < StackList.length; i++) { // For all stack list
622 StackList[i].setThread (PHPMainThread); // Set the PHPTread for all PHPStackFrames
624 if (DBGInt.getModByNo (StackList[i].getModNo ()).equals ("")) {
625 DBGInt.getSourceTree ();
628 StackList[i].setFile (DBGInt.getModByNo (StackList[i].getModNo ()));
631 PHPMainThread.setStackFrames (StackList);
634 PHPMainThread.suspend (); // Fire debug event
636 synchronized (this) {
643 if (PHPMainThread.isTerminated ()) {
646 break; // Go for terminating the thread
649 if (PHPMainThread.isTerminated () ||
650 getDebugTarget ().getProcess ().isTerminated ()) {
653 break; // Go for terminating the thread
657 } catch (Exception ex) {
658 PHPDebugCorePlugin.log (ex);
659 System.out.println (ex);
662 getDebugTarget ().terminate ();
664 closeServerSocket ();
665 } catch (IOException e) {
666 PHPDebugCorePlugin.log (e);
671 //System.out.println("Socket loop finished.");