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