Small bug fixes for the debugger client.
[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
19 import net.sourceforge.phpdt.internal.debug.core.breakpoints.PHPLineBreakpoint;
20 import net.sourceforge.phpdt.internal.debug.core.model.IPHPDebugTarget;
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;
25
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;
32
33 public class PHPDBGProxy {
34
35   private ServerSocket server = null;
36
37   private Socket socket;
38
39   private BufferedReader reader = null;
40
41   private PHPDBGInterface DBGInt = null;
42
43   private IPHPDebugTarget debugTarget = null;
44
45   private PHPLoop phpLoop;
46
47   private PHPThread PHPMainThread;
48
49   private PHPDBGProxy thisProxy = null;
50
51   private int port;
52
53   private boolean remote;
54
55   private boolean pathtranslation;
56   
57   private Map pathmap;
58   
59   private IPath remoteSourcePath;
60
61   public PHPDBGProxy() {
62     thisProxy = this;
63   }
64
65   public PHPDBGProxy(boolean remote, String remoteSourcePath,boolean pathTranslate,Map paths) {
66     thisProxy = this;
67     this.remote = remote;
68     this.remoteSourcePath = new Path(remoteSourcePath);
69     this.pathmap=paths;
70     this.pathtranslation=pathTranslate;
71   }
72
73   public void start() {
74     createServerSocket();
75     this.startPHPLoop();
76   }
77
78   public void stop() {
79     phpLoop.setShouldStop();
80     if (DBGInt != null)
81       DBGInt.setShouldStop();
82     if (!remote) {
83       try {
84         getDebugTarget().getProcess().terminate();
85       } catch (DebugException e) {
86         e.printStackTrace();
87       }
88     }
89     phpLoop.notifyWait();
90   }
91
92   protected ServerSocket getServerSocket() throws IOException {
93     if (server == null) {
94       createServerSocket();
95     }
96     return server;
97   }
98
99   protected void createServerSocket() {
100     port = SocketUtil.findUnusedLocalPort("localhost", 10001, 10101);
101 //    port = 10001;
102     if (port == -1) {
103       PHPDebugCorePlugin.log(5, "Cannot find free port!!!!");
104       return;
105     }
106     try {
107       if (server == null) {
108         server = new ServerSocket(port);
109         //System.out.println("ServerSocket on port: " + port);
110       }
111     } catch (IOException e) {
112       // IO Error
113       PHPDebugCorePlugin.log(e);
114       stop();
115     }
116   }
117
118   public Socket getSocket() throws IOException {
119     return socket;
120   }
121
122   protected void setDBGInterface(PHPDBGInterface DBGInt) {
123     this.DBGInt = DBGInt;
124   }
125
126   public BufferedReader getReader() throws IOException {
127     if (reader == null) {
128       reader = new BufferedReader(new InputStreamReader(this.getSocket().getInputStream(), "ISO8859_1"));
129     }
130     return reader;
131   }
132
133   public BufferedReader getReader(Socket socket) throws IOException {
134     if (socket != null)
135       return new BufferedReader(new InputStreamReader(socket.getInputStream(), "ISO8859_1"));
136     else
137       return null;
138   }
139
140   public OutputStream getOutputStream() throws IOException {
141     return this.getSocket().getOutputStream();
142   }
143
144   protected void setBreakPoints() throws IOException, CoreException {
145     IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints();
146     for (int i = 0; i < breakpoints.length; i++) {
147       addBreakpoint(breakpoints[i]);
148     }
149   }
150
151   private String MapPath(PHPLineBreakpoint phpLBP) {
152                 IPath filename;
153                 if (remote) {
154                         filename = phpLBP.getMarker().getResource()
155                                         .getProjectRelativePath();
156                         filename = remoteSourcePath.append(filename);
157                 } else
158                         filename = phpLBP.getMarker().getResource().getLocation();
159                 String path = filename.toOSString();
160                 if (pathmap != null && remote) {
161                         java.util.Iterator i = pathmap.keySet().iterator();
162                         while (i.hasNext()) {
163                                 String k = (String) i.next();
164                                 if (path.startsWith(k)) {
165                                         path = pathmap.get(k) + path.substring(k.length());
166                                         break;
167                                 }
168                         }
169                 }
170                 if (pathtranslation && remote) {
171                         if (path.substring(0, 1).equals("/"))
172                                 path = path.replace('\\', '/');
173                         else
174                                 path = path.replace('/', '\\');
175                 }
176                 return path;
177         }
178   
179   public void addBreakpoint(IBreakpoint breakpoint) {
180     if (DBGInt == null)
181       return;
182     int bpNo = 0;
183     try {
184       PHPLineBreakpoint phpLBP;
185       if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier()) {
186         phpLBP = (PHPLineBreakpoint) breakpoint;
187         //                              bpNo= DBGInt.addBreakpoint(phpLBP.getMarker().getResource().getLocation().toOSString(), phpLBP.getLineNumber());
188
189         bpNo = DBGInt.addBreakpoint(MapPath(phpLBP), phpLBP.getLineNumber());
190         phpLBP.setDBGBpNo(bpNo);
191       }
192     } catch (IOException e) {
193       PHPDebugCorePlugin.log(e);
194       stop();
195     } catch (CoreException e) {
196       PHPDebugCorePlugin.log(e);
197       stop();
198     }
199   }
200
201   public void removeBreakpoint(IBreakpoint breakpoint) {
202     if (DBGInt == null)
203       return;
204     try {
205       PHPLineBreakpoint phpLBP;
206       if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier()) {
207         phpLBP = (PHPLineBreakpoint) breakpoint;
208
209         //                                      bpNo= DBGInt.addBreakpoint(filename.toOSString(), phpLBP.getLineNumber());
210         DBGInt.removeBreakpoint(MapPath(phpLBP), phpLBP.getLineNumber(), phpLBP.getDBGBpNo());
211       }
212     } catch (IOException e) {
213       PHPDebugCorePlugin.log(e);
214       stop();
215     } catch (CoreException e) {
216       PHPDebugCorePlugin.log(e);
217       stop();
218     }
219   }
220
221   public void phpLoopNotify() {
222     phpLoop.notifyWait();
223   }
224
225   public void startPHPLoop() {
226     phpLoop = new PHPLoop();
227     phpLoop.start();
228   }
229
230   public void resume() {
231     try {
232       DBGInt.continueExecution();
233       phpLoop.notifyWait();
234     } catch (IOException e) {
235       PHPeclipsePlugin.log("Debugging session ended.", e);
236       stop();
237     }
238   }
239
240   public void pause() {
241     try {
242       if (null != DBGInt)
243         DBGInt.pauseExecution();
244       else {
245         // TODO Make sure the Suspend action is grayed out 
246         // when DBGInt is null
247       }
248     } catch (IOException e) {
249       PHPDebugCorePlugin.log(e);
250       stop();
251     }
252   }
253
254   protected IPHPDebugTarget getDebugTarget() {
255     return debugTarget;
256   }
257
258   public void setDebugTarget(IPHPDebugTarget debugTarget) {
259     this.debugTarget = debugTarget;
260     debugTarget.setPHPDBGProxy(this);
261   }
262
263   public PHPVariable[] readVariables(PHPStackFrame frame) {
264     try {
265       return DBGInt.getVariables(frame);
266     } catch (IOException ioex) {
267       ioex.printStackTrace();
268       throw new RuntimeException(ioex.getMessage());
269     } catch (DebugException ex) {
270       ex.printStackTrace();
271       throw new RuntimeException(ex.getMessage());
272     }
273   }
274
275   public PHPVariable[] eval(PHPStackFrame frame, String evalString) {
276     try {
277       return DBGInt.evalBlock(frame, evalString);
278       //                        return DBGInt.getVariables(frame);
279     } catch (IOException ioex) {
280       ioex.printStackTrace();
281       throw new RuntimeException(ioex.getMessage());
282     } catch (DebugException ex) {
283       ex.printStackTrace();
284       throw new RuntimeException(ex.getMessage());
285     }
286   }
287
288   public void readStepOverEnd(PHPStackFrame stackFrame) {
289     try {
290       DBGInt.stepOver();
291       phpLoop.notifyWait();
292     } catch (Exception e) {
293       PHPDebugCorePlugin.log(e);
294     }
295   }
296
297   public void readStepReturnEnd(PHPStackFrame stackFrame) {
298     try {
299       DBGInt.stepOut();
300       phpLoop.notifyWait();
301     } catch (Exception e) {
302       PHPDebugCorePlugin.log(e);
303     }
304   }
305
306   public void readStepIntoEnd(PHPStackFrame stackFrame) {
307     try {
308       DBGInt.stepInto();
309       phpLoop.notifyWait();
310     } catch (Exception e) {
311       PHPDebugCorePlugin.log(e);
312     }
313   }
314
315   /*
316    * public PHPStackFrame[] readFrames(PHPThread thread) { //try { //this.println("th " + thread.getId() + " ; f "); //return new
317    * FramesReader(getMultiReaderStrategy()).readFrames(thread); return null; //} catch (IOException e) { //
318    * PHPDebugCorePlugin.log(e); // return null; //}
319    *  }
320    */
321
322   public void closeSocket() throws IOException {
323     if (socket != null) {
324       socket.close();
325     }
326   }
327
328   public void closeServerSocket() throws IOException {
329     if (server != null) {
330       server.close();
331     }
332   }
333
334   public int getPort() {
335     return port;
336   }
337
338   class PHPLoop extends Thread {
339     private boolean shouldStop;
340
341     public PHPLoop() {
342       shouldStop = false;
343       this.setName("PHPDebuggerLoop");
344     }
345
346     public synchronized void setShouldStop() {
347                 shouldStop = true;
348                 try {
349                         // If the loop thread is blocked on the server socket, 
350                         // forcibly unblock it to avoid leaking the thread, 
351                         // the socket and the port
352                         closeServerSocket();
353                 } catch (IOException x) {
354                         // Log this as a warning?
355                         PHPDebugCorePlugin.log(x);
356                 }
357         }
358
359     public synchronized void notifyWait() {
360       notify();
361     }
362
363     public void run() {
364       try {
365         char[] buf = new char[16];
366         int i, pos, timeout;
367         long interval = 200; // 200ms
368         String line;
369         PHPStackFrame[] StackList;
370         boolean endFile = false;
371         boolean newconnect = false;
372         Socket newSocket = null;
373         PHPDBGInterface newDBGInt;
374         int sid = -1;
375
376         //                              synchronized (this) {
377         //                                      wait();
378         //                              }
379
380         PHPMainThread = new PHPThread(getDebugTarget(), getPort());
381         PHPMainThread.setName("Thread [main]");
382         timeout = 0;
383
384         //                              while ((getDebugTarget() == null) && (timeout < 100)) {
385         //                                      sleep(100);
386         //                                      timeout++;
387         //                              }
388         // Be sure debug target is set
389         //                              PHPMainThread.setDebugTarget(getDebugTarget());
390         getDebugTarget().addThread(PHPMainThread);
391
392         //System.out.println("Waiting for breakpoints.");
393         while (!shouldStop) {
394           newconnect = true;
395           try {
396             newSocket = server.accept();
397             //System.out.println("Accepted! : " + socket.toString());
398           } catch (SocketTimeoutException e) {
399             // no one wants to connect
400             newconnect = false;
401           } catch (IOException e) {
402             PHPDebugCorePlugin.log(e);
403             return;
404           }
405
406           if (newconnect) {
407             if (DBGInt == null)
408               server.setSoTimeout(1);
409             newDBGInt = new PHPDBGInterface(getReader(newSocket), newSocket.getOutputStream(), thisProxy);
410             newDBGInt.waitResponse(1000);
411             newDBGInt.flushAllPackets();
412             // Check version and session ID
413             if ((DBGInt == null) || (DBGInt.getSID() == newDBGInt.getSID())) {
414               DBGInt = newDBGInt;
415               try {
416                 closeSocket();
417               } catch (IOException e) {
418                 PHPDebugCorePlugin.log(e);
419                 shouldStop = true;
420               }
421               socket = newSocket;
422               setBreakPoints();
423               DBGInt.continueExecution();
424             } else {
425               newDBGInt.continueExecution();
426               newSocket.close();
427             }
428           }
429
430           if (DBGInt.waitResponse(interval)) {
431
432             DBGInt.flushAllPackets();
433
434             if (DBGInt.BPUnderHit != 0) {
435               StackList = DBGInt.getStackList();
436               if (StackList.length > 0) {
437                 for (i = 0; i < StackList.length; i++) {
438                   StackList[i].setThread(PHPMainThread);
439                   if (DBGInt.getModByNo(StackList[i].getModNo()).equals("")) {
440                     DBGInt.getSourceTree();
441                   }
442                   StackList[i].setFile(DBGInt.getModByNo(StackList[i].getModNo()));
443                 }
444                 PHPMainThread.setStackFrames(StackList);
445               }
446               // Fire debug event
447               PHPMainThread.suspend();
448
449               synchronized (this) {
450                 wait();
451               }
452             }
453           }
454           if (remote) {
455             if (PHPMainThread.isTerminated()) {
456               shouldStop = true;
457               break;
458             }
459           } else {
460             if (PHPMainThread.isTerminated() || getDebugTarget().getProcess().isTerminated()) {
461               shouldStop = true;
462               break;
463             }
464           }
465         }
466       } catch (Exception ex) {
467         PHPDebugCorePlugin.log(ex);
468         System.out.println(ex);
469       } finally {
470         try {
471           getDebugTarget().terminate();
472           closeSocket();
473           closeServerSocket();
474         } catch (IOException e) {
475           PHPDebugCorePlugin.log(e);
476           return;
477         }
478         //System.out.println("Socket loop finished.");
479       }
480     }
481   }
482 }