using System;
using System.Diagnostics;
using System.IO;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Diagnostics.NETCore.Client;
using Microsoft.Extensions.Logging;
{
processStartedResult = process.Start();
}
- catch (Exception)
+ catch (Exception ex)
{
+ logger.LogError($"Failed executing {adbTool} {command}. Error: {ex.Message}.");
}
if (processStartedResult)
if (!string.IsNullOrEmpty(stdout))
{
- logger.LogTrace($"stdout: {stdout}");
+ logger.LogTrace($"stdout: {stdout.TrimEnd()}");
}
if (!string.IsNullOrEmpty(stderr))
{
- logger.LogError($"stderr: {stderr}");
+ logger.LogError($"stderr: {stderr.TrimEnd()}");
}
}
{
private readonly int _port;
private bool _ownsPortReverse;
+ private Task _portReverseTask;
+ private CancellationTokenSource _portReverseTaskCancelToken;
public static TcpServerRouterFactory CreateADBInstance(string tcpServer, int runtimeTimeoutMs, ILogger logger)
{
// Enable port reverse.
_ownsPortReverse = ADBCommandExec.AdbAddPortReverse(_port, Logger);
+ _portReverseTaskCancelToken = new CancellationTokenSource();
+ _portReverseTask = Task.Run(async () => {
+ using PeriodicTimer timer = new(TimeSpan.FromSeconds(5));
+ while (await timer.WaitForNextTickAsync(_portReverseTaskCancelToken.Token).ConfigureAwait(false) && !_portReverseTaskCancelToken.Token.IsCancellationRequested)
+ {
+ // Make sure reverse port configuration is still active.
+ if (ADBCommandExec.AdbAddPortReverse(_port, Logger) && !_ownsPortReverse)
+ {
+ _ownsPortReverse = true;
+ }
+ }
+ }, _portReverseTaskCancelToken.Token);
+
base.Start();
}
{
await base.Stop().ConfigureAwait(false);
+ try
+ {
+ _portReverseTaskCancelToken.Cancel();
+ await _portReverseTask.ConfigureAwait(false);
+ }
+ catch { }
+
// Disable port reverse.
ADBCommandExec.AdbRemovePortReverse(_port, _ownsPortReverse, Logger);
_ownsPortReverse = false;
{
private readonly int _port;
private bool _ownsPortForward;
+ private Task _portForwardTask;
+ private CancellationTokenSource _portForwardTaskCancelToken;
public static TcpClientRouterFactory CreateADBInstance(string tcpClient, int runtimeTimeoutMs, ILogger logger)
{
{
// Enable port forwarding.
_ownsPortForward = ADBCommandExec.AdbAddPortForward(_port, _logger);
+
+ _portForwardTaskCancelToken = new CancellationTokenSource();
+ _portForwardTask = Task.Run(async () => {
+ using PeriodicTimer timer = new(TimeSpan.FromSeconds(5));
+ while (await timer.WaitForNextTickAsync(_portForwardTaskCancelToken.Token).ConfigureAwait(false) && !_portForwardTaskCancelToken.Token.IsCancellationRequested)
+ {
+ // Make sure forward port configuration is still active.
+ if (ADBCommandExec.AdbAddPortForward(_port, _logger) && !_ownsPortForward)
+ {
+ _ownsPortForward = true;
+ }
+ }
+ }, _portForwardTaskCancelToken.Token);
}
public override void Stop()
{
+ try
+ {
+ _portForwardTaskCancelToken.Cancel();
+ _portForwardTask.Wait();
+ }
+ catch { }
+
// Disable port forwarding.
ADBCommandExec.AdbRemovePortForward(_port, _ownsPortForward, _logger);
_ownsPortForward = false;
}
}
+ public async Task<int> RunIpcServerIOSSimulatorRouter(CancellationToken token, int runtimeTimeout, string verbose)
+ {
+ logDiagnosticPortsConfiguration("ios simulator", "127.0.0.1:9000", false, verbose);
+ return await RunIpcServerTcpServerRouter(token, "", "127.0.0.1:9000", runtimeTimeout, verbose, "").ConfigureAwait(false);
+ }
+
+ public async Task<int> RunIpcServerIOSRouter(CancellationToken token, int runtimeTimeout, string verbose)
+ {
+ logDiagnosticPortsConfiguration("ios device", "127.0.0.1:9000", true, verbose);
+ return await RunIpcServerTcpClientRouter(token, "", "127.0.0.1:9000", runtimeTimeout, verbose, "iOS").ConfigureAwait(false);
+ }
+
+ public async Task<int> RunIpcServerAndroidEmulatorRouter(CancellationToken token, int runtimeTimeout, string verbose)
+ {
+ logDiagnosticPortsConfiguration("android emulator", "10.0.2.2:9000", false, verbose);
+ return await RunIpcServerTcpServerRouter(token, "", "127.0.0.1:9000", runtimeTimeout, verbose, "").ConfigureAwait(false);
+ }
+
+ public async Task<int> RunIpcServerAndroidRouter(CancellationToken token, int runtimeTimeout, string verbose)
+ {
+ logDiagnosticPortsConfiguration("android emulator", "127.0.0.1:9000", false, verbose);
+ return await RunIpcServerTcpServerRouter(token, "", "127.0.0.1:9000", runtimeTimeout, verbose, "Android").ConfigureAwait(false);
+ }
+
private static string GetDefaultIpcServerPath(ILogger logger)
{
string path = string.Empty;
return tcpServerRouterFactory;
}
+ private static void logDiagnosticPortsConfiguration(string deviceName, string deviceTcpIpAddress, bool deviceListenMode, string verbose)
+ {
+ StringBuilder message = new();
+
+ if (!string.IsNullOrEmpty(verbose))
+ {
+ deviceName = !string.IsNullOrEmpty(deviceName) ? $" on {deviceName} " : " ";
+ message.AppendLine($"Start an application{deviceName}with one of the following environment variables set:");
+ }
+
+ string listenMode = deviceListenMode ? ",listen" : ",connect";
+ message.AppendLine($"DOTNET_DiagnosticPorts={deviceTcpIpAddress},nosuspend{listenMode}");
+ message.AppendLine($"DOTNET_DiagnosticPorts={deviceTcpIpAddress},suspend{listenMode}");
+
+ Console.WriteLine(message.ToString());
+ }
+
private static void checkLoopbackOnly(string tcpServer)
{
if (!string.IsNullOrEmpty(tcpServer) && !DiagnosticsServerRouterRunner.isLoopbackOnly(tcpServer))
private delegate Task<int> DiagnosticsServerIpcClientWebSocketServerRouterDelegate(CancellationToken ct, string ipcClient, string webSocket, int runtimeTimeoutS, string verbose);
+ private delegate Task<int> DiagnosticsServerIpcServerIOSSimulatorRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose);
+
+ private delegate Task<int> DiagnosticsServerIpcServerIOSRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose);
+
+ private delegate Task<int> DiagnosticsServerIpcServerAndroidEmulatorRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose);
+
+ private delegate Task<int> DiagnosticsServerIpcServerAndroidRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose);
+
private static Command IpcClientTcpServerRouterCommand() =>
new(
name: "client-server",
IpcClientAddressOption(), TcpClientAddressOption(), RuntimeTimeoutOption(), VerboseOption(), ForwardPortOption()
};
+ private static Command IOSSimulatorRouterCommand() =>
+ new(
+ name: "ios-sim",
+ description: "Start a .NET application Diagnostics Server routing local IPC server <--> iOS Simulator. " +
+ "Router is configured using an IPC server (connecting to by diagnostic tools) " +
+ "and a TCP/IP server (accepting runtime TCP client).")
+ {
+ // Handler
+ HandlerDescriptor.FromDelegate((DiagnosticsServerIpcServerIOSSimulatorRouterDelegate)new DiagnosticsServerRouterCommands().RunIpcServerIOSSimulatorRouter).GetCommandHandler(),
+ // Options
+ RuntimeTimeoutOption(), VerboseOption()
+ };
+
+ private static Command IOSRouterCommand() =>
+ new(
+ name: "ios",
+ description: "Start a .NET application Diagnostics Server routing local IPC server <--> iOS Device over usbmux. " +
+ "Router is configured using an IPC server (connecting to by diagnostic tools) " +
+ "and a TCP/IP client (connecting runtime TCP server over usbmux).")
+ {
+ // Handler
+ HandlerDescriptor.FromDelegate((DiagnosticsServerIpcServerIOSRouterDelegate)new DiagnosticsServerRouterCommands().RunIpcServerIOSRouter).GetCommandHandler(),
+ // Options
+ RuntimeTimeoutOption(), VerboseOption()
+ };
+
+ private static Command AndroidEmulatorRouterCommand() =>
+ new(
+ name: "android-emu",
+ description: "Start a .NET application Diagnostics Server routing local IPC server <--> Android Emulator. " +
+ "Router is configured using an IPC server (connecting to by diagnostic tools) " +
+ "and a TCP/IP server (accepting runtime TCP client).")
+ {
+ // Handler
+ HandlerDescriptor.FromDelegate((DiagnosticsServerIpcServerAndroidEmulatorRouterDelegate)new DiagnosticsServerRouterCommands().RunIpcServerAndroidEmulatorRouter).GetCommandHandler(),
+ // Options
+ RuntimeTimeoutOption(), VerboseOption()
+ };
+
+ private static Command AndroidRouterCommand() =>
+ new(
+ name: "android",
+ description: "Start a .NET application Diagnostics Server routing local IPC server <--> Android Device. " +
+ "Router is configured using an IPC server (connecting to by diagnostic tools) " +
+ "and a TCP/IP server (accepting runtime TCP client).")
+ {
+ // Handler
+ HandlerDescriptor.FromDelegate((DiagnosticsServerIpcServerAndroidRouterDelegate)new DiagnosticsServerRouterCommands().RunIpcServerAndroidRouter).GetCommandHandler(),
+ // Options
+ RuntimeTimeoutOption(), VerboseOption()
+ };
+
private static Option IpcClientAddressOption() =>
new(
aliases: new[] { "--ipc-client", "-ipcc" },
.AddCommand(IpcClientTcpClientRouterCommand())
.AddCommand(IpcServerWebSocketServerRouterCommand())
.AddCommand(IpcClientWebSocketServerRouterCommand())
+ .AddCommand(IOSSimulatorRouterCommand())
+ .AddCommand(IOSRouterCommand())
+ .AddCommand(AndroidEmulatorRouterCommand())
+ .AddCommand(AndroidRouterCommand())
.UseDefaults()
.Build();