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;
51 private boolean bRelaunch; // Relaunch the debugger after script termination
53 private IPath remoteSourcePath;
57 public PHPDBGProxy () {
63 * Clean up the view, but leave the Debug session running.
66 public void updateView () {
73 catch (IOException e) {
76 getDebugTarget ().updateThreads (PHPMainThread);
81 * @param remoteSourcePath
82 * @param pathTranslate
86 public PHPDBGProxy (boolean remote, String remoteSourcePath, boolean pathTranslate, Map paths, boolean bRelaunchOnScriptTermination) {
89 this.remoteSourcePath = new Path (remoteSourcePath);
91 this.pathtranslation = pathTranslate;
92 this.bRelaunch = bRelaunchOnScriptTermination;
98 public void start () {
99 createServerSocket (); // Create a server socket for communicatio with DBG
101 this.startPHPLoop (); //
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
113 // if (!remote) { // If it's not a remote proxy session
115 // getDebugTarget ().getProcess ().terminate (); //
116 // } catch (DebugException e) {
117 // e.printStackTrace ();
121 phpLoop.notifyWait ();
124 public void setTerminated () {
126 PHPMainThread.terminate ();
128 catch (DebugException e) {
133 * TODO Is this method called from anywhere?
135 * Returns a already created server socket, or
136 * creates a server socket if none exists, and
137 * returns the newly created one.
139 * @return A server socket
141 // protected ServerSocket getServerSocket () throws IOException {
142 // if (server == null) { // Do we have already a server socket
143 // createServerSocket (); // No, then create one
146 // return server; // Return the server socket
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.
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.
158 * @note: The free dbg version doesn't allow to set the appropriate port within php.ini!
162 protected void createServerSocket () {
164 port = SocketUtil.findUnusedLocalPort ("localhost", 10001, 10101); // Get the first free port in the range from 10001 to 10101
167 port = SocketUtil.findUnusedLocalPort ("localhost", 7869, 7869); // Get the first free port in the range from 7869 to 7869
170 if (port == -1) { // Did we get a free port?
171 PHPDebugCorePlugin.log (5, "Cannot find free port!!!!"); // No, output a error message
173 return; // And return
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);
180 } catch (IOException e) {
181 PHPDebugCorePlugin.log (e);
189 public Socket getSocket () throws IOException {
190 return socket; // Return the socket
194 * Set the DBG interface which is linked to this proxy
196 * @paran DBGInt The DGB interface which is linked with this proxy
198 // protected void setDBGInterface (PHPDBGInterface DBGInt) {
199 // this.DBGInt = DBGInt;
203 * Get the DBG interface which is linked to this proxy
205 * @paran DBGInt The DGB interface which is linked with this proxy
207 public PHPDBGInterface getDBGInterface () {
212 * Give back a buffered input stream for the socket which is
213 * linked with this proxy
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 (),
221 // return reader; // Return the buffered input stream
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
233 return null; // Without a socket we can't create a input stream
239 * @return The output stream for this proxy's socket
241 public OutputStream getOutputStream () throws IOException {
242 return this.getSocket ().getOutputStream ();
248 protected void setBreakPoints () throws IOException, CoreException {
249 IBreakpoint[] breakpoints = DebugPlugin.getDefault ().getBreakpointManager ().getBreakpoints ();
251 for (int i = 0; i < breakpoints.length; i++) {
252 if (breakpoints[i].isEnabled ()) {
253 addBreakpoint (breakpoints[i]);
261 private String MapPath (PHPLineBreakpoint phpLBP) {
269 filename = phpLBP.getMarker().getResource().getProjectRelativePath();
270 filename = remoteSourcePath.append (filename);
272 filename = phpLBP.getMarker().getResource().getFullPath();
275 String path = filename.toOSString();
277 if ((pathmap != null) && remote) {
278 java.util.Iterator i = pathmap.keySet().iterator();
280 while (i.hasNext()) {
281 String k = (String) i.next();
282 if (path.startsWith(k)) {
283 path = pathmap.get(k) + path.substring(k.length());
289 if (remoteSourcePath.isEmpty ()) {
290 if ((pathmap != null) && remote) {
291 java.util.Iterator iterator = pathmap.keySet().iterator();
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
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 ();
304 if (path.substring (0, 1).equals ("/")) {
305 path = path.replace ('\\', '/');
308 path = path.replace ('/', '\\');
317 if (pathtranslation && remote) {
318 if (remoteSourcePath.toString ().substring (0, 1).equals ("/")) {
319 path = path.replace ('\\', '/');
322 path = path.replace ('/', '\\');
333 public void addBreakpoint (IBreakpoint breakpoint) {
334 if (DBGInt == null) {
341 PHPLineBreakpoint phpLBP;
343 if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier()) {
344 phpLBP = (PHPLineBreakpoint) breakpoint;
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 ());
354 bpNo = DBGInt.addBreakpoint (MapPath(phpLBP),
355 phpLBP.getLineNumber(),
356 phpLBP.getHitCount(),
360 phpLBP.setDBGBpNo(bpNo);
362 } catch (IOException e) {
363 PHPDebugCorePlugin.log(e);
365 } catch (CoreException e) {
366 PHPDebugCorePlugin.log(e);
374 public void removeBreakpoint (IBreakpoint breakpoint) {
375 if (DBGInt == null) {
380 PHPLineBreakpoint phpLBP;
382 if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier ()) {
383 phpLBP = (PHPLineBreakpoint) breakpoint;
385 // bpNo= DBGInt.addBreakpoint(filename.toOSString(), phpLBP.getLineNumber());
387 DBGInt.removeBreakpoint(MapPath(phpLBP), phpLBP.getLineNumber(), phpLBP.getDBGBpNo());
389 } catch (IOException e) {
390 PHPDebugCorePlugin.log (e);
392 } catch (CoreException e) {
393 PHPDebugCorePlugin.log (e);
401 // public void phpLoopNotify () {
402 // phpLoop.notifyWait ();
408 public void startPHPLoop () {
409 phpLoop = new PHPLoop (); // Create a DBG communication loop object
411 phpLoop.start (); // And start the communication loop
417 public void resume () {
419 DBGInt.continueExecution();
420 phpLoop.notifyWait();
421 } catch (IOException e) {
422 PHPeclipsePlugin.log("Debugging session ended.", e);
430 public void pause () {
432 if (null != DBGInt) {
433 DBGInt.pauseExecution();
436 // TODO Make sure the Suspend action is grayed out
437 // when DBGInt is null
439 } catch (IOException e) {
440 PHPDebugCorePlugin.log (e);
448 protected PHPDebugTarget getDebugTarget() {
453 * Is called by the DebuggerRunner
457 public void setDebugTarget (PHPDebugTarget debugTarget) {
458 this.debugTarget = debugTarget;
459 debugTarget.setPHPDBGProxy(this);
463 * This method is called by a stackframe.
464 * It reads the variables from PHP via DBG
466 * @param frame The stackframe which wants the variables.
467 * @return The list of variables for this stackframe.
469 public Vector readVariables (PHPStackFrame frame) {
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 ());
487 public PHPVariable[] eval (PHPStackFrame frame, String evalString) {
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());
500 public void readStepOverEnd (PHPStackFrame stackFrame) {
502 if (DBGInt.stepOver ()) {
503 phpLoop.notifyWait();
505 } catch (Exception e) {
506 PHPDebugCorePlugin.log(e);
510 public void readStepReturnEnd (PHPStackFrame stackFrame) {
512 if (DBGInt.stepOut ()) {
513 phpLoop.notifyWait();
515 } catch (Exception e) {
516 PHPDebugCorePlugin.log(e);
520 public void readStepIntoEnd (PHPStackFrame stackFrame) {
522 if (DBGInt.stepInto ()) {
523 phpLoop.notifyWait();
525 } catch (Exception e) {
526 PHPDebugCorePlugin.log(e);
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; //}
537 public void closeSocket() throws IOException {
538 if (socket != null) {
543 public void closeServerSocket() throws IOException {
544 if (server != null) {
549 public int getPort() {
557 class PHPLoop extends Thread {
558 private boolean shouldStop;
562 this.setName ("PHPDebuggerLoop");
568 public synchronized void setShouldStop () {
569 shouldStop = true; // The run loop should stop
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);
585 public synchronized void notifyWait () {
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;
602 // synchronized (this) {
606 PHPMainThread = new PHPThread (getDebugTarget (), getPort ());
607 PHPMainThread.setName ("Thread [main]");
609 // while ((getDebugTarget() == null) && (timeout < 100)) {
613 // Be sure debug target is set
614 // PHPMainThread.setDebugTarget(getDebugTarget());
616 getDebugTarget ().addThread (PHPMainThread);
618 //System.out.println("Waiting for breakpoints.");
620 while (!shouldStop) { // As long as nobody will stop us
621 newconnect = true; // The first time
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);
633 if (newconnect) { // Is it just after a new connection
634 if (DBGInt == null) { // Do we have a DBG interface?
635 server.setSoTimeout(1); // ???
638 newDBGInt = new PHPDBGInterface (getReader (newSocket), // The input buffer (frames from dbg)
639 newSocket.getOutputStream (), // The output buffer (frames to dbg)
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
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
653 catch (IOException e) {
654 PHPDebugCorePlugin.log (e);
660 DBGInt.continueExecution (); // Notify DBG that PHP should continue
663 newDBGInt.continueExecution (); // Notify DBG that PHP should continue
668 if (DBGInt.waitResponse (interval)) { // Wait for a DBG response (200 ms)
669 DBGInt.flushAllPackets (); // If we got something, read and process it
671 if (DBGInt.BPUnderHit != 0) { // ???
672 StackList = DBGInt.getStackList (); // Get the stack list from DBGInterface
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
678 if (DBGInt.getModByNo (StackList[i].getModNo ()).equals ("")) {
679 DBGInt.getSourceTree ();
682 StackList[i].setFile (DBGInt.getModByNo (StackList[i].getModNo ()));
685 PHPMainThread.setStackFrames (StackList);
688 PHPMainThread.suspend (); // Fire debug event
690 synchronized (this) {
697 if (PHPMainThread.isTerminated ()) {
700 break; // Go for terminating the thread
703 if (PHPMainThread.isTerminated () ||
704 getDebugTarget ().getProcess ().isTerminated ()) {
707 break; // Go for terminating the thread
711 } catch (Exception ex) {
712 PHPDebugCorePlugin.log (ex);
713 System.out.println (ex);
716 getDebugTarget ().terminate ();
718 closeServerSocket ();
719 } catch (IOException e) {
720 PHPDebugCorePlugin.log (e);
725 //System.out.println("Socket loop finished.");