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;
19 import net.sourceforge.phpdt.internal.debug.core.breakpoints.PHPLineBreakpoint;
20 import net.sourceforge.phpdt.internal.debug.core.model.PHPDebugTarget;
21 import net.sourceforge.phpdt.internal.debug.core.model.PHPStackFrame;
22 import net.sourceforge.phpdt.internal.debug.core.model.PHPThread;
23 import net.sourceforge.phpdt.internal.debug.core.model.PHPVariable;
24 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
26 import org.eclipse.core.runtime.CoreException;
27 import org.eclipse.core.runtime.IPath;
28 import org.eclipse.core.runtime.Path;
29 import org.eclipse.debug.core.DebugException;
30 import org.eclipse.debug.core.DebugPlugin;
31 import org.eclipse.debug.core.model.IBreakpoint;
33 public class PHPDBGProxy {
35 private ServerSocket server = null;
36 private BufferedReader reader = null;
37 private PHPDBGInterface DBGInt = null; // The DBG interface which is linked with the proxy
38 private PHPDebugTarget debugTarget = null;
39 private PHPDBGProxy thisProxy = null;
40 private PHPLoop phpLoop;
41 private PHPThread PHPMainThread;
42 private Socket socket;
44 private boolean remote;
45 private boolean pathtranslation;
47 private IPath remoteSourcePath;
51 public PHPDBGProxy () {
57 * @param remoteSourcePath
58 * @param pathTranslate
61 public PHPDBGProxy (boolean remote, String remoteSourcePath, boolean pathTranslate, Map paths) {
64 this.remoteSourcePath = new Path (remoteSourcePath);
66 this.pathtranslation = pathTranslate;
72 public void start () {
73 createServerSocket (); // Create a server socket for communicatio with DBG
75 this.startPHPLoop (); //
82 phpLoop.setShouldStop (); // Notify the thread's 'run loop' to stop
84 if (DBGInt != null) { // If we have a DBG interface linked with this proxy
85 DBGInt.setShouldStop (); // Notify the DBG interface to stop the waiting for response
88 if (!remote) { // If it's not a remote proxy session
90 getDebugTarget ().getProcess ().terminate (); //
91 } catch (DebugException e) {
96 phpLoop.notifyWait ();
100 * TODO Is this method called from anywhere?
102 * Returns a already created server socket, or
103 * creates a server socket if none exists, and
104 * returns the newly created one.
106 * @return A server socket
108 protected ServerSocket getServerSocket () throws IOException {
109 if (server == null) { // Do we have already a server socket
110 createServerSocket (); // No, then create one
113 return server; // Return the server socket
118 * TODO The example for setting up DBG within PHP.ini shows ports from 10000 to 10016 ???
119 * if my interpretation is correct.
120 * How can we find the correct DBG port?
122 protected void createServerSocket () {
123 port = SocketUtil.findUnusedLocalPort ("localhost", 10001, 10101); // Get the first free port in the range from 10001 to 10101
126 if (port == -1) { // Did we get a free port?
127 PHPDebugCorePlugin.log (5, "Cannot find free port!!!!"); // No, output a error message
129 return; // And return
132 if (server == null) { // If there is no server socket yet
133 server = new ServerSocket (port); // create a server socket for the free port
134 //System.out.println("ServerSocket on port: " + port);
136 } catch (IOException e) {
138 PHPDebugCorePlugin.log (e);
146 public Socket getSocket () throws IOException {
147 return socket; // Return the socket
151 * Set the DBG interface which is linked to this proxy
153 * @paran DBGInt The DGB interface which is linked with this proxy
155 protected void setDBGInterface (PHPDBGInterface DBGInt) {
156 this.DBGInt = DBGInt;
160 * Give back a buffered input stream for the socket which is
161 * linked with this proxy
163 public BufferedReader getReader () throws IOException {
164 if (reader == null) { // Do we already have a buffered input stream
165 reader = new BufferedReader (new InputStreamReader (this.getSocket ().getInputStream (),
169 return reader; // Return the buffered input stream
175 public BufferedReader getReader (Socket socket) throws IOException {
176 if (socket != null) { // Is a socket provided
177 return new BufferedReader (new InputStreamReader (socket.getInputStream (),
178 "ISO8859_1")); // Then create a buffered input stream
181 return null; // Without a socket we can't create a input stream
187 * @return The output stream for this proxy's socket
189 public OutputStream getOutputStream () throws IOException {
190 return this.getSocket ().getOutputStream ();
196 protected void setBreakPoints () throws IOException, CoreException {
197 IBreakpoint[] breakpoints = DebugPlugin.getDefault ().getBreakpointManager ().getBreakpoints ();
199 for (int i = 0; i < breakpoints.length; i++) {
200 if (breakpoints[i].isEnabled ()) {
201 addBreakpoint (breakpoints[i]);
209 private String MapPath (PHPLineBreakpoint phpLBP) {
213 filename = phpLBP.getMarker().getResource().getProjectRelativePath();
214 filename = remoteSourcePath.append (filename);
216 filename = phpLBP.getMarker().getResource().getLocation();
219 String path = filename.toOSString();
221 if ((pathmap != null) && remote) {
222 java.util.Iterator i = pathmap.keySet().iterator();
224 while (i.hasNext()) {
225 String k = (String) i.next();
226 if (path.startsWith(k)) {
227 path = pathmap.get(k) + path.substring(k.length());
233 if (pathtranslation && remote) {
234 if (remoteSourcePath.toString ().substring (0, 1).equals ("/")) {
235 path = path.replace ('\\', '/');
238 path = path.replace ('/', '\\');
248 public void addBreakpoint (IBreakpoint breakpoint) {
249 if (DBGInt == null) {
256 PHPLineBreakpoint phpLBP;
258 if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier()) {
259 phpLBP = (PHPLineBreakpoint) breakpoint;
261 // bpNo= DBGInt.addBreakpoint(phpLBP.getMarker().getResource().getLocation().toOSString(), phpLBP.getLineNumber());
263 bpNo = DBGInt.addBreakpoint(MapPath(phpLBP), phpLBP.getLineNumber());
264 phpLBP.setDBGBpNo(bpNo);
266 } catch (IOException e) {
267 PHPDebugCorePlugin.log(e);
269 } catch (CoreException e) {
270 PHPDebugCorePlugin.log(e);
278 public void removeBreakpoint (IBreakpoint breakpoint) {
279 if (DBGInt == null) {
284 PHPLineBreakpoint phpLBP;
286 if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier ()) {
287 phpLBP = (PHPLineBreakpoint) breakpoint;
289 // bpNo= DBGInt.addBreakpoint(filename.toOSString(), phpLBP.getLineNumber());
291 DBGInt.removeBreakpoint(MapPath(phpLBP), phpLBP.getLineNumber(), phpLBP.getDBGBpNo());
293 } catch (IOException e) {
294 PHPDebugCorePlugin.log (e);
296 } catch (CoreException e) {
297 PHPDebugCorePlugin.log (e);
305 public void phpLoopNotify () {
306 phpLoop.notifyWait ();
312 public void startPHPLoop () {
313 phpLoop = new PHPLoop (); // Create a DBG communication loop object
315 phpLoop.start (); // And start the communication loop
321 public void resume () {
323 DBGInt.continueExecution();
324 phpLoop.notifyWait();
325 } catch (IOException e) {
326 PHPeclipsePlugin.log("Debugging session ended.", e);
334 public void pause () {
336 if (null != DBGInt) {
337 DBGInt.pauseExecution();
340 // TODO Make sure the Suspend action is grayed out
341 // when DBGInt is null
343 } catch (IOException e) {
344 PHPDebugCorePlugin.log (e);
352 protected PHPDebugTarget getDebugTarget() {
360 public void setDebugTarget (PHPDebugTarget debugTarget) {
361 this.debugTarget = debugTarget;
362 debugTarget.setPHPDBGProxy(this);
366 * This method is called by a stackframe.
367 * It reads the variables from PHP via DBG
369 * @param frame The stackframe which wants the variables.
370 * @return The array of variables for this stackframe.
372 public PHPVariable[] readVariables (PHPStackFrame frame) {
374 return DBGInt.getVariables (frame); // Get the variables from DBG interface
375 } catch (IOException ioex) {
376 ioex.printStackTrace ();
377 throw new RuntimeException (ioex.getMessage ());
378 } catch (DebugException ex) {
379 ex.printStackTrace ();
380 throw new RuntimeException (ex.getMessage ());
390 public PHPVariable[] eval (PHPStackFrame frame, String evalString) {
392 return DBGInt.evalBlock (frame, evalString);
393 //return DBGInt.getVariables(frame);
394 } catch (IOException ioex) {
395 ioex.printStackTrace();
396 throw new RuntimeException(ioex.getMessage());
397 } catch (DebugException ex) {
398 ex.printStackTrace();
399 throw new RuntimeException(ex.getMessage());
403 public void readStepOverEnd (PHPStackFrame stackFrame) {
406 phpLoop.notifyWait();
407 } catch (Exception e) {
408 PHPDebugCorePlugin.log(e);
412 public void readStepReturnEnd (PHPStackFrame stackFrame) {
415 phpLoop.notifyWait();
416 } catch (Exception e) {
417 PHPDebugCorePlugin.log(e);
421 public void readStepIntoEnd (PHPStackFrame stackFrame) {
424 phpLoop.notifyWait();
425 } catch (Exception e) {
426 PHPDebugCorePlugin.log(e);
431 * public PHPStackFrame[] readFrames(PHPThread thread) { //try { //this.println("th " + thread.getId() + " ; f "); //return new
432 * FramesReader(getMultiReaderStrategy()).readFrames(thread); return null; //} catch (IOException e) { //
433 * PHPDebugCorePlugin.log(e); // return null; //}
437 public void closeSocket() throws IOException {
438 if (socket != null) {
443 public void closeServerSocket() throws IOException {
444 if (server != null) {
449 public int getPort() {
457 class PHPLoop extends Thread {
458 private boolean shouldStop;
462 this.setName ("PHPDebuggerLoop");
468 public synchronized void setShouldStop () {
469 shouldStop = true; // The run loop should stop
472 // If the loop thread is blocked on the server socket,
473 // forcibly unblock it to avoid leaking the thread,
474 // the socket and the port
475 closeServerSocket ();
476 } catch (IOException x) {
477 // Log this as a warning?
478 PHPDebugCorePlugin.log (x);
485 public synchronized void notifyWait () {
497 long interval = 200; // Wait 200 ms maximum for a DBG response
498 boolean newconnect = false; //
499 Socket newSocket = null;
500 PHPStackFrame[] StackList;
501 PHPDBGInterface newDBGInt;
503 // synchronized (this) {
507 PHPMainThread = new PHPThread (getDebugTarget (), getPort ());
508 PHPMainThread.setName ("Thread [main]");
511 // while ((getDebugTarget() == null) && (timeout < 100)) {
515 // Be sure debug target is set
516 // PHPMainThread.setDebugTarget(getDebugTarget());
518 getDebugTarget ().addThread (PHPMainThread);
520 //System.out.println("Waiting for breakpoints.");
522 while (!shouldStop) { // As long as nobody will stop us
523 newconnect = true; // The first time
526 newSocket = server.accept(); // Waits until DBG want to connect
527 //System.out.println("Accepted! : " + socket.toString());
528 } catch (SocketTimeoutException e) {
529 newconnect = false; // No one wants to connect (connection already done)
530 } catch (IOException e) {
531 PHPDebugCorePlugin.log(e);
535 if (newconnect) { // Is it just after a new connection
536 if (DBGInt == null) { // Do we have a DBG interface?
537 server.setSoTimeout(1); // ???
540 newDBGInt = new PHPDBGInterface (getReader (newSocket), // Create a new interface
541 newSocket.getOutputStream (),
543 newDBGInt.waitResponse (1000); // Wait for the initial DBG response
544 newDBGInt.flushAllPackets (); // Read and process the DBG response
546 // Check version and session ID
547 if ((DBGInt == null) || // If we have no interface
548 (DBGInt.getSID () == newDBGInt.getSID ())) {// or the new session ID is different to the old one
549 DBGInt = newDBGInt; // Set the new interface as current one
554 catch (IOException e) {
555 PHPDebugCorePlugin.log (e);
561 DBGInt.continueExecution (); // Notify DBG that PHP should continue
564 newDBGInt.continueExecution (); // Notify DBG that PHP should continue
569 if (DBGInt.waitResponse (interval)) { // Wait for a DBG response (200 ms)
570 DBGInt.flushAllPackets (); // If we got something, read and process it
572 if (DBGInt.BPUnderHit != 0) { // ???
573 StackList = DBGInt.getStackList (); // Get the stack list from DBGInterface
575 if (StackList.length > 0) { // If there is something in stack list
576 for (i = 0; i < StackList.length; i++) { // For all stack list
577 StackList[i].setThread (PHPMainThread); // Set the PHPTread for all PHPStackFrames
579 if (DBGInt.getModByNo (StackList[i].getModNo ()).equals ("")) {
580 DBGInt.getSourceTree ();
583 StackList[i].setFile (DBGInt.getModByNo (StackList[i].getModNo ()));
586 PHPMainThread.setStackFrames (StackList);
589 PHPMainThread.suspend (); // Fire debug event
591 synchronized (this) {
598 if (PHPMainThread.isTerminated ()) {
601 break; // Go for terminating the thread
604 if (PHPMainThread.isTerminated () ||
605 getDebugTarget ().getProcess ().isTerminated ()) {
608 break; // Go for terminating the thread
612 } catch (Exception ex) {
613 PHPDebugCorePlugin.log (ex);
614 System.out.println (ex);
617 getDebugTarget ().terminate ();
619 closeServerSocket ();
620 } catch (IOException e) {
621 PHPDebugCorePlugin.log (e);
626 //System.out.println("Socket loop finished.");