Ejemplo n.º 1
0
 public DiagnosticsClientHolder(DiagnosticsClient client, IpcEndpointInfo endpointInfo, string port, ReversedDiagnosticsServer server)
 {
     Client       = client;
     EndpointInfo = endpointInfo;
     _port        = port;
     _server      = server;
 }
Ejemplo n.º 2
0
        public static async Task <EndpointInfo> FromIpcEndpointInfoAsync(IpcEndpointInfo info, CancellationToken token)
        {
            var client = new DiagnosticsClient(info.Endpoint);

            ProcessInfo processInfo = null;

            try
            {
                // Primary motivation is to keep parity with the FromProcessId implementation,
                // which provides the additional process information because it already has
                // access to it.
                processInfo = await client.GetProcessInfoAsync(token);

                Debug.Assert(info.ProcessId == unchecked ((int)processInfo.ProcessId));
                Debug.Assert(info.RuntimeInstanceCookie == processInfo.RuntimeInstanceCookie);
            }
            catch (ServerErrorException)
            {
                // The runtime likely doesn't understand the GetProcessInfo command.
            }
            catch (TimeoutException)
            {
                // Runtime didn't respond within client timeout.
            }

            return(new EndpointInfo()
            {
                Endpoint = info.Endpoint,
                ProcessId = info.ProcessId,
                RuntimeInstanceCookie = info.RuntimeInstanceCookie,
                CommandLine = processInfo?.CommandLine,
                OperatingSystem = processInfo?.OperatingSystem,
                ProcessArchitecture = processInfo?.ProcessArchitecture
            });
        }
Ejemplo n.º 3
0
        // <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));
        }
            internal override void OnAddedEndpointInfo(IpcEndpointInfo info)
            {
                _outputHelper.WriteLine($"Added endpoint info to collection: {info.ToTestString()}");

                lock (_addedEndpointInfoSources)
                {
                    foreach (var source in _addedEndpointInfoSources)
                    {
                        source.TrySetResult(info);
                    }
                    _addedEndpointInfoSources.Clear();
                }
            }
        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(CancellationToken token)
        {
            // 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)
                {
                }
            }
        }
        private async Task ResumeAndQueueEndpointInfo(IpcEndpointInfo info, CancellationToken token)
        {
            try
            {
                // Send ResumeRuntime message for runtime instances that connect to the server. This will allow
                // those instances that are configured to pause on start to resume after the diagnostics
                // connection has been made. Instances that are not configured to pause on startup will ignore
                // the command and return success.
                var client = new DiagnosticsClient(info.Endpoint);
                try
                {
                    client.ResumeRuntime();
                }
                catch (ServerErrorException)
                {
                    // The runtime likely doesn't understand the ResumeRuntime command.
                }

                EndpointInfo endpointInfo = EndpointInfo.FromIpcEndpointInfo(info);

                await _endpointInfosSemaphore.WaitAsync(token).ConfigureAwait(false);

                try
                {
                    _endpointInfos.Add(endpointInfo);

                    OnAddedEndpointInfo(endpointInfo);
                }
                finally
                {
                    _endpointInfosSemaphore.Release();
                }
            }
            catch (Exception)
            {
                _server?.RemoveConnection(info.RuntimeInstanceCookie);

                throw;
            }
        }
            public async Task <IpcEndpointInfo> WaitForNewEndpointInfoAsync(TimeSpan timeout)
            {
                TaskCompletionSource <IpcEndpointInfo> addedEndpointInfoSource = new TaskCompletionSource <IpcEndpointInfo>(TaskCreationOptions.RunContinuationsAsynchronously);

                using var timeoutCancellation = new CancellationTokenSource();
                var token = timeoutCancellation.Token;

                using var _ = token.Register(() => addedEndpointInfoSource.TrySetCanceled(token));

                lock (_addedEndpointInfoSources)
                {
                    _addedEndpointInfoSources.Add(addedEndpointInfoSource);
                }

                _outputHelper.WriteLine("Waiting for new endpoint info.");
                timeoutCancellation.CancelAfter(timeout);
                IpcEndpointInfo endpointInfo = await addedEndpointInfoSource.Task;

                _outputHelper.WriteLine("Notified of new endpoint info.");

                return(endpointInfo);
            }
        private async Task PruneIfNotViable(IpcEndpointInfo info, CancellationToken token)
        {
            using var timeoutSource = new CancellationTokenSource();
            using var linkedSource  = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutSource.Token);

            try
            {
                timeoutSource.CancelAfter(PruneWaitForConnectionTimeout);

                await info.Endpoint.WaitForConnectionAsync(linkedSource.Token).ConfigureAwait(false);
            }
            catch
            {
                // Only remove the endpoint info if due to some exception
                // other than cancelling the pruning operation.
                if (!token.IsCancellationRequested)
                {
                    _endpointInfos.Remove(info);
                    OnRemovedEndpointInfo(info);
                    _server.RemoveConnection(info.RuntimeInstanceCookie);
                }
            }
        }
 internal override void OnRemovedEndpointInfo(IpcEndpointInfo info)
 {
     _outputHelper.WriteLine($"Removed endpoint info from collection: {info.ToTestString()}");
 }
 public EndpointInfo(IpcEndpointInfo info)
 {
     _info = info;
 }
 internal virtual void OnRemovedEndpointInfo(IpcEndpointInfo info)
 {
 }
 internal virtual void OnAddedEndpointInfo(IpcEndpointInfo info)
 {
 }