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