Example #1
0
        /// <summary>
        /// Start the debug server listening.
        /// </summary>
        /// <returns>A task that completes when the server is ready.</returns>
        public async Task StartAsync()
        {
            _jsonRpcServer = await JsonRpcServer.From(options =>
            {
                options.Serializer    = new DapProtocolSerializer();
                options.Receiver      = new DapReceiver();
                options.LoggerFactory = _loggerFactory;
                ILogger logger        = options.LoggerFactory.CreateLogger("DebugOptionsStartup");

                // We need to let the PowerShell Context Service know that we are in a debug session
                // so that it doesn't send the powerShell/startDebugger message.
                _powerShellContextService = ServiceProvider.GetService <PowerShellContextService>();
                _powerShellContextService.IsDebugServerActive = true;

                // Needed to make sure PSReadLine's static properties are initialized in the pipeline thread.
                // This is only needed for Temp sessions who only have a debug server.
                if (_usePSReadLine && _useTempSession && Interlocked.Exchange(ref s_hasRunPsrlStaticCtor, 1) == 0)
                {
                    // This must be run synchronously to ensure debugging works
                    _powerShellContextService
                    .ExecuteScriptStringAsync("[System.Runtime.CompilerServices.RuntimeHelpers]::RunClassConstructor([Microsoft.PowerShell.PSConsoleReadLine].TypeHandle)")
                    .GetAwaiter()
                    .GetResult();
                }

                options.Services = new ServiceCollection()
                                   .AddPsesDebugServices(ServiceProvider, this, _useTempSession);

                options
                .WithInput(_inputStream)
                .WithOutput(_outputStream);

                logger.LogInformation("Adding handlers");

                options
                .WithHandler <InitializeHandler>()
                .WithHandler <LaunchHandler>()
                .WithHandler <AttachHandler>()
                .WithHandler <DisconnectHandler>()
                .WithHandler <SetFunctionBreakpointsHandler>()
                .WithHandler <SetExceptionBreakpointsHandler>()
                .WithHandler <ConfigurationDoneHandler>()
                .WithHandler <ThreadsHandler>()
                .WithHandler <SetBreakpointsHandler>()
                .WithHandler <StackTraceHandler>()
                .WithHandler <ScopesHandler>()
                .WithHandler <VariablesHandler>()
                .WithHandler <ContinueHandler>()
                .WithHandler <NextHandler>()
                .WithHandler <PauseHandler>()
                .WithHandler <StepInHandler>()
                .WithHandler <StepOutHandler>()
                .WithHandler <SourceHandler>()
                .WithHandler <SetVariableHandler>()
                .WithHandler <DebugEvaluateHandler>();

                logger.LogInformation("Handlers added");
            }).ConfigureAwait(false);
        }
        public async Task <EvaluateResponseBody> Handle(EvaluateRequestArguments request, CancellationToken cancellationToken)
        {
            string valueString = "";
            int    variableId  = 0;

            bool isFromRepl =
                string.Equals(
                    request.Context,
                    "repl",
                    StringComparison.CurrentCultureIgnoreCase);

            if (isFromRepl)
            {
                var notAwaited =
                    _powerShellContextService
                    .ExecuteScriptStringAsync(request.Expression, false, true)
                    .ConfigureAwait(false);
            }
            else
            {
                VariableDetailsBase result = null;

                // VS Code might send this request after the debugger
                // has been resumed, return an empty result in this case.
                if (_powerShellContextService.IsDebuggerStopped)
                {
                    // First check to see if the watch expression refers to a naked variable reference.
                    result =
                        _debugService.GetVariableFromExpression(request.Expression, request.FrameId);

                    // If the expression is not a naked variable reference, then evaluate the expression.
                    if (result == null)
                    {
                        result =
                            await _debugService.EvaluateExpressionAsync(
                                request.Expression,
                                request.FrameId,
                                isFromRepl);
                    }
                }

                if (result != null)
                {
                    valueString = result.ValueString;
                    variableId  =
                        result.IsExpandable ?
                        result.Id : 0;
                }
            }

            return(new EvaluateResponseBody
            {
                Result = valueString,
                VariablesReference = variableId
            });
        }
        public async Task <DisconnectResponse> Handle(DisconnectArguments request, CancellationToken cancellationToken)
        {
            _debugEventHandlerService.UnregisterEventHandlers();
            if (_debugStateService.ExecutionCompleted == false)
            {
                _debugStateService.ExecutionCompleted = true;
                _powerShellContextService.AbortExecution(shouldAbortDebugSession: true);

                if (_debugStateService.IsInteractiveDebugSession && _debugStateService.IsAttachSession)
                {
                    // Pop the sessions
                    if (_powerShellContextService.CurrentRunspace.Context == RunspaceContext.EnteredProcess)
                    {
                        try
                        {
                            await _powerShellContextService.ExecuteScriptStringAsync("Exit-PSHostProcess").ConfigureAwait(false);

                            if (_debugStateService.IsRemoteAttach &&
                                _powerShellContextService.CurrentRunspace.Location == RunspaceLocation.Remote)
                            {
                                await _powerShellContextService.ExecuteScriptStringAsync("Exit-PSSession").ConfigureAwait(false);
                            }
                        }
                        catch (Exception e)
                        {
                            _logger.LogException("Caught exception while popping attached process after debugging", e);
                        }
                    }
                }

                _debugService.IsClientAttached = false;
            }

            _logger.LogInformation("Debug adapter is shutting down...");

#pragma warning disable CS4014
            // Trigger the clean up of the debugger. No need to wait for it.
            Task.Run(_psesDebugServer.OnSessionEnded);
#pragma warning restore CS4014

            return(new DisconnectResponse());
        }
        public async Task StartAsync(IServiceProvider languageServerServiceProvider, bool useTempSession)
        {
            _jsonRpcServer = await JsonRpcServer.From(options =>
            {
                options.Serializer    = new DapProtocolSerializer();
                options.Reciever      = new DapReciever();
                options.LoggerFactory = _loggerFactory;
                ILogger logger        = options.LoggerFactory.CreateLogger("DebugOptionsStartup");

                // We need to let the PowerShell Context Service know that we are in a debug session
                // so that it doesn't send the powerShell/startDebugger message.
                _powerShellContextService = languageServerServiceProvider.GetService <PowerShellContextService>();
                _powerShellContextService.IsDebugServerActive = true;

                // Needed to make sure PSReadLine's static properties are initialized in the pipeline thread.
                _powerShellContextService
                .ExecuteScriptStringAsync("[System.Runtime.CompilerServices.RuntimeHelpers]::RunClassConstructor([Microsoft.PowerShell.PSConsoleReadLine].TypeHandle)")
                .Wait();

                options.Services = new ServiceCollection()
                                   .AddPsesDebugServices(languageServerServiceProvider, this, useTempSession);

                options
                .WithInput(_inputStream)
                .WithOutput(_outputStream);

                logger.LogInformation("Adding handlers");

                options
                .WithHandler <InitializeHandler>()
                .WithHandler <LaunchHandler>()
                .WithHandler <AttachHandler>()
                .WithHandler <DisconnectHandler>()
                .WithHandler <SetFunctionBreakpointsHandler>()
                .WithHandler <SetExceptionBreakpointsHandler>()
                .WithHandler <ConfigurationDoneHandler>()
                .WithHandler <ThreadsHandler>()
                .WithHandler <SetBreakpointsHandler>()
                .WithHandler <StackTraceHandler>()
                .WithHandler <ScopesHandler>()
                .WithHandler <VariablesHandler>()
                .WithHandler <ContinueHandler>()
                .WithHandler <NextHandler>()
                .WithHandler <PauseHandler>()
                .WithHandler <StepInHandler>()
                .WithHandler <StepOutHandler>()
                .WithHandler <SourceHandler>()
                .WithHandler <SetVariableHandler>()
                .WithHandler <DebugEvaluateHandler>();

                logger.LogInformation("Handlers added");
            });
        }
Example #5
0
        public Task <EvaluateResponseBody> Handle(EvaluateRequestArguments request, CancellationToken cancellationToken)
        {
            _powerShellContextService.ExecuteScriptStringAsync(
                request.Expression,
                writeInputToHost: true,
                writeOutputToHost: true,
                addToHistory: true);

            return(Task.FromResult(new EvaluateResponseBody
            {
                Result = "",
                VariablesReference = 0
            }));
        }
Example #6
0
        public async Task <BreakpointDetails[]> SetLineBreakpointsAsync(
            PowerShellContextService powerShellContext,
            string scriptPath,
            BreakpointDetails[] breakpoints)
        {
            List <BreakpointDetails> resultBreakpointDetails =
                new List <BreakpointDetails>();

            // We always get the latest array of breakpoint line numbers
            // so store that for future use
            if (breakpoints.Length > 0)
            {
                // Set the breakpoints for this scriptPath
                this.breakpointsPerFile[scriptPath] =
                    breakpoints.Select(b => b.LineNumber).ToArray();
            }
            else
            {
                // No more breakpoints for this scriptPath, remove it
                this.breakpointsPerFile.Remove(scriptPath);
            }

            string hashtableString =
                string.Join(
                    ", ",
                    this.breakpointsPerFile
                    .Select(file => $"@{{Path=\"{file.Key}\";Line=@({string.Join(",", file.Value)})}}"));

            // Run Enable-DscDebug as a script because running it as a PSCommand
            // causes an error which states that the Breakpoint parameter has not
            // been passed.
            await powerShellContext.ExecuteScriptStringAsync(
                hashtableString.Length > 0
                ?$"Enable-DscDebug -Breakpoint {hashtableString}"
                : "Disable-DscDebug",
                false,
                false).ConfigureAwait(false);

            // Verify all the breakpoints and return them
            foreach (var breakpoint in breakpoints)
            {
                breakpoint.Verified = true;
            }

            return(breakpoints.ToArray());
        }
        private async Task LaunchScriptAsync(string scriptToLaunch)
        {
            // Is this an untitled script?
            if (ScriptFile.IsUntitledPath(scriptToLaunch))
            {
                ScriptFile untitledScript = _workspaceService.GetFile(scriptToLaunch);

                await _powerShellContextService
                .ExecuteScriptStringAsync(untitledScript.Contents, true, true);
            }
            else
            {
                await _powerShellContextService
                .ExecuteScriptWithArgsAsync(scriptToLaunch, _debugStateService.Arguments, writeInputToHost : true);
            }

            _jsonRpcServer.SendNotification(EventNames.Terminated);
        }
Example #8
0
        public async Task <Unit> Handle(PsesAttachRequestArguments request, CancellationToken cancellationToken)
        {
            _debugStateService.IsAttachSession = true;

            _debugEventHandlerService.RegisterEventHandlers();

            bool processIdIsSet      = !string.IsNullOrEmpty(request.ProcessId) && request.ProcessId != "undefined";
            bool customPipeNameIsSet = !string.IsNullOrEmpty(request.CustomPipeName) && request.CustomPipeName != "undefined";

            PowerShellVersionDetails runspaceVersion =
                _powerShellContextService.CurrentRunspace.PowerShellVersion;

            // If there are no host processes to attach to or the user cancels selection, we get a null for the process id.
            // This is not an error, just a request to stop the original "attach to" request.
            // Testing against "undefined" is a HACK because I don't know how to make "Cancel" on quick pick loading
            // to cancel on the VSCode side without sending an attachRequest with processId set to "undefined".
            if (!processIdIsSet && !customPipeNameIsSet)
            {
                _logger.LogInformation(
                    $"Attach request aborted, received {request.ProcessId} for processId.");

                throw new RpcErrorException(0, "User aborted attach to PowerShell host process.");
            }

            StringBuilder errorMessages = new StringBuilder();

            if (request.ComputerName != null)
            {
                if (runspaceVersion.Version.Major < 4)
                {
                    throw new RpcErrorException(0, $"Remote sessions are only available with PowerShell 4 and higher (current session is {runspaceVersion.Version}).");
                }
                else if (_powerShellContextService.CurrentRunspace.Location == RunspaceLocation.Remote)
                {
                    throw new RpcErrorException(0, $"Cannot attach to a process in a remote session when already in a remote session.");
                }

                await _powerShellContextService.ExecuteScriptStringAsync(
                    $"Enter-PSSession -ComputerName \"{request.ComputerName}\"",
                    errorMessages).ConfigureAwait(false);

                if (errorMessages.Length > 0)
                {
                    throw new RpcErrorException(0, $"Could not establish remote session to computer '{request.ComputerName}'");
                }

                _debugStateService.IsRemoteAttach = true;
            }

            if (processIdIsSet && int.TryParse(request.ProcessId, out int processId) && (processId > 0))
            {
                if (runspaceVersion.Version.Major < 5)
                {
                    throw new RpcErrorException(0, $"Attaching to a process is only available with PowerShell 5 and higher (current session is {runspaceVersion.Version}).");
                }

                await _powerShellContextService.ExecuteScriptStringAsync(
                    $"Enter-PSHostProcess -Id {processId}",
                    errorMessages).ConfigureAwait(false);

                if (errorMessages.Length > 0)
                {
                    throw new RpcErrorException(0, $"Could not attach to process '{processId}'");
                }
            }
            else if (customPipeNameIsSet)
            {
                if (runspaceVersion.Version < s_minVersionForCustomPipeName)
                {
                    throw new RpcErrorException(0, $"Attaching to a process with CustomPipeName is only available with PowerShell 6.2 and higher (current session is {runspaceVersion.Version}).");
                }

                await _powerShellContextService.ExecuteScriptStringAsync(
                    $"Enter-PSHostProcess -CustomPipeName {request.CustomPipeName}",
                    errorMessages).ConfigureAwait(false);

                if (errorMessages.Length > 0)
                {
                    throw new RpcErrorException(0, $"Could not attach to process with CustomPipeName: '{request.CustomPipeName}'");
                }
            }
            else if (request.ProcessId != "current")
            {
                _logger.LogError(
                    $"Attach request failed, '{request.ProcessId}' is an invalid value for the processId.");

                throw new RpcErrorException(0, "A positive integer must be specified for the processId field.");
            }

            // Execute the Debug-Runspace command but don't await it because it
            // will block the debug adapter initialization process.  The
            // InitializedEvent will be sent as soon as the RunspaceChanged
            // event gets fired with the attached runspace.

            string debugRunspaceCmd;

            if (request.RunspaceName != null)
            {
                IEnumerable <int?> ids = await _powerShellContextService.ExecuteCommandAsync <int?>(new PSCommand()
                                                                                                    .AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace")
                                                                                                    .AddParameter("Name", request.RunspaceName)
                                                                                                    .AddCommand("Microsoft.PowerShell.Utility\\Select-Object")
                                                                                                    .AddParameter("ExpandProperty", "Id"));

                foreach (var id in ids)
                {
                    _debugStateService.RunspaceId = id;
                    break;
                }
                debugRunspaceCmd = $"\nDebug-Runspace -Name '{request.RunspaceName}'";
            }
            else if (request.RunspaceId != null)
            {
                if (!int.TryParse(request.RunspaceId, out int runspaceId) || runspaceId <= 0)
                {
                    _logger.LogError(
                        $"Attach request failed, '{request.RunspaceId}' is an invalid value for the processId.");

                    throw new RpcErrorException(0, "A positive integer must be specified for the RunspaceId field.");
                }

                _debugStateService.RunspaceId = runspaceId;

                debugRunspaceCmd = $"\nDebug-Runspace -Id {runspaceId}";
            }
            else
            {
                _debugStateService.RunspaceId = 1;

                debugRunspaceCmd = "\nDebug-Runspace -Id 1";
            }

            // Clear any existing breakpoints before proceeding
            await _breakpointService.RemoveAllBreakpointsAsync().ConfigureAwait(continueOnCapturedContext: false);

            _debugStateService.WaitingForAttach = true;
            Task nonAwaitedTask = _powerShellContextService
                                  .ExecuteScriptStringAsync(debugRunspaceCmd)
                                  .ContinueWith(OnExecutionCompletedAsync);

            if (runspaceVersion.Version.Major >= 7)
            {
                _jsonRpcServer.SendNotification(EventNames.Initialized);
            }
            return(Unit.Value);
        }
        private async Task CheckPackageManagement()
        {
            PSCommand getModule = new PSCommand().AddCommand("Get-Module").AddParameter("ListAvailable").AddParameter("Name", "PackageManagement");

            foreach (PSModuleInfo module in await _powerShellContextService.ExecuteCommandAsync <PSModuleInfo>(getModule).ConfigureAwait(false))
            {
                // The user has a good enough version of PackageManagement
                if (module.Version >= s_desiredPackageManagementVersion)
                {
                    break;
                }

                _logger.LogDebug("Old version of PackageManagement detected.");

                if (_powerShellContextService.CurrentRunspace.Runspace.SessionStateProxy.LanguageMode != PSLanguageMode.FullLanguage)
                {
                    _languageServer.Window.ShowWarning("You have an older version of PackageManagement known to cause issues with the PowerShell extension. Please run the following command in a new Windows PowerShell session and then restart the PowerShell extension: `Install-Module PackageManagement -Force -AllowClobber -MinimumVersion 1.4.6`");
                    return;
                }

                var takeActionText = "Yes";
                MessageActionItem messageAction = await _languageServer.Window.ShowMessageRequest(new ShowMessageRequestParams
                {
                    Message = "You have an older version of PackageManagement known to cause issues with the PowerShell extension. Would you like to update PackageManagement (You will need to restart the PowerShell extension after)?",
                    Type    = MessageType.Warning,
                    Actions = new[]
                    {
                        new MessageActionItem
                        {
                            Title = takeActionText
                        },
                        new MessageActionItem
                        {
                            Title = "Not now"
                        }
                    }
                }).ConfigureAwait(false);

                // If the user chose "Not now" ignore it for the rest of the session.
                if (messageAction?.Title == takeActionText)
                {
                    StringBuilder errors = new StringBuilder();
                    await _powerShellContextService.ExecuteScriptStringAsync(
                        "powershell.exe -NoLogo -NoProfile -Command '[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Install-Module -Name PackageManagement -Force -MinimumVersion 1.4.6 -Scope CurrentUser -AllowClobber -Repository PSGallery'",
                        errors,
                        writeInputToHost : true,
                        writeOutputToHost : true,
                        addToHistory : true).ConfigureAwait(false);

                    if (errors.Length == 0)
                    {
                        _logger.LogDebug("PackageManagement is updated.");
                        _languageServer.Window.ShowMessage(new ShowMessageParams
                        {
                            Type    = MessageType.Info,
                            Message = "PackageManagement updated, If you already had PackageManagement loaded in your session, please restart the PowerShell extension."
                        });
                    }
                    else
                    {
                        // There were errors installing PackageManagement.
                        _logger.LogError($"PackageManagement installation had errors: {errors.ToString()}");
                        _languageServer.Window.ShowMessage(new ShowMessageParams
                        {
                            Type    = MessageType.Error,
                            Message = "PackageManagement update failed. This might be due to PowerShell Gallery using TLS 1.2. More info can be found at https://aka.ms/psgallerytls"
                        });
                    }
                }
            }
        }
        private async Task CheckPackageManagement()
        {
            PSCommand getModule = new PSCommand().AddCommand("Get-Module").AddParameter("ListAvailable").AddParameter("Name", "PackageManagement");

            foreach (PSModuleInfo module in await _powerShellContextService.ExecuteCommandAsync <PSModuleInfo>(getModule))
            {
                // The user has a good enough version of PackageManagement
                if (module.Version >= s_desiredPackageManagementVersion)
                {
                    break;
                }

                _logger.LogDebug("Old version of PackageManagement detected. Attempting to update.");

                var takeActionText = "Yes";
                MessageActionItem messageAction = await _languageServer.Window.ShowMessage(new ShowMessageRequestParams
                {
                    Message = "You have an older version of PackageManagement known to cause issues with the PowerShell extension. Would you like to update PackageManagement (You will need to restart the PowerShell extension after)?",
                    Type    = MessageType.Warning,
                    Actions = new []
                    {
                        new MessageActionItem
                        {
                            Title = takeActionText
                        },
                        new MessageActionItem
                        {
                            Title = "Not now"
                        }
                    }
                });

                // If the user chose "Not now" ignore it for the rest of the session.
                if (messageAction?.Title == takeActionText)
                {
                    StringBuilder errors = new StringBuilder();
                    await _powerShellContextService.ExecuteScriptStringAsync(
                        "powershell.exe -NoLogo -NoProfile -Command 'Install-Module -Name PackageManagement -Force -MinimumVersion 1.4.6 -Scope CurrentUser -AllowClobber'",
                        errors,
                        writeInputToHost : true,
                        writeOutputToHost : true,
                        addToHistory : true).ConfigureAwait(false);

                    if (errors.Length == 0)
                    {
                        _languageServer.Window.ShowMessage(new ShowMessageParams
                        {
                            Type    = MessageType.Info,
                            Message = "PackageManagement updated, If you already had PackageManagement loaded in your session, please restart the PowerShell extension."
                        });
                    }
                    else
                    {
                        // There were errors installing PackageManagement.
                        _languageServer.Window.ShowMessage(new ShowMessageParams
                        {
                            Type    = MessageType.Error,
                            Message = "PackageManagement update failed. Please run the following command in a new Windows PowerShell session and then restart the PowerShell extension: `Install-Module PackageManagement -Force -AllowClobber -MinimumVersion 1.4.6`"
                        });
                    }
                }
            }
        }