pascalabcnet/VisualPascalABCNETLinux/MonoDebugging/Debugger/VirtualMachineManager.cs

371 lines
11 KiB
C#
Raw Normal View History

2023-04-18 18:53:21 +03:00
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
#if !NETCOREAPP
using System.Runtime.Remoting.Messaging;
#endif
using System.Text;
using System.Threading.Tasks;
namespace Mono.Debugger.Soft
{
public class LaunchOptions {
public string AgentArgs {
get; set;
}
public bool Valgrind {
get; set;
}
public ProcessLauncher CustomProcessLauncher {
get; set;
}
public TargetProcessLauncher CustomTargetProcessLauncher {
get; set;
}
public delegate Process ProcessLauncher (ProcessStartInfo info);
public delegate ITargetProcess TargetProcessLauncher (ProcessStartInfo info);
}
public class VirtualMachineManager
{
private delegate VirtualMachine LaunchCallback (ITargetProcess p, ProcessStartInfo info, Socket socket);
private delegate VirtualMachine ListenCallback (Socket dbg_sock, Socket con_sock);
private delegate VirtualMachine ConnectCallback (Socket dbg_sock, Socket con_sock, IPEndPoint dbg_ep, IPEndPoint con_ep);
internal VirtualMachineManager () {
}
public static VirtualMachine LaunchInternal (Process p, ProcessStartInfo info, Socket socket)
{
return LaunchInternal (new ProcessWrapper (p), info, socket);
}
public static VirtualMachine LaunchInternal (ITargetProcess p, ProcessStartInfo info, Socket socket) {
return LaunchInternalAsync (p, info, socket).Result;
}
public static async Task<VirtualMachine> LaunchInternalAsync (ITargetProcess p, ProcessStartInfo info, Socket socket)
{
Socket accepted = null;
try {
accepted = await socket.AcceptAsync ().ConfigureAwait(false);
} catch (Exception) {
throw;
}
Connection conn = new TcpConnection (accepted);
VirtualMachine vm = new VirtualMachine (p, conn);
if (info.RedirectStandardOutput && false)
2023-04-18 18:53:21 +03:00
vm.StandardOutput = p.StandardOutput;
if (info.RedirectStandardError && false)
2023-04-18 18:53:21 +03:00
vm.StandardError = p.StandardError;
2023-04-18 18:53:21 +03:00
conn.EventHandler = new EventHandler (vm);
vm.connect ();
return vm;
}
public static IAsyncResult BeginLaunch (ProcessStartInfo info, AsyncCallback callback)
{
return BeginLaunch (info, callback, null);
}
public static ITargetProcess currentProcess;
2023-04-18 18:53:21 +03:00
public static IAsyncResult BeginLaunch (ProcessStartInfo info, AsyncCallback callback, LaunchOptions options)
{
if (info == null)
throw new ArgumentNullException ("info");
Socket socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind (new IPEndPoint (IPAddress.Loopback, 0));
socket.Listen (1000);
IPEndPoint ep = (IPEndPoint) socket.LocalEndPoint;
// We need to inject our arguments into the psi
info.Arguments = string.Format ("{0} --debug --debugger-agent=transport=dt_socket,address={1}:{2}{3} {4}",
options == null || !options.Valgrind ? "" : info.FileName,
ep.Address,
ep.Port,
options == null || options.AgentArgs == null ? "" : "," + options.AgentArgs,
info.Arguments);
if (options != null && options.Valgrind)
info.FileName = "valgrind";
info.UseShellExecute = false;
info.RedirectStandardError = true;
2023-04-18 18:53:21 +03:00
if (info.RedirectStandardError)
info.StandardErrorEncoding = Encoding.UTF8;
if (info.RedirectStandardOutput)
info.StandardOutputEncoding = Encoding.UTF8;
info.RedirectStandardInput = true;
2023-04-18 18:53:21 +03:00
ITargetProcess p;
if (options != null && options.CustomProcessLauncher != null)
p = new ProcessWrapper (options.CustomProcessLauncher (info));
else if (options != null && options.CustomTargetProcessLauncher != null)
p = options.CustomTargetProcessLauncher (info);
else
{
var netProcess = new System.Diagnostics.Process();
netProcess.StartInfo = info;
netProcess.EnableRaisingEvents = true;
netProcess.Start();
p = new ProcessWrapper(netProcess);
}
2023-04-18 18:53:21 +03:00
p.Exited += delegate (object sender, EventArgs eargs) {
socket.Close ();
};
currentProcess = p;
2023-04-18 18:53:21 +03:00
var listenTask = LaunchInternalAsync (p, info, socket);
listenTask.ContinueWith (t => callback (listenTask));
return listenTask;
}
public static VirtualMachine EndLaunch (IAsyncResult asyncResult) {
if (asyncResult == null)
throw new ArgumentNullException ("asyncResult");
var listenTask = (Task<VirtualMachine>)asyncResult;
return listenTask.Result;
}
public static VirtualMachine Launch (ProcessStartInfo info)
{
return Launch (info, null);
}
public static VirtualMachine Launch (ProcessStartInfo info, LaunchOptions options)
{
return EndLaunch (BeginLaunch (info, null, options));
}
public static VirtualMachine Launch (string[] args)
{
return Launch (args, null);
}
public static VirtualMachine Launch (string[] args, LaunchOptions options)
{
ProcessStartInfo pi = new ProcessStartInfo ("mono");
pi.Arguments = String.Join (" ", args);
return Launch (pi, options);
}
public static VirtualMachine ListenInternal (Socket dbg_sock, Socket con_sock)
{
return ListenInternalAsync (dbg_sock, con_sock).Result;
}
static async Task<VirtualMachine> ListenInternalAsync (Socket dbg_sock, Socket con_sock)
{
Socket con_acc = null;
Socket dbg_acc = null;
if (con_sock != null) {
try {
con_acc = await con_sock.AcceptAsync ().ConfigureAwait (false);
} catch (Exception) {
try {
dbg_sock.Close ();
} catch { }
throw;
}
}
try {
dbg_acc = await dbg_sock.AcceptAsync ().ConfigureAwait (false);
} catch (Exception) {
if (con_sock != null) {
try {
con_sock.Close ();
con_acc.Close ();
} catch { }
}
throw;
}
if (con_sock != null) {
if (con_sock.Connected)
con_sock.Disconnect (false);
con_sock.Close ();
}
if (dbg_sock.Connected)
dbg_sock.Disconnect (false);
dbg_sock.Close ();
Connection transport = new TcpConnection (dbg_acc);
StreamReader console = con_acc != null ? new StreamReader (new NetworkStream (con_acc)) : null;
return Connect (transport, console, null);
}
public static IAsyncResult BeginListen (IPEndPoint dbg_ep, AsyncCallback callback) {
return BeginListen (dbg_ep, null, callback);
}
public static IAsyncResult BeginListen (IPEndPoint dbg_ep, IPEndPoint con_ep, AsyncCallback callback)
{
int dbg_port, con_port;
return BeginListen (dbg_ep, con_ep, callback, out dbg_port, out con_port);
}
public static IAsyncResult BeginListen (IPEndPoint dbg_ep, IPEndPoint con_ep, AsyncCallback callback,
out int dbg_port, out int con_port)
{
dbg_port = con_port = 0;
Socket dbg_sock = null;
Socket con_sock = null;
dbg_sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
dbg_sock.Bind (dbg_ep);
dbg_sock.Listen (1000);
dbg_port = ((IPEndPoint) dbg_sock.LocalEndPoint).Port;
if (con_ep != null) {
con_sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
con_sock.Bind (con_ep);
con_sock.Listen (1000);
con_port = ((IPEndPoint) con_sock.LocalEndPoint).Port;
}
var listenTask = ListenInternalAsync (dbg_sock, con_sock);
listenTask.ContinueWith (t => callback (listenTask));
return listenTask;
}
public static VirtualMachine EndListen (IAsyncResult asyncResult) {
if (asyncResult == null)
throw new ArgumentNullException ("asyncResult");
var listenTask = (Task<VirtualMachine>)asyncResult;
return listenTask.Result;
}
public static VirtualMachine Listen (IPEndPoint dbg_ep)
{
return Listen (dbg_ep, null);
}
public static VirtualMachine Listen (IPEndPoint dbg_ep, IPEndPoint con_ep)
{
return EndListen (BeginListen (dbg_ep, con_ep, null));
}
/*
* Connect to a virtual machine listening at the specified address.
*/
public static VirtualMachine Connect (IPEndPoint endpoint) {
return Connect (endpoint, null);
}
public static VirtualMachine Connect (IPEndPoint endpoint, IPEndPoint consoleEndpoint) {
if (endpoint == null)
throw new ArgumentNullException ("endpoint");
return EndConnect (BeginConnect (endpoint, consoleEndpoint, null));
}
public static VirtualMachine ConnectInternal (Socket dbg_sock, Socket con_sock, IPEndPoint dbg_ep, IPEndPoint con_ep)
{
return ConnectInternalAsync (dbg_sock, con_sock, dbg_ep, con_ep).Result;
}
public static async Task<VirtualMachine> ConnectInternalAsync (Socket dbg_sock, Socket con_sock, IPEndPoint dbg_ep, IPEndPoint con_ep)
{
if (con_sock != null) {
try {
await con_sock.ConnectAsync(con_ep).ConfigureAwait (false);
} catch (Exception) {
try {
dbg_sock.Close ();
} catch { }
throw;
}
}
try {
await dbg_sock.ConnectAsync(dbg_ep).ConfigureAwait(false);
} catch (Exception) {
if (con_sock != null) {
try {
con_sock.Close ();
} catch { }
}
throw;
}
Connection transport = new TcpConnection (dbg_sock);
StreamReader console = con_sock != null ? new StreamReader (new NetworkStream (con_sock)) : null;
return Connect (transport, console, null);
}
public static IAsyncResult BeginConnect (IPEndPoint dbg_ep, AsyncCallback callback) {
return BeginConnect (dbg_ep, null, callback);
}
public static IAsyncResult BeginConnect (IPEndPoint dbg_ep, IPEndPoint con_ep, AsyncCallback callback) {
Socket dbg_sock = null;
Socket con_sock = null;
dbg_sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
if (con_ep != null) {
con_sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
var connectTask = ConnectInternalAsync (dbg_sock, con_sock, dbg_ep, con_ep);
connectTask.ContinueWith (t => callback (connectTask));
return connectTask;
}
public static VirtualMachine EndConnect (IAsyncResult asyncResult) {
if (asyncResult == null)
throw new ArgumentNullException ("asyncResult");
var connectTask = (Task<VirtualMachine>)asyncResult;
return connectTask.Result;
}
public static void CancelConnection (IAsyncResult asyncResult)
{
//AsyncState could be null if the debugger incoming connection doesn't happen, so there's no socket between debugger and debuggee
//This could occur if the debugger session started but the connection to the app failed at some point
((Socket)asyncResult.AsyncState)?.Close ();
}
public static VirtualMachine Connect (Connection transport, StreamReader standardOutput, StreamReader standardError)
{
VirtualMachine vm = new VirtualMachine (null, transport);
//vm.StandardOutput = standardOutput;
//vm.StandardError = standardError;
2023-04-18 18:53:21 +03:00
transport.EventHandler = new EventHandler (vm);
vm.connect ();
return vm;
}
}
}