// <summary> // Starts the child process and returns the diagnostics client once the child proc connects to the reversed diagnostics pipe. // The callee needs to resume the diagnostics client at appropriate time. // </summary> public static DiagnosticsClient Build(ProcessLauncher childProcLauncher, string toolName, int timeoutInSec) { if (!childProcLauncher.HasChildProc) { throw new InvalidOperationException("Must have a valid child process to launch."); } // Create and start the reversed server string diagnosticTransportName = GetTransportName(toolName); ReversedDiagnosticsServer server = new ReversedDiagnosticsServer(diagnosticTransportName); server.Start(); // Start the child proc if (!childProcLauncher.Start(diagnosticTransportName)) { throw new InvalidOperationException("Failed to start dotnet-counters."); } // Wait for attach IpcEndpointInfo endpointInfo = server.Accept(TimeSpan.FromSeconds(timeoutInSec)); // If for some reason a different process attached to us, wait until the expected process attaches. while (endpointInfo.ProcessId != childProcLauncher.ChildProc.Id) { endpointInfo = server.Accept(TimeSpan.FromSeconds(timeoutInSec)); } return(new DiagnosticsClient(endpointInfo.Endpoint)); }
public async Task <DiagnosticsClientHolder> Build(CancellationToken ct, int processId, string portName, bool showChildIO, bool printLaunchCommand) { if (ProcessLauncher.Launcher.HasChildProc) { // Create and start the reversed server string diagnosticTransportName = GetTransportName(_toolName); ReversedDiagnosticsServer server = new ReversedDiagnosticsServer(diagnosticTransportName); server.Start(); // Start the child proc if (!ProcessLauncher.Launcher.Start(diagnosticTransportName, ct, showChildIO, printLaunchCommand)) { throw new InvalidOperationException($"Failed to start '{ProcessLauncher.Launcher.ChildProc.StartInfo.FileName} {ProcessLauncher.Launcher.ChildProc.StartInfo.Arguments}'."); } IpcEndpointInfo endpointInfo; try { // Wait for attach endpointInfo = server.Accept(TimeSpan.FromSeconds(_timeoutInSec)); // If for some reason a different process attached to us, wait until the expected process attaches. while (endpointInfo.ProcessId != ProcessLauncher.Launcher.ChildProc.Id) { endpointInfo = server.Accept(TimeSpan.FromSeconds(_timeoutInSec)); } } catch (TimeoutException) { Console.Error.WriteLine("Unable to start tracing session - the target app failed to connect to the diagnostics port. This may happen if the target application is running .NET Core 3.1 or older versions. Attaching at startup is only available from .NET 5.0 or later."); throw; } return(new DiagnosticsClientHolder(new DiagnosticsClient(endpointInfo.Endpoint), endpointInfo, server)); } else if (!string.IsNullOrEmpty(portName)) { ReversedDiagnosticsServer server = new ReversedDiagnosticsServer(portName); server.Start(); string fullPort = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? portName : Path.GetFullPath(portName); Console.WriteLine($"Waiting for connection on {fullPort}"); Console.WriteLine($"Start an application with the following environment variable: DOTNET_DiagnosticPorts={fullPort}"); IpcEndpointInfo endpointInfo = await server.AcceptAsync(ct); return(new DiagnosticsClientHolder(new DiagnosticsClient(endpointInfo.Endpoint), endpointInfo, fullPort, server)); } else { return(new DiagnosticsClientHolder(new DiagnosticsClient(processId))); } }
/// <summary> /// Accepts endpoint infos from the reversed diagnostics server. /// </summary> /// <param name="token">The token to monitor for cancellation requests.</param> private async Task ListenAsync(int maxConnections, CancellationToken token) { _server.Start(maxConnections); // Continuously accept endpoint infos from the reversed diagnostics server so // that <see cref="ReversedDiagnosticsServer.AcceptAsync(CancellationToken)"/> // is always awaited in order to to handle new runtime instance connections // as well as existing runtime instance reconnections. while (!token.IsCancellationRequested) { try { IpcEndpointInfo info = await _server.AcceptAsync(token).ConfigureAwait(false); _ = Task.Run(() => ResumeAndQueueEndpointInfo(info, token), token); } catch (OperationCanceledException) { } } }
public async Task RunRemoteServer() { var debuggee = Path.GetFullPath(_debuggee); var diagnosticPort = $"Fusion_{Process.GetCurrentProcess().Id}"; // 1. Start the diagnostic server ReversedDiagnosticsServer srv = new ReversedDiagnosticsServer(diagnosticPort); srv.Start(); // 2. Start accepting connections using CancellationTokenSource cancellation = new CancellationTokenSource(20000); var acceptTask = srv.AcceptAsync(cancellation.Token); // 3. Run the debuggee Console.WriteLine("Starting child process using Diagnostic Port over Pipe"); ProcessStartInfo psi = new(debuggee); psi.CreateNoWindow = false; psi.EnvironmentVariables[_diagPortEnvKey] = diagnosticPort; var process = Process.Start(psi); // 4. Wait for the remote CLR to connect with us var endpoint = await acceptTask; Console.WriteLine($"Remote process {endpoint.ProcessId} connected with cookie: {endpoint.RuntimeInstanceCookie}"); // 5. Use the endpoint to start the diagnostic client using var fusion = new FusionTrace(endpoint.Endpoint); fusion.Start(e => SubscribeDynamicEvents(e), s => SubscribeRuntimeEvents(s)); while (Console.ReadKey().Key != ConsoleKey.Q) { ; } }