Merge branch 'master' into msbuilddll2
[mono.git] / mcs / class / Mono.Debugger.Soft / Mono.Debugger.Soft / VirtualMachineManager.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.IO;
5 using System.Net;
6 using System.Net.Sockets;
7 using System.Runtime.Remoting.Messaging;
8
9 namespace Mono.Debugger.Soft
10 {
11         public class LaunchOptions {
12                 public string AgentArgs {
13                         get; set;
14                 }
15
16                 public bool Valgrind {
17                         get; set;
18                 }
19                 
20                 public ProcessLauncher CustomProcessLauncher {
21                         get; set;
22                 }
23
24                 public TargetProcessLauncher CustomTargetProcessLauncher {
25                         get; set;
26                 }
27
28                 public delegate Process ProcessLauncher (ProcessStartInfo info);
29                 public delegate ITargetProcess TargetProcessLauncher (ProcessStartInfo info);
30         }
31
32         public class VirtualMachineManager
33         {
34                 private delegate VirtualMachine LaunchCallback (ITargetProcess p, ProcessStartInfo info, Socket socket);
35                 private delegate VirtualMachine ListenCallback (Socket dbg_sock, Socket con_sock); 
36                 private delegate VirtualMachine ConnectCallback (Socket dbg_sock, Socket con_sock, IPEndPoint dbg_ep, IPEndPoint con_ep); 
37
38                 internal VirtualMachineManager () {
39                 }
40
41                 public static VirtualMachine LaunchInternal (Process p, ProcessStartInfo info, Socket socket)
42                 {
43                         return LaunchInternal (new ProcessWrapper (p), info, socket);
44                 }
45                         
46                 public static VirtualMachine LaunchInternal (ITargetProcess p, ProcessStartInfo info, Socket socket) {
47                         Socket accepted = null;
48                         try {
49                                 accepted = socket.Accept ();
50                         } catch (Exception) {
51                                 throw;
52                         }
53
54                         Connection conn = new TcpConnection (accepted);
55
56                         VirtualMachine vm = new VirtualMachine (p, conn);
57
58                         if (info.RedirectStandardOutput)
59                                 vm.StandardOutput = p.StandardOutput;
60                         
61                         if (info.RedirectStandardError)
62                                 vm.StandardError = p.StandardError;
63
64                         conn.EventHandler = new EventHandler (vm);
65
66                         vm.connect ();
67
68                         return vm;
69                 }
70
71                 public static IAsyncResult BeginLaunch (ProcessStartInfo info, AsyncCallback callback)
72                 {
73                         return BeginLaunch (info, callback, null);
74                 }
75
76                 public static IAsyncResult BeginLaunch (ProcessStartInfo info, AsyncCallback callback, LaunchOptions options)
77                 {
78                         if (info == null)
79                                 throw new ArgumentNullException ("info");
80
81                         Socket socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
82                         socket.Bind (new IPEndPoint (IPAddress.Loopback, 0));
83                         socket.Listen (1000);
84                         IPEndPoint ep = (IPEndPoint) socket.LocalEndPoint;
85
86                         // We need to inject our arguments into the psi
87                         info.Arguments = string.Format ("{0} --debug --debugger-agent=transport=dt_socket,address={1}:{2}{3} {4}", 
88                                                                 options == null || !options.Valgrind ? "" : info.FileName,
89                                                                 ep.Address,
90                                                                 ep.Port,
91                                                                 options == null || options.AgentArgs == null ? "" : "," + options.AgentArgs,
92                                                                 info.Arguments);
93
94                         if (options != null && options.Valgrind)
95                                 info.FileName = "valgrind";
96                         info.UseShellExecute = false;
97
98                         ITargetProcess p;
99                         if (options != null && options.CustomProcessLauncher != null)
100                                 p = new ProcessWrapper (options.CustomProcessLauncher (info));
101                         else if (options != null && options.CustomTargetProcessLauncher != null)
102                                 p = options.CustomTargetProcessLauncher (info);
103                         else
104                                 p = new ProcessWrapper (Process.Start (info));
105                         
106                         p.Exited += delegate (object sender, EventArgs eargs) {
107                                 socket.Close ();
108                         };
109
110                         LaunchCallback c = new LaunchCallback (LaunchInternal);
111                         return c.BeginInvoke (p, info, socket, callback, socket);
112                 }
113
114                 public static VirtualMachine EndLaunch (IAsyncResult asyncResult) {
115                         if (asyncResult == null)
116                                 throw new ArgumentNullException ("asyncResult");
117
118                         if (!asyncResult.IsCompleted)
119                                 asyncResult.AsyncWaitHandle.WaitOne ();
120
121                         AsyncResult result = (AsyncResult) asyncResult;
122                         LaunchCallback cb = (LaunchCallback) result.AsyncDelegate;
123                         return cb.EndInvoke (asyncResult);
124                 }
125
126                 public static VirtualMachine Launch (ProcessStartInfo info)
127                 {
128                         return Launch (info, null);
129                 }
130
131                 public static VirtualMachine Launch (ProcessStartInfo info, LaunchOptions options)
132                 {
133                         return EndLaunch (BeginLaunch (info, null, options));
134                 }
135
136                 public static VirtualMachine Launch (string[] args)
137                 {
138                         return Launch (args, null);
139                 }
140
141                 public static VirtualMachine Launch (string[] args, LaunchOptions options)
142                 {
143                         ProcessStartInfo pi = new ProcessStartInfo ("mono");
144                         pi.Arguments = String.Join (" ", args);
145
146                         return Launch (pi, options);
147                 }
148                         
149                 public static VirtualMachine ListenInternal (Socket dbg_sock, Socket con_sock) {
150                         Socket con_acc = null;
151                         Socket dbg_acc = null;
152
153                         if (con_sock != null) {
154                                 try {
155                                         con_acc = con_sock.Accept ();
156                                 } catch (Exception) {
157                                         try {
158                                                 dbg_sock.Close ();
159                                         } catch {}
160                                         throw;
161                                 }
162                         }
163                                                 
164                         try {
165                                 dbg_acc = dbg_sock.Accept ();
166                         } catch (Exception) {
167                                 if (con_sock != null) {
168                                         try {
169                                                 con_sock.Close ();
170                                                 con_acc.Close ();
171                                         } catch {}
172                                 }
173                                 throw;
174                         }
175
176                         if (con_sock != null) {
177                                 if (con_sock.Connected)
178                                         con_sock.Disconnect (false);
179                                 con_sock.Close ();
180                         }
181
182                         if (dbg_sock.Connected)
183                                 dbg_sock.Disconnect (false);
184                         dbg_sock.Close ();
185
186                         Connection transport = new TcpConnection (dbg_acc);
187                         StreamReader console = con_acc != null? new StreamReader (new NetworkStream (con_acc)) : null;
188                         
189                         return Connect (transport, console, null);
190                 }
191
192                 public static IAsyncResult BeginListen (IPEndPoint dbg_ep, AsyncCallback callback) {
193                         return BeginListen (dbg_ep, null, callback);
194                 }
195                 
196                 public static IAsyncResult BeginListen (IPEndPoint dbg_ep, IPEndPoint con_ep, AsyncCallback callback)
197                 {
198                         int dbg_port, con_port;
199                         return BeginListen (dbg_ep, con_ep, callback, out dbg_port, out con_port);
200                 }
201
202                 public static IAsyncResult BeginListen (IPEndPoint dbg_ep, IPEndPoint con_ep, AsyncCallback callback,
203                         out int dbg_port, out int con_port)
204                 {
205                         dbg_port = con_port = 0;
206                         
207                         Socket dbg_sock = null;
208                         Socket con_sock = null;
209
210                         dbg_sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
211                         dbg_sock.Bind (dbg_ep);
212                         dbg_sock.Listen (1000);
213                         dbg_port = ((IPEndPoint) dbg_sock.LocalEndPoint).Port;
214
215                         if (con_ep != null) {
216                                 con_sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
217                                 con_sock.Bind (con_ep);
218                                 con_sock.Listen (1000);
219                                 con_port = ((IPEndPoint) con_sock.LocalEndPoint).Port;
220                         }
221                         
222                         ListenCallback c = new ListenCallback (ListenInternal);
223                         return c.BeginInvoke (dbg_sock, con_sock, callback, con_sock ?? dbg_sock);
224                 }
225
226                 public static VirtualMachine EndListen (IAsyncResult asyncResult) {
227                         if (asyncResult == null)
228                                 throw new ArgumentNullException ("asyncResult");
229
230                         if (!asyncResult.IsCompleted)
231                                 asyncResult.AsyncWaitHandle.WaitOne ();
232
233                         AsyncResult result = (AsyncResult) asyncResult;
234                         ListenCallback cb = (ListenCallback) result.AsyncDelegate;
235                         return cb.EndInvoke (asyncResult);
236                 }
237
238                 public static VirtualMachine Listen (IPEndPoint dbg_ep)
239                 {
240                         return Listen (dbg_ep, null);
241                 }
242
243                 public static VirtualMachine Listen (IPEndPoint dbg_ep, IPEndPoint con_ep)
244                 {
245                         return EndListen (BeginListen (dbg_ep, con_ep, null));
246                 }
247
248                 /*
249                  * Connect to a virtual machine listening at the specified address.
250                  */
251                 public static VirtualMachine Connect (IPEndPoint endpoint) {
252                         return Connect (endpoint, null);
253                 }
254
255                 public static VirtualMachine Connect (IPEndPoint endpoint, IPEndPoint consoleEndpoint) { 
256                         if (endpoint == null)
257                                 throw new ArgumentNullException ("endpoint");
258
259                         return EndConnect (BeginConnect (endpoint, consoleEndpoint, null));
260                 }
261
262                 public static VirtualMachine ConnectInternal (Socket dbg_sock, Socket con_sock, IPEndPoint dbg_ep, IPEndPoint con_ep) {
263                         if (con_sock != null) {
264                                 try {
265                                         con_sock.Connect (con_ep);
266                                 } catch (Exception) {
267                                         try {
268                                                 dbg_sock.Close ();
269                                         } catch {}
270                                         throw;
271                                 }
272                         }
273                                                 
274                         try {
275                                 dbg_sock.Connect (dbg_ep);
276                         } catch (Exception) {
277                                 if (con_sock != null) {
278                                         try {
279                                                 con_sock.Close ();
280                                         } catch {}
281                                 }
282                                 throw;
283                         }
284                         
285                         Connection transport = new TcpConnection (dbg_sock);
286                         StreamReader console = con_sock != null? new StreamReader (new NetworkStream (con_sock)) : null;
287                         
288                         return Connect (transport, console, null);
289                 }
290
291                 public static IAsyncResult BeginConnect (IPEndPoint dbg_ep, AsyncCallback callback) {
292                         return BeginConnect (dbg_ep, null, callback);
293                 }
294
295                 public static IAsyncResult BeginConnect (IPEndPoint dbg_ep, IPEndPoint con_ep, AsyncCallback callback) {
296                         Socket dbg_sock = null;
297                         Socket con_sock = null;
298
299                         dbg_sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
300
301                         if (con_ep != null) {
302                                 con_sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
303                         }
304                         
305                         ConnectCallback c = new ConnectCallback (ConnectInternal);
306                         return c.BeginInvoke (dbg_sock, con_sock, dbg_ep, con_ep, callback, con_sock ?? dbg_sock);
307                 }
308
309                 public static VirtualMachine EndConnect (IAsyncResult asyncResult) {
310                         if (asyncResult == null)
311                                 throw new ArgumentNullException ("asyncResult");
312
313                         if (!asyncResult.IsCompleted)
314                                 asyncResult.AsyncWaitHandle.WaitOne ();
315
316                         AsyncResult result = (AsyncResult) asyncResult;
317                         ConnectCallback cb = (ConnectCallback) result.AsyncDelegate;
318                         return cb.EndInvoke (asyncResult);
319                 }
320
321                 public static void CancelConnection (IAsyncResult asyncResult)
322                 {
323                         ((Socket)asyncResult.AsyncState).Close ();
324                 }
325                 
326                 public static VirtualMachine Connect (Connection transport, StreamReader standardOutput, StreamReader standardError)
327                 {
328                         VirtualMachine vm = new VirtualMachine (null, transport);
329                         
330                         vm.StandardOutput = standardOutput;
331                         vm.StandardError = standardError;
332                         
333                         transport.EventHandler = new EventHandler (vm);
334
335                         vm.connect ();
336
337                         return vm;
338                 }
339         }
340 }