Patch from Anders Betn�- improve path mapping when remote debugging between systems...
[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()
146                                 .getBreakpointManager().getBreakpoints();
147                 for (int i = 0; i < breakpoints.length; i++) {
148                         if (breakpoints[i].isEnabled()) {
149                                 addBreakpoint(breakpoints[i]);
150                         }
151                 }
152         }
153
154   private String MapPath(PHPLineBreakpoint phpLBP) {
155                 IPath filename;
156                 if (remote) {
157                         filename = phpLBP.getMarker().getResource()
158                                         .getProjectRelativePath();
159                         filename = remoteSourcePath.append(filename);
160                 } else
161                         filename = phpLBP.getMarker().getResource().getLocation();
162                 String path = filename.toOSString();
163                 if (pathmap != null && remote) {
164                         java.util.Iterator i = pathmap.keySet().iterator();
165                         while (i.hasNext()) {
166                                 String k = (String) i.next();
167                                 if (path.startsWith(k)) {
168                                         path = pathmap.get(k) + path.substring(k.length());
169                                         break;
170                                 }
171                         }
172                 }
173                 if (pathtranslation && remote) {
174                         if (remoteSourcePath.toString().substring(0, 1).equals("/"))
175                                 path = path.replace('\\', '/');
176                         else
177                                 path = path.replace('/', '\\');
178                 }
179                 return path;
180         }
181   
182   public void addBreakpoint(IBreakpoint breakpoint) {
183     if (DBGInt == null)
184       return;
185     int bpNo = 0;
186     try {
187       PHPLineBreakpoint phpLBP;
188       if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier()) {
189         phpLBP = (PHPLineBreakpoint) breakpoint;
190         //                              bpNo= DBGInt.addBreakpoint(phpLBP.getMarker().getResource().getLocation().toOSString(), phpLBP.getLineNumber());
191
192         bpNo = DBGInt.addBreakpoint(MapPath(phpLBP), phpLBP.getLineNumber());
193         phpLBP.setDBGBpNo(bpNo);
194       }
195     } catch (IOException e) {
196       PHPDebugCorePlugin.log(e);
197       stop();
198     } catch (CoreException e) {
199       PHPDebugCorePlugin.log(e);
200       stop();
201     }
202   }
203
204   public void removeBreakpoint(IBreakpoint breakpoint) {
205     if (DBGInt == null)
206       return;
207     try {
208       PHPLineBreakpoint phpLBP;
209       if (breakpoint.getModelIdentifier() == PHPDebugCorePlugin.getUniqueIdentifier()) {
210         phpLBP = (PHPLineBreakpoint) breakpoint;
211
212         //                                      bpNo= DBGInt.addBreakpoint(filename.toOSString(), phpLBP.getLineNumber());
213         DBGInt.removeBreakpoint(MapPath(phpLBP), phpLBP.getLineNumber(), phpLBP.getDBGBpNo());
214       }
215     } catch (IOException e) {
216       PHPDebugCorePlugin.log(e);
217       stop();
218     } catch (CoreException e) {
219       PHPDebugCorePlugin.log(e);
220       stop();
221     }
222   }
223
224   public void phpLoopNotify() {
225     phpLoop.notifyWait();
226   }
227
228   public void startPHPLoop() {
229     phpLoop = new PHPLoop();
230     phpLoop.start();
231   }
232
233   public void resume() {
234     try {
235       DBGInt.continueExecution();
236       phpLoop.notifyWait();
237     } catch (IOException e) {
238       PHPeclipsePlugin.log("Debugging session ended.", e);
239       stop();
240     }
241   }
242
243   public void pause() {
244     try {
245       if (null != DBGInt)
246         DBGInt.pauseExecution();
247       else {
248         // TODO Make sure the Suspend action is grayed out 
249         // when DBGInt is null
250       }
251     } catch (IOException e) {
252       PHPDebugCorePlugin.log(e);
253       stop();
254     }
255   }
256
257   protected IPHPDebugTarget getDebugTarget() {
258     return debugTarget;
259   }
260
261   public void setDebugTarget(IPHPDebugTarget debugTarget) {
262     this.debugTarget = debugTarget;
263     debugTarget.setPHPDBGProxy(this);
264   }
265
266   public PHPVariable[] readVariables(PHPStackFrame frame) {
267     try {
268       return DBGInt.getVariables(frame);
269     } catch (IOException ioex) {
270       ioex.printStackTrace();
271       throw new RuntimeException(ioex.getMessage());
272     } catch (DebugException ex) {
273       ex.printStackTrace();
274       throw new RuntimeException(ex.getMessage());
275     }
276   }
277
278   public PHPVariable[] eval(PHPStackFrame frame, String evalString) {
279     try {
280       return DBGInt.evalBlock(frame, evalString);
281       //                        return DBGInt.getVariables(frame);
282     } catch (IOException ioex) {
283       ioex.printStackTrace();
284       throw new RuntimeException(ioex.getMessage());
285     } catch (DebugException ex) {
286       ex.printStackTrace();
287       throw new RuntimeException(ex.getMessage());
288     }
289   }
290
291   public void readStepOverEnd(PHPStackFrame stackFrame) {
292     try {
293       DBGInt.stepOver();
294       phpLoop.notifyWait();
295     } catch (Exception e) {
296       PHPDebugCorePlugin.log(e);
297     }
298   }
299
300   public void readStepReturnEnd(PHPStackFrame stackFrame) {
301     try {
302       DBGInt.stepOut();
303       phpLoop.notifyWait();
304     } catch (Exception e) {
305       PHPDebugCorePlugin.log(e);
306     }
307   }
308
309   public void readStepIntoEnd(PHPStackFrame stackFrame) {
310     try {
311       DBGInt.stepInto();
312       phpLoop.notifyWait();
313     } catch (Exception e) {
314       PHPDebugCorePlugin.log(e);
315     }
316   }
317
318   /*
319    * public PHPStackFrame[] readFrames(PHPThread thread) { //try { //this.println("th " + thread.getId() + " ; f "); //return new
320    * FramesReader(getMultiReaderStrategy()).readFrames(thread); return null; //} catch (IOException e) { //
321    * PHPDebugCorePlugin.log(e); // return null; //}
322    *  }
323    */
324
325   public void closeSocket() throws IOException {
326     if (socket != null) {
327       socket.close();
328     }
329   }
330
331   public void closeServerSocket() throws IOException {
332     if (server != null) {
333       server.close();
334     }
335   }
336
337   public int getPort() {
338     return port;
339   }
340
341   class PHPLoop extends Thread {
342     private boolean shouldStop;
343
344     public PHPLoop() {
345       shouldStop = false;
346       this.setName("PHPDebuggerLoop");
347     }
348
349     public synchronized void setShouldStop() {
350                 shouldStop = true;
351                 try {
352                         // If the loop thread is blocked on the server socket, 
353                         // forcibly unblock it to avoid leaking the thread, 
354                         // the socket and the port
355                         closeServerSocket();
356                 } catch (IOException x) {
357                         // Log this as a warning?
358                         PHPDebugCorePlugin.log(x);
359                 }
360         }
361
362     public synchronized void notifyWait() {
363       notify();
364     }
365
366     public void run() {
367       try {
368         char[] buf = new char[16];
369         int i, pos, timeout;
370         long interval = 200; // 200ms
371         String line;
372         PHPStackFrame[] StackList;
373         boolean endFile = false;
374         boolean newconnect = false;
375         Socket newSocket = null;
376         PHPDBGInterface newDBGInt;
377         int sid = -1;
378
379         //                              synchronized (this) {
380         //                                      wait();
381         //                              }
382
383         PHPMainThread = new PHPThread(getDebugTarget(), getPort());
384         PHPMainThread.setName("Thread [main]");
385         timeout = 0;
386
387         //                              while ((getDebugTarget() == null) && (timeout < 100)) {
388         //                                      sleep(100);
389         //                                      timeout++;
390         //                              }
391         // Be sure debug target is set
392         //                              PHPMainThread.setDebugTarget(getDebugTarget());
393         getDebugTarget().addThread(PHPMainThread);
394
395         //System.out.println("Waiting for breakpoints.");
396         while (!shouldStop) {
397           newconnect = true;
398           try {
399             newSocket = server.accept();
400             //System.out.println("Accepted! : " + socket.toString());
401           } catch (SocketTimeoutException e) {
402             // no one wants to connect
403             newconnect = false;
404           } catch (IOException e) {
405             PHPDebugCorePlugin.log(e);
406             return;
407           }
408
409           if (newconnect) {
410             if (DBGInt == null)
411               server.setSoTimeout(1);
412             newDBGInt = new PHPDBGInterface(getReader(newSocket), newSocket.getOutputStream(), thisProxy);
413             newDBGInt.waitResponse(1000);
414             newDBGInt.flushAllPackets();
415             // Check version and session ID
416             if ((DBGInt == null) || (DBGInt.getSID() == newDBGInt.getSID())) {
417               DBGInt = newDBGInt;
418               try {
419                 closeSocket();
420               } catch (IOException e) {
421                 PHPDebugCorePlugin.log(e);
422                 shouldStop = true;
423               }
424               socket = newSocket;
425               setBreakPoints();
426               DBGInt.continueExecution();
427             } else {
428               newDBGInt.continueExecution();
429               newSocket.close();
430             }
431           }
432
433           if (DBGInt.waitResponse(interval)) {
434
435             DBGInt.flushAllPackets();
436
437             if (DBGInt.BPUnderHit != 0) {
438               StackList = DBGInt.getStackList();
439               if (StackList.length > 0) {
440                 for (i = 0; i < StackList.length; i++) {
441                   StackList[i].setThread(PHPMainThread);
442                   if (DBGInt.getModByNo(StackList[i].getModNo()).equals("")) {
443                     DBGInt.getSourceTree();
444                   }
445                   StackList[i].setFile(DBGInt.getModByNo(StackList[i].getModNo()));
446                 }
447                 PHPMainThread.setStackFrames(StackList);
448               }
449               // Fire debug event
450               PHPMainThread.suspend();
451
452               synchronized (this) {
453                 wait();
454               }
455             }
456           }
457           if (remote) {
458             if (PHPMainThread.isTerminated()) {
459               shouldStop = true;
460               break;
461             }
462           } else {
463             if (PHPMainThread.isTerminated() || getDebugTarget().getProcess().isTerminated()) {
464               shouldStop = true;
465               break;
466             }
467           }
468         }
469       } catch (Exception ex) {
470         PHPDebugCorePlugin.log(ex);
471         System.out.println(ex);
472       } finally {
473         try {
474           getDebugTarget().terminate();
475           closeSocket();
476           closeServerSocket();
477         } catch (IOException e) {
478           PHPDebugCorePlugin.log(e);
479           return;
480         }
481         //System.out.println("Socket loop finished.");
482       }
483     }
484   }
485 }