Exemplo n.º 1
0
 internal ServerDispatcher(ICompilerServerHost compilerServerHost, IClientConnectionHost clientConnectionHost, IDiagnosticListener?diagnosticListener = null)
 {
     _compilerServerHost   = compilerServerHost;
     _logger               = compilerServerHost.Logger;
     _clientConnectionHost = clientConnectionHost;
     _diagnosticListener   = diagnosticListener ?? new EmptyDiagnosticListener();
 }
Exemplo n.º 2
0
        internal static ServerData Create(
            ICompilerServerLogger logger,
            string pipeName = null,
            ICompilerServerHost compilerServerHost     = null,
            IClientConnectionHost clientConnectionHost = null,
            TimeSpan?keepAlive = null)
        {
            // The total pipe path must be < 92 characters on Unix, so trim this down to 10 chars
            pipeName ??= ServerUtil.GetPipeName();
            compilerServerHost ??= BuildServerController.CreateCompilerServerHost(logger);
            clientConnectionHost ??= BuildServerController.CreateClientConnectionHost(pipeName, logger);
            keepAlive ??= TimeSpan.FromMilliseconds(-1);

            var listener           = new TestableDiagnosticListener();
            var serverListenSource = new TaskCompletionSource <bool>();
            var cts       = new CancellationTokenSource();
            var mutexName = BuildServerConnection.GetServerMutexName(pipeName);
            var task      = Task.Run(() =>
            {
                BuildServerController.CreateAndRunServer(
                    pipeName,
                    compilerServerHost,
                    clientConnectionHost,
                    listener,
                    keepAlive: keepAlive,
                    cancellationToken: cts.Token);
                return(listener);
            });

            return(new ServerData(cts, pipeName, logger, task));
        }
Exemplo n.º 3
0
        /// <summary>
        /// Shutting down the server is an inherently racy operation.  The server can be started or stopped by
        /// external parties at any time.
        ///
        /// This function will return success if at any time in the function the server is determined to no longer
        /// be running.
        /// </summary>
        internal static async Task <bool> RunServerShutdownRequestAsync(
            string pipeName,
            int?timeoutOverride,
            bool waitForProcess,
            ICompilerServerLogger logger,
            CancellationToken cancellationToken)
        {
            if (wasServerRunning(pipeName) == false)
            {
                // The server holds the mutex whenever it is running, if it's not open then the
                // server simply isn't running.
                return(true);
            }

            try
            {
                var request = BuildRequest.CreateShutdown();

                // Don't create the server when sending a shutdown request. That would defeat the
                // purpose a bit.
                var response = await RunServerBuildRequestAsync(
                    request,
                    pipeName,
                    timeoutOverride,
                    tryCreateServerFunc : (_, _) => false,
                    logger,
                    cancellationToken).ConfigureAwait(false);

                if (response is ShutdownBuildResponse shutdownBuildResponse)
                {
                    if (waitForProcess)
                    {
                        try
                        {
                            var process = Process.GetProcessById(shutdownBuildResponse.ServerProcessId);
#if NET5_0_OR_GREATER
                            await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
#else
                            process.WaitForExit();
#endif
                        }
                        catch (Exception)
                        {
                            // There is an inherent race here with the server process.  If it has already shutdown
                            // by the time we try to access it then the operation has succeed.
                        }
                    }

                    return(true);
                }

                return(wasServerRunning(pipeName) == false);
            }
            catch (Exception)
            {
                // If the server was in the process of shutting down when we connected then it's reasonable
                // for an exception to happen.  If the mutex has shutdown at this point then the server
                // is shut down.
                return(wasServerRunning(pipeName) == false);
            }
        public ulong Reserved; //always 0

        public static bool IsMemoryAvailable(ICompilerServerLogger logger)
        {
            if (!PlatformInformation.IsWindows)
            {
                // assume we have enough memory on non-Windows machines
                return(true);
            }

            MemoryHelper status = new MemoryHelper();

            GlobalMemoryStatusEx(status);
            ulong max  = status.MaxVirtual;
            ulong free = status.AvailableVirtual;

            int    shift = 20;
            string unit  = "MB";

            if (free >> shift == 0)
            {
                shift = 10;
                unit  = "KB";
            }

            logger.Log("Free memory: {1}{0} of {2}{0}.", unit, free >> shift, max >> shift);

            return(free >= 800 << 20); // Value (500MB) is arbitrary; feel free to improve.
        }
Exemplo n.º 5
0
 internal static void Log(this ICompilerServerLogger logger, string format, params object?[] arguments)
 {
     if (logger.IsLogging)
     {
         logger.Log(string.Format(format, arguments));
     }
 }
Exemplo n.º 6
0
        internal static ICompilerServerHost CreateCompilerServerHost(ICompilerServerLogger logger)
        {
            var clientDirectory = BuildClient.GetClientDirectory();
            var sdkDirectory    = BuildClient.GetSystemSdkDirectory();

            return(new CompilerServerHost(clientDirectory, sdkDirectory, logger));
        }
Exemplo n.º 7
0
        /// <summary>
        /// Log an exception. Also logs information about inner exceptions.
        /// </summary>
        internal static void LogException(this ICompilerServerLogger logger, Exception exception, string reason)
        {
            if (!logger.IsLogging)
            {
                return;
            }

            var builder = new StringBuilder();

            builder.Append("Error ");
            AppendException(exception);

            int       innerExceptionLevel = 0;
            Exception?e = exception.InnerException;

            while (e != null)
            {
                builder.Append($"Inner exception[{innerExceptionLevel}]  ");
                AppendException(e);
                e = e.InnerException;
                innerExceptionLevel += 1;
            }

            logger.Log(builder.ToString());

            void AppendException(Exception exception)
            {
                builder.AppendLine($"Error: '{exception.GetType().Name}' '{exception.Message}' occurred during '{reason}'");
                builder.AppendLine("Stack trace:");
                builder.AppendLine(exception.StackTrace);
            }
        }
Exemplo n.º 8
0
        /// <summary>
        /// Used to log a message that should go into both the compiler server log as well as the MSBuild logs
        ///
        /// These are intended to be processed by automation in the binlog hence do not change the structure of
        /// the messages here.
        /// </summary>
        private void LogCompilationMessage(
            ICompilerServerLogger logger,
            Guid requestId,
            CompilationKind kind,
            string diagnostic
            )
        {
            var category = kind switch
            {
                CompilationKind.Server => "server",
                CompilationKind.Tool => "tool",
                CompilationKind.ToolFallback => "server failed",
                CompilationKind.FatalError => "fatal error",
                _ => throw new Exception($"Unexpected value {kind}"),
            };

            var message = $"CompilerServer: {category} - {diagnostic} - {requestId}";

            if (kind == CompilationKind.FatalError)
            {
                logger.LogError(message);
                Log.LogError(message);
            }
            else
            {
                logger.Log(message);
                Log.LogMessage(message);
            }
        }
Exemplo n.º 9
0
        public static Task <BuildResponse> RunServerCompilationAsync(
            Guid requestId,
            RequestLanguage language,
            string?sharedCompilationId,
            List <string> arguments,
            BuildPathsAlt buildPaths,
            string?keepAlive,
            string?libEnvVariable,
            ICompilerServerLogger logger,
            CancellationToken cancellationToken)
        {
            var pipeNameOpt = sharedCompilationId ?? GetPipeNameForPath(buildPaths.ClientDirectory);

            return(RunServerCompilationCoreAsync(
                       requestId,
                       language,
                       arguments,
                       buildPaths,
                       pipeNameOpt,
                       keepAlive,
                       libEnvVariable,
                       timeoutOverride: null,
                       createServerFunc: TryCreateServerCore,
                       logger: logger,
                       cancellationToken: cancellationToken));
        }
        /// <summary>
        /// Creates a Task that waits for a client connection to occur and returns the connected
        /// <see cref="NamedPipeServerStream"/> object.  Throws on any connection error.
        /// </summary>
        /// <param name="cancellationToken">Used to cancel the connection sequence.</param>
        private static async Task ListenCoreAsync(
            string pipeName,
            ICompilerServerLogger logger,
            AsyncQueue <ListenResult> queue,
            CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                NamedPipeServerStream?pipeStream = null;

                try
                {
                    // Create the pipe and begin waiting for a connection. This
                    // doesn't block, but could fail in certain circumstances, such
                    // as Windows refusing to create the pipe for some reason
                    // (out of handles?), or the pipe was disconnected before we
                    // starting listening
                    logger.Log($"Constructing pipe and waiting for connections '{pipeName}'");
                    pipeStream = NamedPipeUtil.CreateServer(pipeName);

                    // The WaitForConnectionAsync API does not fully respect the provided CancellationToken
                    // on all platforms:
                    //
                    //  https://github.com/dotnet/runtime/issues/40289
                    //
                    // To mitigate this we need to setup a cancellation Task and dispose the NamedPipeServerStream
                    // if it ever completes. Once all of the NamedPipeServerStream for the given pipe name are
                    // disposed they will all exit the WaitForConnectionAsync method
                    var connectTask = pipeStream.WaitForConnectionAsync(cancellationToken);
                    if (!PlatformInformation.IsWindows)
                    {
                        var cancelTask    = Task.Delay(TimeSpan.FromMilliseconds(-1), cancellationToken);
                        var completedTask = await Task.WhenAny(new[] { connectTask, cancelTask }).ConfigureAwait(false);

                        if (completedTask == cancelTask)
                        {
                            throw new OperationCanceledException();
                        }
                    }

                    await connectTask.ConfigureAwait(false);

                    logger.Log("Pipe connection established.");
                    var connection = new NamedPipeClientConnection(pipeStream, logger);
                    queue.Enqueue(new ListenResult(connection: connection));
                }
                catch (OperationCanceledException)
                {
                    // Expected when the host is shutting down.
                    logger.Log($"Pipe connection cancelled");
                    pipeStream?.Dispose();
                }
                catch (Exception ex)
                {
                    logger.LogException(ex, $"Pipe connection error");
                    queue.Enqueue(new ListenResult(exception: ex));
                    pipeStream?.Dispose();
                }
            }
        }
Exemplo n.º 11
0
 private ServerData(CancellationTokenSource cancellationTokenSource, string pipeName, ICompilerServerLogger logger, Task <TestableDiagnosticListener> serverTask)
 {
     CancellationTokenSource = cancellationTokenSource;
     PipeName   = pipeName;
     Logger     = logger;
     ServerTask = serverTask;
 }
Exemplo n.º 12
0
 internal static void LogError(this ICompilerServerLogger logger, string format, params object?[] arguments)
 {
     if (logger.IsLogging)
     {
         logger.Log($"Error: {format}", arguments);
     }
 }
Exemplo n.º 13
0
 internal BuildServerController(
     NameValueCollection appSettings,
     ICompilerServerLogger logger
     )
 {
     _appSettings = appSettings;
     _logger      = logger;
 }
Exemplo n.º 14
0
 internal TestableCompilerServerHost(
     Func <RunRequest, CancellationToken, BuildResponse> runCompilation = null,
     ICompilerServerLogger logger = null
     )
 {
     RunCompilation = runCompilation;
     Logger         = logger ?? EmptyCompilerServerLogger.Instance;
 }
Exemplo n.º 15
0
 internal NamedPipeClientConnection(
     NamedPipeServerStream stream,
     ICompilerServerLogger logger
     )
 {
     Stream = stream;
     Logger = logger;
 }
Exemplo n.º 16
0
        internal static async Task <BuildResponse> RunServerCompilationCoreAsync(
            Guid requestId,
            RequestLanguage language,
            List <string> arguments,
            BuildPathsAlt buildPaths,
            string?pipeName,
            string?keepAlive,
            string?libDirectory,
            int?timeoutOverride,
            CreateServerFunc createServerFunc,
            ICompilerServerLogger logger,
            CancellationToken cancellationToken)
        {
            if (pipeName is null)
            {
                throw new ArgumentException(nameof(pipeName));
            }

            if (buildPaths.TempDirectory == null)
            {
                throw new ArgumentException(nameof(buildPaths));
            }

            // early check for the build hash. If we can't find it something is wrong; no point even trying to go to the server
            if (string.IsNullOrWhiteSpace(BuildProtocolConstants.GetCommitHash()))
            {
                return(new IncorrectHashBuildResponse());
            }

            var pipeTask = tryConnectToServer(pipeName, buildPaths, timeoutOverride, createServerFunc, logger, cancellationToken);

            if (pipeTask is null)
            {
                return(new RejectedBuildResponse("Failed to connect to server"));
            }
            else
            {
                using var pipe = await pipeTask.ConfigureAwait(false);

                if (pipe is null)
                {
                    return(new RejectedBuildResponse("Failed to connect to server"));
                }
                else
                {
                    var request = BuildRequest.Create(language,
                                                      arguments,
                                                      workingDirectory: buildPaths.WorkingDirectory,
                                                      tempDirectory: buildPaths.TempDirectory,
                                                      compilerHash: BuildProtocolConstants.GetCommitHash() ?? "",
                                                      requestId: requestId,
                                                      keepAlive: keepAlive,
                                                      libDirectory: libDirectory);

                    return(await TryCompileAsync(pipe, request, logger, cancellationToken).ConfigureAwait(false));
                }
            }
Exemplo n.º 17
0
 internal CompilerServerHost(
     string clientDirectory,
     string sdkDirectory,
     ICompilerServerLogger logger
     )
 {
     ClientDirectory = clientDirectory;
     SdkDirectory    = sdkDirectory;
     Logger          = logger;
 }
Exemplo n.º 18
0
 internal static BuildClient CreateBuildClient(
     RequestLanguage language,
     ICompilerServerLogger logger,
     CompileFunc compileFunc = null,
     TextWriter textWriter   = null,
     int?timeoutOverride     = null)
 {
     compileFunc = compileFunc ?? GetCompileFunc(language);
     textWriter  = textWriter ?? new StringWriter();
     return(new BuildClient(language, compileFunc, logger, timeoutOverride: timeoutOverride));
 }
Exemplo n.º 19
0
        internal static async Task <ServerData> CreateServer(
            ICompilerServerLogger logger,
            string pipeName = null,
            ICompilerServerHost compilerServerHost     = null,
            IClientConnectionHost clientConnectionHost = null,
            TimeSpan?keepAlive = null)
        {
            var serverData = ServerData.Create(logger, pipeName, compilerServerHost, clientConnectionHost, keepAlive);
            await serverData.WaitForServerAsync();

            return(serverData);
        }
Exemplo n.º 20
0
        internal static ICompilerServerHost CreateCompilerServerHost(ICompilerServerLogger logger)
        {
            // VBCSCompiler is installed in the same directory as csc.exe and vbc.exe which is also the
            // location of the response files.
            //
            // BaseDirectory was mistakenly marked as potentially null in 3.1
            // https://github.com/dotnet/runtime/pull/32486
            var clientDirectory = AppDomain.CurrentDomain.BaseDirectory !;
            var sdkDirectory    = BuildClient.GetSystemSdkDirectory();

            return(new CompilerServerHost(clientDirectory, sdkDirectory, logger));
        }
Exemplo n.º 21
0
        internal static async Task <ServerData> CreateServer(
            ICompilerServerLogger logger,
            string pipeName = null,
            ICompilerServerHost compilerServerHost     = null,
            IClientConnectionHost clientConnectionHost = null,
            TimeSpan?keepAlive = null
            )
        {
            // The total pipe path must be < 92 characters on Unix, so trim this down to 10 chars
            pipeName ??= GetPipeName();
            compilerServerHost ??= BuildServerController.CreateCompilerServerHost(logger);
            clientConnectionHost ??= BuildServerController.CreateClientConnectionHost(
                pipeName,
                logger
                );
            keepAlive ??= TimeSpan.FromMilliseconds(-1);

            var listener           = new TestableDiagnosticListener();
            var serverListenSource = new TaskCompletionSource <bool>();
            var cts       = new CancellationTokenSource();
            var mutexName = BuildServerConnection.GetServerMutexName(pipeName);
            var task      = Task.Run(
                () =>
            {
                BuildServerController.CreateAndRunServer(
                    pipeName,
                    compilerServerHost,
                    clientConnectionHost,
                    listener,
                    keepAlive: keepAlive,
                    cancellationToken: cts.Token
                    );
                return(listener);
            }
                );

            // The contract of this function is that it will return once the server has started.  Spin here until
            // we can verify the server has started or simply failed to start.
            while (BuildServerConnection.WasServerMutexOpen(mutexName) != true && !task.IsCompleted)
            {
                await Task.Yield();
            }

            return(new ServerData(cts, pipeName, logger, task));
        }
Exemplo n.º 22
0
        internal static BuildClient CreateBuildClient(
            RequestLanguage language,
            ICompilerServerLogger logger)
        {
            // Create a client to run the build.  Infinite timeout is used to account for the
            // case where these tests are run under extreme load.  In high load scenarios the
            // client will correctly drop down to a local compilation if the server doesn't respond
            // fast enough.
            CompileOnServerFunc compileOnServerFunc = (request, pipeName, cancellationToken) =>
                                                      BuildServerConnection.RunServerBuildRequestAsync(
                request,
                pipeName,
                timeoutOverride: Timeout.Infinite,
                tryCreateServerFunc: (_, _) => false,
                logger,
                cancellationToken);

            var compileFunc = GetCompileFunc(language);

            return(new BuildClient(language, compileFunc, compileOnServerFunc));
        }
Exemplo n.º 23
0
        internal static int Run(
            IEnumerable <string> arguments,
            RequestLanguage language,
            CompileFunc compileFunc,
            ICompilerServerLogger logger,
            Guid?requestId = null
            )
        {
            var sdkDir = GetSystemSdkDirectory();

            if (RuntimeHostInfo.IsCoreClrRuntime)
            {
                // Register encodings for console
                // https://github.com/dotnet/roslyn/issues/10785
                System.Text.Encoding.RegisterProvider(
                    System.Text.CodePagesEncodingProvider.Instance
                    );
            }

            var client     = new BuildClient(language, compileFunc, logger);
            var clientDir  = AppContext.BaseDirectory;
            var workingDir = Directory.GetCurrentDirectory();
            var tempDir    = BuildServerConnection.GetTempPath(workingDir);
            var buildPaths = new BuildPaths(
                clientDir: clientDir,
                workingDir: workingDir,
                sdkDir: sdkDir,
                tempDir: tempDir
                );
            var originalArguments = GetCommandLineArgs(arguments);

            return(client.RunCompilation(
                       originalArguments,
                       buildPaths,
                       requestId: requestId
                       ).ExitCode);
        }
Exemplo n.º 24
0
        /// <summary>
        /// Handle a response from the server, reporting messages and returning
        /// the appropriate exit code.
        /// </summary>
        private int HandleResponse(BuildResponse response, string pathToTool, string responseFileCommands, string commandLineCommands, ICompilerServerLogger logger)
        {
            if (response.Type != BuildResponse.ResponseType.Completed)
            {
                ValidateBootstrapUtil.AddFailedServerConnection(response.Type, OutputAssembly?.ItemSpec);
            }

            switch (response.Type)
            {
            case BuildResponse.ResponseType.Completed:
                var completedResponse = (CompletedBuildResponse)response;
                LogMessages(completedResponse.Output, StandardOutputImportanceToUse);

                if (LogStandardErrorAsError)
                {
                    LogErrorMultiline(completedResponse.ErrorOutput);
                }
                else
                {
                    LogMessages(completedResponse.ErrorOutput, StandardErrorImportanceToUse);
                }

                return(completedResponse.ReturnCode);

            case BuildResponse.ResponseType.MismatchedVersion:
                logError("Roslyn compiler server reports different protocol version than build task.");
                return(base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands));

            case BuildResponse.ResponseType.IncorrectHash:
                logError("Roslyn compiler server reports different hash version than build task.");
                return(base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands));

            case BuildResponse.ResponseType.Rejected:
            case BuildResponse.ResponseType.AnalyzerInconsistency:
                logger.LogError($"Server rejected request {response.Type}");
                return(base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands));

            default:
                logError($"Received an unrecognized response from the server: {response.Type}");
                return(base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands));
            }

            void logError(string message)
            {
                logger.LogError(message);
                Log.LogError(message);
            }
        }
Exemplo n.º 25
0
 internal static IClientConnectionHost CreateClientConnectionHost(string pipeName, ICompilerServerLogger logger) => new NamedPipeClientConnectionHost(pipeName, logger);
 internal NamedPipeClientConnectionHost(string pipeName, ICompilerServerLogger logger)
 {
     PipeName = pipeName;
     Logger   = logger;
 }