Remove endpoint info, runtime info, and command line helper. (#2371)
authorJustin Anderson <jander-msft@users.noreply.github.com>
Thu, 1 Jul 2021 04:19:45 +0000 (21:19 -0700)
committerGitHub <noreply@github.com>
Thu, 1 Jul 2021 04:19:45 +0000 (21:19 -0700)
src/Microsoft.Diagnostics.Monitoring/ClientEndpointInfoSource.cs [deleted file]
src/Microsoft.Diagnostics.Monitoring/CommandLineHelper.cs [deleted file]
src/Microsoft.Diagnostics.Monitoring/EndpointInfo.cs [deleted file]
src/Microsoft.Diagnostics.Monitoring/IEndpointInfoSource.cs [deleted file]
src/Microsoft.Diagnostics.Monitoring/RuntimeInfo.cs [deleted file]
src/Microsoft.Diagnostics.Monitoring/ServerEndpointInfoSource.cs [deleted file]
src/tests/Microsoft.Diagnostics.Monitoring/CommandLineHelperTests.cs [deleted file]
src/tests/Microsoft.Diagnostics.Monitoring/EndpointInfoSourceTests.cs [deleted file]

diff --git a/src/Microsoft.Diagnostics.Monitoring/ClientEndpointInfoSource.cs b/src/Microsoft.Diagnostics.Monitoring/ClientEndpointInfoSource.cs
deleted file mode 100644 (file)
index 7cac048..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Diagnostics.NETCore.Client;
-
-namespace Microsoft.Diagnostics.Monitoring
-{
-    internal sealed class ClientEndpointInfoSource : IEndpointInfoSourceInternal
-    {
-        public async Task<IEnumerable<IEndpointInfo>> GetEndpointInfoAsync(CancellationToken token)
-        {
-            var endpointInfoTasks = new List<Task<EndpointInfo>>();
-            // Run the EndpointInfo creation parallel. The call to FromProcessId sends
-            // a GetProcessInfo command to the runtime instance to get additional information.
-            foreach (int pid in DiagnosticsClient.GetPublishedProcesses())
-            {
-                endpointInfoTasks.Add(Task.Run(() =>
-                {
-                    try
-                    {
-                        return EndpointInfo.FromProcessId(pid);
-                    }
-                    //Catch when the application is running a more privilaged socket than dotnet-monitor. For example, running a web app as administrator
-                    //while running dotnet-monitor without elevation.
-                    catch (UnauthorizedAccessException)
-                    {
-                        return null;
-                    }
-                    //Most errors from IpcTransport, such as a stale socket.
-                    catch (ServerNotAvailableException)
-                    {
-                        return null;
-                    }
-                }, token));
-            }
-
-            await Task.WhenAll(endpointInfoTasks);
-
-            return endpointInfoTasks.Where(t => t.Result != null).Select(t => t.Result);
-        }
-    }
-}
diff --git a/src/Microsoft.Diagnostics.Monitoring/CommandLineHelper.cs b/src/Microsoft.Diagnostics.Monitoring/CommandLineHelper.cs
deleted file mode 100644 (file)
index 653467e..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Text;
-
-namespace Microsoft.Diagnostics.Monitoring
-{
-    internal class CommandLineHelper
-    {
-        public static string ExtractExecutablePath(string commandLine, bool isWindows)
-        {
-            if (string.IsNullOrEmpty(commandLine))
-            {
-                return commandLine;
-            }
-
-            int commandLineLength = commandLine.Length;
-            bool isQuoted = false;
-            bool isEscaped = false;
-            int i = 0;
-            char c = commandLine[0];
-
-            // Search for the first whitespace character that is not quoted.
-            // Store character literals as it iterates the command line. Escaped
-            // characters within double quotes are unescaped for non-Windows systems.
-            // Algorithm based on INIT_FormatCommandLine behavior from
-            // https://github.com/dotnet/runtime/blob/main/src/coreclr/pal/src/init/pal.cpp
-            StringBuilder builder = new StringBuilder(commandLineLength);
-            do
-            {
-                if (isEscaped)
-                {
-                    builder.Append(c);
-                    isEscaped = false;
-                }
-                else if (c == '"')
-                {
-                    isQuoted = !isQuoted;
-                }
-                else if (c == '\\' && !isWindows)
-                {
-                    if (isQuoted)
-                    {
-                        isEscaped = true;
-                    }
-                    else
-                    {
-                        builder.Append(c);
-                    }
-                }
-                else
-                {
-                    builder.Append(c);
-                }
-
-                if (commandLineLength == ++i)
-                {
-                    break;
-                }
-
-                c = commandLine[i];
-            }
-            while (isQuoted || !char.IsWhiteSpace(c));
-
-            return builder.ToString();
-        }
-    }
-}
diff --git a/src/Microsoft.Diagnostics.Monitoring/EndpointInfo.cs b/src/Microsoft.Diagnostics.Monitoring/EndpointInfo.cs
deleted file mode 100644 (file)
index dbac65f..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Diagnostics;
-using Microsoft.Diagnostics.NETCore.Client;
-
-namespace Microsoft.Diagnostics.Monitoring
-{
-    [DebuggerDisplay("{DebuggerDisplay,nq}")]
-    internal class EndpointInfo : IEndpointInfo
-    {
-        public static EndpointInfo FromProcessId(int processId)
-        {
-            var client = new DiagnosticsClient(processId);
-
-            ProcessInfo processInfo = null;
-            try
-            {
-                // Primary motivation is to get the runtime instance cookie in order to
-                // keep parity with the FromIpcEndpointInfo implementation; store the
-                // remainder of the information since it already has access to it.
-                processInfo = client.GetProcessInfo();
-
-                Debug.Assert(processId == unchecked((int)processInfo.ProcessId));
-            }
-            catch (ServerErrorException)
-            {
-                // The runtime likely doesn't understand the GetProcessInfo command.
-            }
-            catch (TimeoutException)
-            {
-                // Runtime didn't respond within client timeout.
-            }
-
-            // CONSIDER: Generate a runtime instance identifier based on the pipe name
-            // for .NET Core 3.1 e.g. pid + disambiguator in GUID form.
-            return new EndpointInfo()
-            {
-                Endpoint = new PidIpcEndpoint(processId),
-                ProcessId = processId,
-                RuntimeInstanceCookie = processInfo?.RuntimeInstanceCookie ?? Guid.Empty,
-                CommandLine = processInfo?.CommandLine,
-                OperatingSystem = processInfo?.OperatingSystem,
-                ProcessArchitecture = processInfo?.ProcessArchitecture
-            };
-        }
-
-        public static EndpointInfo FromIpcEndpointInfo(IpcEndpointInfo info)
-        {
-            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 = client.GetProcessInfo();
-
-                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
-            };
-        }
-
-        public IpcEndpoint Endpoint { get; private set; }
-
-        public int ProcessId { get; private set; }
-
-        public Guid RuntimeInstanceCookie { get; private set; }
-
-        public string CommandLine { get; private set; }
-
-        public string OperatingSystem { get; private set; }
-
-        public string ProcessArchitecture { get; private set; }
-
-        internal string DebuggerDisplay => FormattableString.Invariant($"PID={ProcessId}, Cookie={RuntimeInstanceCookie}");
-    }
-}
diff --git a/src/Microsoft.Diagnostics.Monitoring/IEndpointInfoSource.cs b/src/Microsoft.Diagnostics.Monitoring/IEndpointInfoSource.cs
deleted file mode 100644 (file)
index f3b90b5..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Diagnostics.NETCore.Client;
-
-namespace Microsoft.Diagnostics.Monitoring
-{
-    internal interface IEndpointInfo
-    {
-        IpcEndpoint Endpoint { get; }
-
-        int ProcessId { get; }
-
-        Guid RuntimeInstanceCookie { get; }
-
-        string CommandLine { get; }
-
-        string OperatingSystem { get; }
-
-        string ProcessArchitecture { get; }
-    }
-
-    public interface IEndpointInfoSource
-    {
-    }
-
-    internal interface IEndpointInfoSourceInternal : IEndpointInfoSource
-    {
-        Task<IEnumerable<IEndpointInfo>> GetEndpointInfoAsync(CancellationToken token);
-    }
-}
diff --git a/src/Microsoft.Diagnostics.Monitoring/RuntimeInfo.cs b/src/Microsoft.Diagnostics.Monitoring/RuntimeInfo.cs
deleted file mode 100644 (file)
index 270b170..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-using System;
-using System.IO;
-using System.Runtime.InteropServices;
-
-namespace Microsoft.Diagnostics.Monitoring
-{
-    public static class RuntimeInfo
-    {
-        public static bool IsDiagnosticsEnabled
-        {
-            get
-            {
-                string enableDiagnostics = Environment.GetEnvironmentVariable("COMPlus_EnableDiagnostics");
-                return string.IsNullOrEmpty(enableDiagnostics) || !"0".Equals(enableDiagnostics, StringComparison.Ordinal);
-            }
-        }
-
-        public static bool IsInDockerContainer
-        {
-            get
-            {
-                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
-                {
-                    // Check if one of the control groups of this process is owned by docker
-                    if (File.ReadAllText("/proc/self/cgroup").Contains("/docker/"))
-                    {
-                        return true;
-                    }
-
-                    // Most of the control groups are owned by "kubepods" when running in kubernetes;
-                    // Check for docker environment file
-                    return File.Exists("/.dockerenv");
-                }
-
-                // TODO: Add detection for other platforms
-                return false;
-            }
-        }
-
-        public static bool IsInKubernetes => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_HOST"));
-    }
-}
\ No newline at end of file
diff --git a/src/Microsoft.Diagnostics.Monitoring/ServerEndpointInfoSource.cs b/src/Microsoft.Diagnostics.Monitoring/ServerEndpointInfoSource.cs
deleted file mode 100644 (file)
index 615f4c9..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Diagnostics.NETCore.Client;
-
-namespace Microsoft.Diagnostics.Monitoring
-{
-    /// <summary>
-    /// Aggregates diagnostic endpoints that are established at a transport path via a reversed server.
-    /// </summary>
-    internal class ServerEndpointInfoSource : IEndpointInfoSourceInternal, IAsyncDisposable
-    {
-        // The amount of time to wait when checking if the a endpoint info should be
-        // pruned from the list of endpoint infos. If the runtime doesn't have a viable connection within
-        // this time, it will be pruned from the list.
-        private static readonly TimeSpan PruneWaitForConnectionTimeout = TimeSpan.FromMilliseconds(250);
-
-        private readonly CancellationTokenSource _cancellation = new CancellationTokenSource();
-        private readonly IList<EndpointInfo> _endpointInfos = new List<EndpointInfo>();
-        private readonly SemaphoreSlim _endpointInfosSemaphore = new SemaphoreSlim(1);
-        private readonly string _transportPath;
-
-        private Task _listenTask;
-        private bool _disposed = false;
-        private ReversedDiagnosticsServer _server;
-
-        /// <summary>
-        /// Constructs a <see cref="ServerEndpointInfoSource"/> that aggreates diagnostic endpoints
-        /// from a reversed diagnostics server at path specified by <paramref name="transportPath"/>.
-        /// </summary>
-        /// <param name="transportPath">
-        /// The path of the server endpoint.
-        /// On Windows, this can be a full pipe path or the name without the "\\.\pipe\" prefix.
-        /// On all other systems, this must be the full file path of the socket.
-        /// </param>
-        public ServerEndpointInfoSource(string transportPath)
-        {
-            _transportPath = transportPath;
-        }
-
-        public async ValueTask DisposeAsync()
-        {
-            if (!_disposed)
-            {
-                _cancellation.Cancel();
-
-                if (null != _listenTask)
-                {
-                    try
-                    {
-                        await _listenTask.ConfigureAwait(false);
-                    }
-                    catch (Exception ex)
-                    {
-                        Debug.Fail(ex.Message);
-                    }
-                }
-
-                if (null != _server)
-                {
-                    await _server.DisposeAsync().ConfigureAwait(false);
-                }
-
-                _endpointInfosSemaphore.Dispose();
-
-                _cancellation.Dispose();
-
-                _disposed = true;
-            }
-        }
-
-        /// <summary>
-        /// Starts listening to the reversed diagnostics server for new connections.
-        /// </summary>
-        public void Start()
-        {
-            Start(ReversedDiagnosticsServer.MaxAllowedConnections);
-        }
-
-        /// <summary>
-        /// Starts listening to the reversed diagnostics server for new connections.
-        /// </summary>
-        /// <param name="maxConnections">The maximum number of connections the server will support.</param>
-        public void Start(int maxConnections)
-        {
-            VerifyNotDisposed();
-
-            if (IsListening)
-            {
-                throw new InvalidOperationException(nameof(ServerEndpointInfoSource.Start) + " method can only be called once.");
-            }
-
-            _server = new ReversedDiagnosticsServer(_transportPath);
-
-            _listenTask = ListenAsync(maxConnections, _cancellation.Token);
-        }
-
-        /// <summary>
-        /// Gets the list of <see cref="IpcEndpointInfo"/> served from the reversed diagnostics server.
-        /// </summary>
-        /// <param name="token">The token to monitor for cancellation requests.</param>
-        /// <returns>A list of active <see cref="IEndpointInfo"/> instances.</returns>
-        public async Task<IEnumerable<IEndpointInfo>> GetEndpointInfoAsync(CancellationToken token)
-        {
-            VerifyNotDisposed();
-
-            VerifyIsListening();
-
-            using CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(token, _cancellation.Token);
-            CancellationToken linkedToken = linkedSource.Token;
-
-            // Prune connections that no longer have an active runtime instance before
-            // returning the list of connections.
-            await _endpointInfosSemaphore.WaitAsync(linkedToken).ConfigureAwait(false);
-
-            try
-            {
-                // Check the transport for each endpoint info and remove it if the check fails.
-                IDictionary<EndpointInfo, Task<bool>> checkMap = new Dictionary<EndpointInfo, Task<bool>>();
-                foreach (EndpointInfo info in _endpointInfos)
-                {
-                    checkMap.Add(info, Task.Run(() => CheckNotViable(info, linkedToken), linkedToken));
-                }
-
-                // Wait for all checks to complete
-                await Task.WhenAll(checkMap.Values).ConfigureAwait(false);
-
-                // Remove connections for failed checks
-                foreach (KeyValuePair<EndpointInfo, Task<bool>> entry in checkMap)
-                {
-                    if (entry.Value.Result)
-                    {
-                        _endpointInfos.Remove(entry.Key);
-                        OnRemovedEndpointInfo(entry.Key);
-                        _server?.RemoveConnection(entry.Key.RuntimeInstanceCookie);
-                    }
-                }
-
-                return _endpointInfos.ToList();
-            }
-            finally
-            {
-                _endpointInfosSemaphore.Release();
-            }
-        }
-
-        /// <summary>
-        /// Returns true if the connection is not longer viable.
-        /// </summary>
-        private static async Task<bool> CheckNotViable(EndpointInfo 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 report not viable if check was not cancelled.
-                if (!token.IsCancellationRequested)
-                {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        /// <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)
-                {
-                }
-            }
-        }
-
-        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;
-            }
-        }
-
-        internal virtual void OnAddedEndpointInfo(EndpointInfo info)
-        {
-        }
-
-        internal virtual void OnRemovedEndpointInfo(EndpointInfo info)
-        {
-        }
-
-        private void VerifyNotDisposed()
-        {
-            if (_disposed)
-            {
-                throw new ObjectDisposedException(nameof(ServerEndpointInfoSource));
-            }
-        }
-
-        private void VerifyIsListening()
-        {
-            if (!IsListening)
-            {
-                throw new InvalidOperationException(nameof(ServerEndpointInfoSource.Start) + " method must be called before invoking this operation.");
-            }
-        }
-
-        private bool IsListening => null != _server && null != _listenTask;
-    }
-}
diff --git a/src/tests/Microsoft.Diagnostics.Monitoring/CommandLineHelperTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring/CommandLineHelperTests.cs
deleted file mode 100644 (file)
index 2bd81cf..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.Diagnostics.Monitoring;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Microsoft.Diagnostics.Monitoring.UnitTests
-{
-    public class CommandLineHelperTests
-    {
-        private readonly ITestOutputHelper _output;
-
-        public CommandLineHelperTests(ITestOutputHelper output)
-        {
-            _output = output;
-        }
-
-        [Theory]
-        [InlineData(true, null, null)]
-        [InlineData(true, "", "")]
-        [InlineData(true, @"C:\NoArgs\test.exe", @"C:\NoArgs\test.exe")]
-        [InlineData(true, @"C:\WithArgs\test.exe arg1 arg2", @"C:\WithArgs\test.exe")]
-        [InlineData(true, @"""C:\With Space No Args\test.exe""", @"C:\With Space No Args\test.exe")]
-        [InlineData(true, @"""C:\With Space With Args\test.exe"" arg1 arg2", @"C:\With Space With Args\test.exe")]
-        [InlineData(true, @"C:\With'Quotes'No'Args\test.exe", @"C:\With'Quotes'No'Args\test.exe")]
-        [InlineData(true, @"C:\With'Quotes'With'Args\test.exe arg1 arg2", @"C:\With'Quotes'With'Args\test.exe")]
-        [InlineData(false, null, null)]
-        [InlineData(false, "", "")]
-        [InlineData(false, "/home/noargs/test", "/home/noargs/test")]
-        [InlineData(false, "/home/withargs/test arg1 arg2", "/home/withargs/test")]
-        [InlineData(false, @"""/home/with space no args/test""", "/home/with space no args/test")]
-        [InlineData(false, @"""/home/with space with args/test"" arg1 arg2", "/home/with space with args/test")]
-        [InlineData(false, @"""/home/escaped\\backslashes\\no\\args/test""", @"/home/escaped\backslashes\no\args/test")]
-        [InlineData(false, @"""/home/escaped\\backslashes\\with\\args/test"" arg1 arg2", @"/home/escaped\backslashes\with\args/test")]
-        [InlineData(false, @"""/home/escaped\""quotes\""no\""args/test""", @"/home/escaped""quotes""no""args/test")]
-        [InlineData(false, @"""/home/escaped\""quotes\""with\""args/test"" arg1 arg2", @"/home/escaped""quotes""with""args/test")]
-        public void CommandLineValidPathTest(bool isWindows, string commandLine, string expectedProcessPath)
-        {
-            Assert.Equal(expectedProcessPath, CommandLineHelper.ExtractExecutablePath(commandLine, isWindows));
-        }
-    }
-}
diff --git a/src/tests/Microsoft.Diagnostics.Monitoring/EndpointInfoSourceTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring/EndpointInfoSourceTests.cs
deleted file mode 100644 (file)
index c1f8871..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Diagnostics.NETCore.Client;
-using Microsoft.Diagnostics.NETCore.Client.UnitTests;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Microsoft.Diagnostics.Monitoring.UnitTests
-{
-    public class EndpointInfoSourceTests
-    {
-        // Generous timeout to allow APIs to respond on slower or more constrained machines
-        private static readonly TimeSpan DefaultPositiveVerificationTimeout = TimeSpan.FromSeconds(30);
-        private static readonly TimeSpan DefaultNegativeVerificationTimeout = TimeSpan.FromSeconds(2);
-
-        private readonly ITestOutputHelper _outputHelper;
-
-        public EndpointInfoSourceTests(ITestOutputHelper outputHelper)
-        {
-            _outputHelper = outputHelper;
-        }
-
-        /// <summary>
-        /// Tests that other <see cref="ServerEndpointInfoSource"> methods throw if
-        /// <see cref="ServerEndpointInfoSource.Start"/> is not called.
-        /// </summary>
-        [Fact]
-        public async Task ServerSourceNoStartTest()
-        {
-            await using var source = CreateServerSource(out string transportName);
-            // Intentionally do not call Start
-
-            using CancellationTokenSource cancellation = new CancellationTokenSource(DefaultNegativeVerificationTimeout);
-
-            await Assert.ThrowsAsync<InvalidOperationException>(
-                () => source.GetEndpointInfoAsync(cancellation.Token));
-        }
-
-        /// <summary>
-        /// Tests that the server endpoint info source has not connections if no processes connect to it.
-        /// </summary>
-        [Fact]
-        public async Task ServerSourceNoConnectionsTest()
-        {
-            await using var source = CreateServerSource(out _);
-            source.Start();
-
-            var endpointInfos = await GetEndpointInfoAsync(source);
-            Assert.Empty(endpointInfos);
-        }
-
-        /// <summary>
-        /// Tests that server endpoint info source should throw ObjectDisposedException
-        /// from API surface after being disposed.
-        /// </summary>
-        [Fact]
-        public async Task ServerSourceThrowsWhenDisposedTest()
-        {
-            var source = CreateServerSource(out _);
-            source.Start();
-
-            await source.DisposeAsync();
-
-            // Validate source surface throws after disposal
-            Assert.Throws<ObjectDisposedException>(
-                () => source.Start());
-
-            Assert.Throws<ObjectDisposedException>(
-                () => source.Start(1));
-
-            using var cancellation = new CancellationTokenSource(DefaultNegativeVerificationTimeout);
-            await Assert.ThrowsAsync<ObjectDisposedException>(
-                () => source.GetEndpointInfoAsync(cancellation.Token));
-        }
-
-        /// <summary>
-        /// Tests that server endpoint info source should throw an exception from
-        /// <see cref="ServerEndpointInfoSource.Start"/> and
-        /// <see cref="ServerEndpointInfoSource.Start(int)"/> after listening was already started.
-        /// </summary>
-        [Fact]
-        public async Task ServerSourceThrowsWhenMultipleStartTest()
-        {
-            await using var source = CreateServerSource(out _);
-            source.Start();
-
-            Assert.Throws<InvalidOperationException>(
-                () => source.Start());
-
-            Assert.Throws<InvalidOperationException>(
-                () => source.Start(1));
-        }
-
-        /// <summary>
-        /// Tests that the server endpoint info source can properly enumerate endpoint infos when a single
-        /// target connects to it and "disconnects" from it.
-        /// </summary>
-        [Fact]
-        public async Task ServerSourceAddRemoveSingleConnectionTest()
-        {
-            await using var source = CreateServerSource(out string transportName);
-            source.Start();
-
-            var endpointInfos = await GetEndpointInfoAsync(source);
-            Assert.Empty(endpointInfos);
-
-            Task newEndpointInfoTask = source.WaitForNewEndpointInfoAsync(DefaultPositiveVerificationTimeout);
-
-            await using (var execution1 = StartTraceeProcess("LoggerRemoteTest", transportName))
-            {
-                await newEndpointInfoTask;
-
-                execution1.SendSignal();
-
-                endpointInfos = await GetEndpointInfoAsync(source);
-
-                var endpointInfo = Assert.Single(endpointInfos);
-                Assert.NotNull(endpointInfo.CommandLine);
-                Assert.NotNull(endpointInfo.OperatingSystem);
-                Assert.NotNull(endpointInfo.ProcessArchitecture);
-                VerifyConnection(execution1.TestRunner, endpointInfo);
-
-                _outputHelper.WriteLine("Stopping tracee.");
-            }
-
-            await Task.Delay(TimeSpan.FromSeconds(1));
-
-            endpointInfos = await GetEndpointInfoAsync(source);
-
-            Assert.Empty(endpointInfos);
-        }
-
-        /// <summary>
-        /// Tests that the server endpoint info source can properly enumerate endpoint infos when multiple
-        /// targets connect to it and "disconnect" from it.
-        /// </summary>
-        [Fact]
-        public async Task ServerSourceAddRemoveMultipleConnectionTest()
-        {
-            await using var source = CreateServerSource(out string transportName);
-            source.Start();
-
-            var endpointInfos = await GetEndpointInfoAsync(source);
-            Assert.Empty(endpointInfos);
-
-            const int appCount = 5;
-            RemoteTestExecution[] executions = new RemoteTestExecution[appCount];
-
-            try
-            {
-                // Start all app instances
-                for (int i = 0; i < appCount; i++)
-                {
-                    Task newEndpointInfoTask = source.WaitForNewEndpointInfoAsync(DefaultPositiveVerificationTimeout);
-
-                    executions[i] = StartTraceeProcess("LoggerRemoteTest", transportName);
-
-                    await newEndpointInfoTask;
-
-                    executions[i].SendSignal();
-                }
-
-                await Task.Delay(TimeSpan.FromSeconds(1));
-
-                endpointInfos = await GetEndpointInfoAsync(source);
-
-                Assert.Equal(appCount, endpointInfos.Count());
-
-                for (int i = 0; i < appCount; i++)
-                {
-                    IEndpointInfo endpointInfo = endpointInfos.FirstOrDefault(info => info.ProcessId == executions[i].TestRunner.Pid);
-                    Assert.NotNull(endpointInfo);
-                    Assert.NotNull(endpointInfo.CommandLine);
-                    Assert.NotNull(endpointInfo.OperatingSystem);
-                    Assert.NotNull(endpointInfo.ProcessArchitecture);
-
-                    VerifyConnection(executions[i].TestRunner, endpointInfo);
-                }
-            }
-            finally
-            {
-                _outputHelper.WriteLine("Stopping tracees.");
-
-                int executionCount = 0;
-                for (int i = 0; i < appCount; i++)
-                {
-                    if (null != executions[i])
-                    {
-                        executionCount++;
-                        await executions[i].DisposeAsync();
-                    }
-                }
-                Assert.Equal(appCount, executionCount);
-            }
-
-            await Task.Delay(TimeSpan.FromSeconds(1));
-
-            endpointInfos = await GetEndpointInfoAsync(source);
-
-            Assert.Empty(endpointInfos);
-        }
-
-        private TestServerEndpointInfoSource CreateServerSource(out string transportName)
-        {
-            transportName = ReversedServerHelper.CreateServerTransportName();
-            _outputHelper.WriteLine("Starting server endpoint info source at '" + transportName + "'.");
-            return new TestServerEndpointInfoSource(transportName, _outputHelper);
-        }
-
-        private RemoteTestExecution StartTraceeProcess(string loggerCategory, string transportName = null)
-        {
-            _outputHelper.WriteLine("Starting tracee.");
-            string exePath = CommonHelper.GetTraceePathWithArgs("EventPipeTracee", targetFramework: "net5.0");
-            return RemoteTestExecution.StartProcess(exePath + " " + loggerCategory, _outputHelper, transportName);
-        }
-
-        private async Task<IEnumerable<IEndpointInfo>> GetEndpointInfoAsync(ServerEndpointInfoSource source)
-        {
-            _outputHelper.WriteLine("Getting endpoint infos.");
-            using CancellationTokenSource cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10));
-            return await source.GetEndpointInfoAsync(cancellationSource.Token);
-        }
-
-        /// <summary>
-        /// Verifies basic information on the connection and that it matches the target process from the runner.
-        /// </summary>
-        private static void VerifyConnection(TestRunner runner, IEndpointInfo endpointInfo)
-        {
-            Assert.NotNull(runner);
-            Assert.NotNull(endpointInfo);
-            Assert.Equal(runner.Pid, endpointInfo.ProcessId);
-            Assert.NotEqual(Guid.Empty, endpointInfo.RuntimeInstanceCookie);
-            Assert.NotNull(endpointInfo.Endpoint);
-        }
-
-        private sealed class TestServerEndpointInfoSource : ServerEndpointInfoSource
-        {
-            private readonly ITestOutputHelper _outputHelper;
-            private readonly List<TaskCompletionSource<EndpointInfo>> _addedEndpointInfoSources = new List<TaskCompletionSource<EndpointInfo>>();
-
-            public TestServerEndpointInfoSource(string transportPath, ITestOutputHelper outputHelper)
-                : base(transportPath)
-            {
-                _outputHelper = outputHelper;
-            }
-
-            public async Task<EndpointInfo> WaitForNewEndpointInfoAsync(TimeSpan timeout)
-            {
-                TaskCompletionSource<EndpointInfo> addedEndpointInfoSource = new TaskCompletionSource<EndpointInfo>(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);
-                EndpointInfo endpointInfo = await addedEndpointInfoSource.Task;
-                _outputHelper.WriteLine("Notified of new endpoint info.");
-
-                return endpointInfo;
-            }
-
-            internal override void OnAddedEndpointInfo(EndpointInfo info)
-            {
-                _outputHelper.WriteLine($"Added endpoint info to collection: {info.DebuggerDisplay}");
-                
-                lock (_addedEndpointInfoSources)
-                {
-                    foreach (var source in _addedEndpointInfoSources)
-                    {
-                        source.TrySetResult(info);
-                    }
-                    _addedEndpointInfoSources.Clear();
-                }
-            }
-
-            internal override void OnRemovedEndpointInfo(EndpointInfo info)
-            {
-                _outputHelper.WriteLine($"Removed endpoint info from collection: {info.DebuggerDisplay}");
-            }
-        }
-    }
-}