From cc0fed479a3e455b746a3d12597f078462f2d644 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 5 Sep 2019 15:30:25 -0300 Subject: [PATCH] Add APIs for breakpoint management in runspaces and enable attach to process without BreakAll for PSES (#10338) --- .../commands/utility/DebugRunspaceCommand.cs | 22 +- .../EnableDisableRunspaceDebugCommand.cs | 22 - .../commands/utility/New-PSBreakpoint.cs | 101 ---- .../commands/utility/Set-PSBreakpoint.cs | 20 +- .../Microsoft.PowerShell.Utility.psd1 | 6 +- .../Microsoft.PowerShell.Utility.psd1 | 12 +- .../engine/InitialSessionState.cs | 1 - .../engine/PSVersionInfo.cs | 6 + .../engine/debugger/Breakpoint.cs | 16 +- .../engine/debugger/debugger.cs | 517 +++++++++--------- .../engine/hostifaces/PSTask.cs | 30 +- .../engine/remoting/client/Job.cs | 34 +- .../engine/remoting/client/remoterunspace.cs | 238 +++++++- .../engine/remoting/commands/DebugJob.cs | 9 +- .../RemoteDebuggingCapability.cs | 118 ++++ .../server/ServerRunspacePoolDriver.cs | 297 +++++++--- .../resources/DebuggerStrings.resx | 3 + .../EnableDisablePSBreakpoint.Tests.ps1 | 23 - .../New-PSBreakpoint.Tests.ps1 | 195 ------- test/powershell/SDK/Breakpoint.Tests.ps1 | 168 ++++++ .../engine/Basic/DefaultCommands.Tests.ps1 | 1 - test/tools/TestMetadata.json | 1 - test/xUnit/csharp/test_Runspace.cs | 26 - 23 files changed, 1069 insertions(+), 797 deletions(-) delete mode 100644 src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-PSBreakpoint.cs create mode 100644 src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteDebuggingCapability.cs delete mode 100644 test/powershell/Modules/Microsoft.PowerShell.Utility/EnableDisablePSBreakpoint.Tests.ps1 delete mode 100644 test/powershell/Modules/Microsoft.PowerShell.Utility/New-PSBreakpoint.Tests.ps1 create mode 100644 test/powershell/SDK/Breakpoint.Tests.ps1 diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs index 02e6fcad67..7b22cbcddd 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs @@ -101,19 +101,11 @@ namespace Microsoft.PowerShell.Commands } /// - /// The optional breakpoint objects to use for debugging. + /// Gets or sets a flag that tells PowerShell to automatically perform a BreakAll when the debugger is attached to the remote target. /// - [Experimental("Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints", ExperimentAction.Show)] - [Parameter(Position = 1, - ParameterSetName = DebugRunspaceCommand.InstanceIdParameterSet)] - [Parameter(ParameterSetName = DebugRunspaceCommand.RunspaceParameterSet)] - [Parameter(ParameterSetName = DebugRunspaceCommand.IdParameterSet)] - [Parameter(ParameterSetName = DebugRunspaceCommand.NameParameterSet)] - public Breakpoint[] Breakpoint - { - get; - set; - } + [Experimental("Microsoft.PowerShell.Utility.PSManageBreakpointsInRunspace", ExperimentAction.Show)] + [Parameter] + public SwitchParameter BreakAll { get; set; } #endregion @@ -275,7 +267,7 @@ namespace Microsoft.PowerShell.Commands _debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript); // Set up host script debugger to debug the runspace. - _debugger.DebugRunspace(_runspace, disableBreakAll: Breakpoint?.Length > 0); + _debugger.DebugRunspace(_runspace, breakAll: BreakAll); while (_debugging) { @@ -532,10 +524,6 @@ namespace Microsoft.PowerShell.Commands { SetLocalMode(runspace.Debugger, true); EnableHostDebugger(runspace, false); - if (Breakpoint?.Length > 0) - { - runspace.Debugger?.SetBreakpoints(Breakpoint); - } } private void RestoreRunspace(Runspace runspace) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs index a9192ae2da..02869619e3 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs @@ -350,22 +350,6 @@ namespace Microsoft.PowerShell.Commands set; } - /// - /// Gets or sets the optional breakpoint objects to use for debugging. - /// - [Experimental("Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints", ExperimentAction.Show)] - [Parameter(Position = 1, - ParameterSetName = CommonRunspaceCommandBase.RunspaceParameterSet)] - [Parameter(Position = 1, - ParameterSetName = CommonRunspaceCommandBase.RunspaceNameParameterSet)] - [Parameter(Position = 1, - ParameterSetName = CommonRunspaceCommandBase.RunspaceIdParameterSet)] - public Breakpoint[] Breakpoint - { - get; - set; - } - #endregion #region Overrides @@ -428,12 +412,6 @@ namespace Microsoft.PowerShell.Commands debugger.SetDebuggerStepMode(false); } } - - // If any breakpoints were provided, set those in the debugger. - if (Breakpoint?.Length > 0) - { - debugger.SetBreakpoints(Breakpoint); - } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-PSBreakpoint.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-PSBreakpoint.cs deleted file mode 100644 index b5837cc0da..0000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-PSBreakpoint.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; -using System.Management.Automation; -using System.Management.Automation.Internal; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// This class implements New-PSBreakpoint command. - /// - [Experimental("Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints", ExperimentAction.Show)] - [Cmdlet(VerbsCommon.New, "PSBreakpoint", DefaultParameterSetName = LineParameterSetName, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113449")] - [OutputType(typeof(VariableBreakpoint), typeof(CommandBreakpoint), typeof(LineBreakpoint))] - public class NewPSBreakpointCommand : PSBreakpointCreationBase - { - /// - /// Create a new breakpoint. - /// - protected override void ProcessRecord() - { - // If there is a script, resolve its path - Collection scripts = ResolveScriptPaths(); - - // If it is a command breakpoint... - if (ParameterSetName.Equals(CommandParameterSetName, StringComparison.OrdinalIgnoreCase)) - { - for (int i = 0; i < Command.Length; i++) - { - if (scripts.Count > 0) - { - foreach (string path in scripts) - { - WildcardPattern pattern = WildcardPattern.Get(Command[i], WildcardOptions.Compiled | WildcardOptions.IgnoreCase); - WriteObject(new CommandBreakpoint(path, pattern, Command[i], Action)); - } - } - else - { - WildcardPattern pattern = WildcardPattern.Get(Command[i], WildcardOptions.Compiled | WildcardOptions.IgnoreCase); - WriteObject(new CommandBreakpoint(null, pattern, Command[i], Action)); - } - } - } - else if (ParameterSetName.Equals(VariableParameterSetName, StringComparison.OrdinalIgnoreCase)) - { - // If it is a variable breakpoint... - for (int i = 0; i < Variable.Length; i++) - { - if (scripts.Count > 0) - { - foreach (string path in scripts) - { - WriteObject(new VariableBreakpoint(path, Variable[i], Mode, Action)); - } - } - else - { - WriteObject(new VariableBreakpoint(null, Variable[i], Mode, Action)); - } - } - } - else - { - // Else it is the default parameter set (Line breakpoint)... - Debug.Assert(ParameterSetName.Equals(LineParameterSetName, StringComparison.OrdinalIgnoreCase)); - - for (int i = 0; i < Line.Length; i++) - { - if (Line[i] < 1) - { - WriteError( - new ErrorRecord( - new ArgumentException(Debugger.LineLessThanOne), - "NewPSBreakpoint:LineLessThanOne", - ErrorCategory.InvalidArgument, - null)); - - continue; - } - - foreach (string path in scripts) - { - if (Column != 0) - { - WriteObject(new LineBreakpoint(path, Line[i], Column, Action)); - } - else - { - WriteObject(new LineBreakpoint(path, Line[i], Action)); - } - } - } - } - } - } -} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Set-PSBreakpoint.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Set-PSBreakpoint.cs index 23773fd447..30e9ae7142 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Set-PSBreakpoint.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Set-PSBreakpoint.cs @@ -75,13 +75,13 @@ namespace Microsoft.PowerShell.Commands foreach (string path in scripts) { WriteObject( - Context.Debugger.NewCommandBreakpoint(path.ToString(), Command[i], Action)); + Context.Debugger.SetCommandBreakpoint(Command[i], Action, path)); } } else { WriteObject( - Context.Debugger.NewCommandBreakpoint(Command[i], Action)); + Context.Debugger.SetCommandBreakpoint(Command[i], Action)); } } } @@ -97,13 +97,13 @@ namespace Microsoft.PowerShell.Commands foreach (string path in scripts) { WriteObject( - Context.Debugger.NewVariableBreakpoint(path.ToString(), Variable[i], Mode, Action)); + Context.Debugger.SetVariableBreakpoint(Variable[i], Mode, Action, path)); } } else { WriteObject( - Context.Debugger.NewVariableBreakpoint(Variable[i], Mode, Action)); + Context.Debugger.SetVariableBreakpoint(Variable[i], Mode, Action)); } } } @@ -130,16 +130,8 @@ namespace Microsoft.PowerShell.Commands foreach (string path in scripts) { - if (Column != 0) - { - WriteObject( - Context.Debugger.NewStatementBreakpoint(path, Line[i], Column, Action)); - } - else - { - WriteObject( - Context.Debugger.NewLineBreakpoint(path, Line[i], Action)); - } + WriteObject( + Context.Debugger.SetLineBreakpoint(path, Line[i], Column, Action)); } } } diff --git a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 267ddc928d..34e7cc94ca 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -18,7 +18,7 @@ CmdletsToExport = @( 'Set-MarkdownOption', 'Add-Member', 'Get-Member', 'Compare-Object', 'Group-Object', 'Measure-Object', 'New-Object', 'Select-Object', 'Sort-Object', 'Tee-Object', 'Register-ObjectEvent', 'Write-Output', 'Import-PowerShellDataFile', 'Write-Progress', 'Disable-PSBreakpoint', 'Enable-PSBreakpoint', - 'Get-PSBreakpoint', 'Remove-PSBreakpoint', 'Set-PSBreakpoint', 'New-PSBreakpoint', 'Get-PSCallStack', 'Export-PSSession', + 'Get-PSBreakpoint', 'Remove-PSBreakpoint', 'Set-PSBreakpoint', 'Get-PSCallStack', 'Export-PSSession', 'Import-PSSession', 'Get-Random', 'Invoke-RestMethod', 'Debug-Runspace', 'Get-Runspace', 'Disable-RunspaceDebug', 'Enable-RunspaceDebug', 'Get-RunspaceDebug', 'Start-Sleep', 'Join-String', 'Out-String', 'Select-String', 'ConvertFrom-StringData', 'Format-Table', 'New-TemporaryFile', 'New-TimeSpan', @@ -35,8 +35,8 @@ PrivateData = @{ PSData = @{ ExperimentalFeatures = @( @{ - Name = 'Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints' - Description = "Enables the New-PSBreakpoint cmdlet and the -Breakpoint parameter on Debug-Runspace to set breakpoints in another Runspace upfront." + Name = 'Microsoft.PowerShell.Utility.PSManageBreakpointsInRunspace' + Description = 'Enables -BreakAll parameter on Debug-Runspace and Debug-Job cmdlets to allow users to decide if they want PowerShell to break immediately in the current location when they attach a debugger.' } ) } diff --git a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 6ef4095acf..a020aa17ea 100644 --- a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -17,7 +17,7 @@ CmdletsToExport = @( 'Show-Markdown', 'Get-MarkdownOption', 'Set-MarkdownOption', 'Add-Member', 'Get-Member', 'Compare-Object', 'Group-Object', 'Measure-Object', 'New-Object', 'Select-Object', 'Sort-Object', 'Tee-Object', 'Register-ObjectEvent', 'Write-Output', 'Import-PowerShellDataFile', 'Write-Progress', 'Disable-PSBreakpoint', 'Enable-PSBreakpoint', 'Get-PSBreakpoint', - 'Remove-PSBreakpoint', 'Set-PSBreakpoint', 'New-PSBreakpoint', 'Get-PSCallStack', 'Export-PSSession', 'Import-PSSession', 'Get-Random', + 'Remove-PSBreakpoint', 'Set-PSBreakpoint', 'Get-PSCallStack', 'Export-PSSession', 'Import-PSSession', 'Get-Random', 'Invoke-RestMethod', 'Debug-Runspace', 'Get-Runspace', 'Disable-RunspaceDebug', 'Enable-RunspaceDebug', 'Get-RunspaceDebug', 'ConvertFrom-SddlString', 'Start-Sleep', 'Join-String', 'Out-String', 'Select-String', 'ConvertFrom-StringData', 'Format-Table', 'New-TemporaryFile', 'New-TimeSpan', 'Get-TraceSource', 'Set-TraceSource', @@ -29,4 +29,14 @@ FunctionsToExport = @() AliasesToExport = @('fhx') NestedModules = @("Microsoft.PowerShell.Commands.Utility.dll") HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=855960' +PrivateData = @{ + PSData = @{ + ExperimentalFeatures = @( + @{ + Name = 'Microsoft.PowerShell.Utility.PSManageBreakpointsInRunspace' + Description = 'Enables -BreakAll parameter on Debug-Runspace and Debug-Job cmdlets to allow users to decide if they want PowerShell to break immediately in the current location when they attach a debugger.' + } + ) + } +} } diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 0f61eb0b16..ebf1f76818 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4545,7 +4545,6 @@ end { new SessionStateAliasEntry("mi", "Move-Item", string.Empty, ReadOnly), new SessionStateAliasEntry("mp", "Move-ItemProperty", string.Empty, ReadOnly), new SessionStateAliasEntry("nal", "New-Alias", string.Empty, ReadOnly), - new SessionStateAliasEntry("nbp", "New-PSBreakpoint", string.Empty, ReadOnly), new SessionStateAliasEntry("ndr", "New-PSDrive", string.Empty, ReadOnly), new SessionStateAliasEntry("ni", "New-Item", string.Empty, ReadOnly), new SessionStateAliasEntry("nv", "New-Variable", string.Empty, ReadOnly), diff --git a/src/System.Management.Automation/engine/PSVersionInfo.cs b/src/System.Management.Automation/engine/PSVersionInfo.cs index ef58dafb56..7ac1c4b94b 100644 --- a/src/System.Management.Automation/engine/PSVersionInfo.cs +++ b/src/System.Management.Automation/engine/PSVersionInfo.cs @@ -61,6 +61,7 @@ namespace System.Management.Automation private static readonly SemanticVersion s_psV6Version = new SemanticVersion(6, 0, 0, preReleaseLabel: null, buildLabel: null); private static readonly SemanticVersion s_psV61Version = new SemanticVersion(6, 1, 0, preReleaseLabel: null, buildLabel: null); private static readonly SemanticVersion s_psV62Version = new SemanticVersion(6, 2, 0, preReleaseLabel: null, buildLabel: null); + private static readonly SemanticVersion s_psV7Version = new SemanticVersion(7, 0, 0, preReleaseLabel: null, buildLabel: null); private static readonly SemanticVersion s_psSemVersion; private static readonly Version s_psVersion; @@ -329,6 +330,11 @@ namespace System.Management.Automation get { return s_psV6Version; } } + internal static SemanticVersion PSV7Version + { + get { return s_psV7Version; } + } + internal static SemanticVersion PSCurrentVersion { get { return s_psSemVersion; } diff --git a/src/System.Management.Automation/engine/debugger/Breakpoint.cs b/src/System.Management.Automation/engine/debugger/Breakpoint.cs index 80fc66430a..30d83e5769 100644 --- a/src/System.Management.Automation/engine/debugger/Breakpoint.cs +++ b/src/System.Management.Automation/engine/debugger/Breakpoint.cs @@ -128,9 +128,7 @@ namespace System.Management.Automation return BreakpointAction.Continue; } - internal virtual void RemoveSelf(ScriptDebugger debugger) - { - } + internal virtual bool RemoveSelf(ScriptDebugger debugger) => false; #endregion methods @@ -208,10 +206,8 @@ namespace System.Management.Automation : StringUtil.Format(DebuggerStrings.CommandBreakpointString, Command); } - internal override void RemoveSelf(ScriptDebugger debugger) - { + internal override bool RemoveSelf(ScriptDebugger debugger) => debugger.RemoveCommandBreakpoint(this); - } private bool CommandInfoMatches(CommandInfo commandInfo) { @@ -350,10 +346,8 @@ namespace System.Management.Automation return false; } - internal override void RemoveSelf(ScriptDebugger debugger) - { + internal override bool RemoveSelf(ScriptDebugger debugger) => debugger.RemoveVariableBreakpoint(this); - } } /// @@ -589,7 +583,7 @@ namespace System.Management.Automation this.BreakpointBitArray.Set(SequencePointIndex, true); } - internal override void RemoveSelf(ScriptDebugger debugger) + internal override bool RemoveSelf(ScriptDebugger debugger) { if (this.SequencePoints != null) { @@ -612,7 +606,7 @@ namespace System.Management.Automation } } - debugger.RemoveLineBreakpoint(this); + return debugger.RemoveLineBreakpoint(this); } } } diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index 60be22a00d..a14b2b59f4 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -618,31 +618,74 @@ namespace System.Management.Automation return new Collection(); } - /// - /// Adds the provided set of breakpoints to the debugger. - /// - /// Breakpoints. - public virtual void SetBreakpoints(IEnumerable breakpoints) - { - throw new PSNotImplementedException(); - } - /// /// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets. /// /// Id of the breakpoint you want. - public virtual Breakpoint GetBreakpoint(int id) - { + public virtual Breakpoint GetBreakpoint(int id) => throw new PSNotImplementedException(); - } /// /// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet. /// - public virtual List GetBreakpoints() - { + public virtual List GetBreakpoints() => + throw new PSNotImplementedException(); + + /// + /// Sets a command breakpoint in the debugger. + /// + /// The name of the command that will trigger the breakpoint. This value is required and may not be null. + /// The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit. + /// The path to the script file where the breakpoint may be hit. If null, the breakpoint may be hit anywhere the command is invoked. + /// The command breakpoint that was set. + public virtual CommandBreakpoint SetCommandBreakpoint(string command, ScriptBlock action = null, string path = null) => + throw new PSNotImplementedException(); + + /// + /// Sets a line breakpoint in the debugger. + /// + /// The path to the script file where the breakpoint may be hit. This value is required and may not be null. + /// The line in the script file where the breakpoint may be hit. This value is required and must be greater than or equal to 1. + /// The column in the script file where the breakpoint may be hit. If 0, the breakpoint will trigger on any statement on the line. + /// The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit. + /// The line breakpoint that was set. + public virtual LineBreakpoint SetLineBreakpoint(string path, int line, int column = 0, ScriptBlock action = null) => + throw new PSNotImplementedException(); + + /// + /// Sets a variable breakpoint in the debugger. + /// + /// The name of the variable that will trigger the breakpoint. This value is required and may not be null. + /// The variable access mode that will trigger the breakpoint. By default variable breakpoints will trigger only when the variable is updated. + /// The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit. + /// The path to the script file where the breakpoint may be hit. If null, the breakpoint may be hit anywhere the variable is accessed using the specified access mode. + /// The variable breakpoint that was set. + public virtual VariableBreakpoint SetVariableBreakpoint(string variableName, VariableAccessMode accessMode = VariableAccessMode.Write, ScriptBlock action = null, string path = null) => + throw new PSNotImplementedException(); + + /// + /// Removes a breakpoint from the debugger. + /// + /// The breakpoint to remove from the debugger. This value is required and may not be null. + /// True if the breakpoint was removed from the debugger; false otherwise. + public virtual bool RemoveBreakpoint(Breakpoint breakpoint) => + throw new PSNotImplementedException(); + + /// + /// Enables a breakpoint in the debugger. + /// + /// The breakpoint to enable in the debugger. This value is required and may not be null. + /// The updated breakpoint if it was found; null if the breakpoint was not found in the debugger. + public virtual Breakpoint EnableBreakpoint(Breakpoint breakpoint) => + throw new PSNotImplementedException(); + + /// + /// Disables a breakpoint in the debugger. + /// + /// The breakpoint to enable in the debugger. This value is required and may not be null. + /// The updated breakpoint if it was found; null if the breakpoint was not found in the debugger. + public virtual Breakpoint DisableBreakpoint(Breakpoint breakpoint) => throw new PSNotImplementedException(); - } /// /// Resets the command processor source information so that it is @@ -704,13 +747,15 @@ namespace System.Management.Automation /// Sets up debugger to debug provided job or its child jobs. /// /// - /// Job object that is either a debuggable job or a container - /// of debuggable child jobs. + /// Job object that is either a debuggable job or a container of + /// debuggable child jobs. /// - internal virtual void DebugJob(Job job) - { + /// + /// If true, the debugger automatically invokes a break all when it + /// attaches to the job. + /// + internal virtual void DebugJob(Job job, bool breakAll) => throw new PSNotImplementedException(); - } /// /// Removes job from debugger job list and pops the its @@ -763,21 +808,15 @@ namespace System.Management.Automation /// /// Sets up debugger to debug provided Runspace in a nested debug session. /// - /// Runspace to debug. - internal virtual void DebugRunspace(Runspace runspace) - { + /// + /// The runspace to debug. + /// + /// + /// If true, the debugger automatically invokes a break all when it + /// attaches to the runspace. + /// + internal virtual void DebugRunspace(Runspace runspace, bool breakAll) => throw new PSNotImplementedException(); - } - - /// - /// Sets up debugger to debug provided Runspace in a nested debug session. - /// - /// Runspace to debug. - /// - internal virtual void DebugRunspace(Runspace runspace, bool disableBreakAll) - { - throw new PSNotImplementedException(); - } /// /// Removes the provided Runspace from the nested "active" debugger state. @@ -1122,7 +1161,7 @@ namespace System.Management.Automation #endregion Call stack management - #region adding breakpoints + #region setting breakpoints internal void AddBreakpointCommon(Breakpoint breakpoint) { @@ -1135,30 +1174,14 @@ namespace System.Management.Automation OnBreakpointUpdated(new BreakpointUpdatedEventArgs(breakpoint, BreakpointUpdateType.Set, _idToBreakpoint.Count)); } - private Breakpoint AddCommandBreakpoint(CommandBreakpoint breakpoint) + private CommandBreakpoint AddCommandBreakpoint(CommandBreakpoint breakpoint) { AddBreakpointCommon(breakpoint); _commandBreakpoints[breakpoint.Id] = breakpoint; return breakpoint; } - internal Breakpoint NewCommandBreakpoint(string path, string command, ScriptBlock action) - { - WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase); - - CheckForBreakpointSupport(); - return AddCommandBreakpoint(new CommandBreakpoint(path, pattern, command, action)); - } - - internal Breakpoint NewCommandBreakpoint(string command, ScriptBlock action) - { - WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase); - - CheckForBreakpointSupport(); - return AddCommandBreakpoint(new CommandBreakpoint(null, pattern, command, action)); - } - - private Breakpoint AddLineBreakpoint(LineBreakpoint breakpoint) + private LineBreakpoint AddLineBreakpoint(LineBreakpoint breakpoint) { AddBreakpointCommon(breakpoint); _pendingBreakpoints[breakpoint.Id] = breakpoint; @@ -1188,22 +1211,6 @@ namespace System.Management.Automation } } - internal Breakpoint NewLineBreakpoint(string path, int line, ScriptBlock action) - { - Diagnostics.Assert(path != null, "caller to verify path is not null"); - - CheckForBreakpointSupport(); - return AddLineBreakpoint(new LineBreakpoint(path, line, action)); - } - - internal Breakpoint NewStatementBreakpoint(string path, int line, int column, ScriptBlock action) - { - Diagnostics.Assert(path != null, "caller to verify path is not null"); - - CheckForBreakpointSupport(); - return AddLineBreakpoint(new LineBreakpoint(path, line, column, action)); - } - internal VariableBreakpoint AddVariableBreakpoint(VariableBreakpoint breakpoint) { AddBreakpointCommon(breakpoint); @@ -1218,16 +1225,30 @@ namespace System.Management.Automation return breakpoint; } - internal Breakpoint NewVariableBreakpoint(string path, string variableName, VariableAccessMode accessMode, ScriptBlock action) + private void UpdateBreakpoints(FunctionContext functionContext) { - CheckForBreakpointSupport(); - return AddVariableBreakpoint(new VariableBreakpoint(path, variableName, accessMode, action)); - } + if (functionContext._breakPoints == null) + { + // This should be rare - setting a breakpoint inside a script, but debugger hadn't started. + SetupBreakpoints(functionContext); + } + else + { + // Check pending breakpoints to see if any apply to this script. + if (string.IsNullOrEmpty(functionContext._file)) + { + return; + } - internal Breakpoint NewVariableBreakpoint(string variableName, VariableAccessMode accessMode, ScriptBlock action) - { - CheckForBreakpointSupport(); - return AddVariableBreakpoint(new VariableBreakpoint(null, variableName, accessMode, action)); + foreach ((int breakpointId, LineBreakpoint item) in _pendingBreakpoints) + { + if (item.IsScriptBreakpoint && item.Script.Equals(functionContext._file, StringComparison.OrdinalIgnoreCase)) + { + SetPendingBreakpoints(functionContext); + break; + } + } + } } /// @@ -1239,44 +1260,27 @@ namespace System.Management.Automation RaiseBreakpointUpdatedEvent(e); } - #endregion adding breakpoints + #endregion setting breakpoints #region removing breakpoints - // This is the implementation of the Remove-PSBreakpoint cmdlet. - internal void RemoveBreakpoint(Breakpoint breakpoint) - { - _idToBreakpoint.Remove(breakpoint.Id, out _); - - breakpoint.RemoveSelf(this); - - if (CanDisableDebugger) - { - SetInternalDebugMode(InternalDebugMode.Disabled); - } - - OnBreakpointUpdated(new BreakpointUpdatedEventArgs(breakpoint, BreakpointUpdateType.Removed, _idToBreakpoint.Count)); - } - - internal void RemoveVariableBreakpoint(VariableBreakpoint breakpoint) - { + internal bool RemoveVariableBreakpoint(VariableBreakpoint breakpoint) => _variableBreakpoints[breakpoint.Variable].Remove(breakpoint.Id, out _); - } - internal void RemoveCommandBreakpoint(CommandBreakpoint breakpoint) - { + internal bool RemoveCommandBreakpoint(CommandBreakpoint breakpoint) => _commandBreakpoints.Remove(breakpoint.Id, out _); - } - internal void RemoveLineBreakpoint(LineBreakpoint breakpoint) + internal bool RemoveLineBreakpoint(LineBreakpoint breakpoint) { - _pendingBreakpoints.Remove(breakpoint.Id, out _); + bool removed = _pendingBreakpoints.Remove(breakpoint.Id, out _); Tuple> value; if (_boundBreakpoints.TryGetValue(breakpoint.Script, out value)) { - value.Item2.Remove(breakpoint.Id, out _); + removed = value.Item2.Remove(breakpoint.Id, out _); } + + return removed; } #endregion removing breakpoints @@ -1386,24 +1390,6 @@ namespace System.Management.Automation OnDebuggerStop(invocationInfo, breakpoints.ToList()); } - /// - /// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets. - /// - /// Id of the breakpoint you want. - public override Breakpoint GetBreakpoint(int id) - { - _idToBreakpoint.TryGetValue(id, out Breakpoint breakpoint); - return breakpoint; - } - - /// - /// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet. - /// - public override List GetBreakpoints() - { - return (from bp in _idToBreakpoint.Values orderby bp.Id select bp).ToList(); - } - // Return the line breakpoints bound in a specific script block (used when a sequence point // is hit, to find which breakpoints are set on that sequence point.) internal List GetBoundBreakpoints(IScriptExtent[] sequencePoints) @@ -1458,30 +1444,6 @@ namespace System.Management.Automation return breaks; } - #endregion triggering breakpoints - - #region enabling/disabling breakpoints - - /// - /// Implementation of Enable-PSBreakpoint cmdlet. - /// - internal void EnableBreakpoint(Breakpoint bp) - { - bp.SetEnabled(true); - OnBreakpointUpdated(new BreakpointUpdatedEventArgs(bp, BreakpointUpdateType.Enabled, _idToBreakpoint.Count)); - } - - /// - /// Implementation of Disable-PSBreakpoint cmdlet. - /// - internal void DisableBreakpoint(Breakpoint bp) - { - bp.SetEnabled(false); - OnBreakpointUpdated(new BreakpointUpdatedEventArgs(bp, BreakpointUpdateType.Disabled, _idToBreakpoint.Count)); - } - - #endregion enabling/disabling breakpoints - internal void OnSequencePointHit(FunctionContext functionContext) { if (_context.ShouldTraceStatement && !_callStack.Last().IsFrameHidden && !functionContext._debuggerStepThrough) @@ -1533,34 +1495,7 @@ namespace System.Management.Automation } } - private void UpdateBreakpoints(FunctionContext functionContext) - { - if (functionContext._breakPoints == null) - { - // This should be rare - setting a breakpoint inside a script, but debugger hadn't started. - SetupBreakpoints(functionContext); - } - else - { - // Check pending breakpoints to see if any apply to this script. - if (string.IsNullOrEmpty(functionContext._file)) { return; } - - bool havePendingBreakpoint = false; - foreach ((int breakpointId, LineBreakpoint item) in _pendingBreakpoints) - { - if (item.IsScriptBreakpoint && item.Script.Equals(functionContext._file, StringComparison.OrdinalIgnoreCase)) - { - havePendingBreakpoint = true; - break; - } - } - - if (havePendingBreakpoint) - { - SetPendingBreakpoints(functionContext); - } - } - } + #endregion triggering breakpoints #endregion internal methods @@ -2448,39 +2383,6 @@ namespace System.Management.Automation } } - /// - /// SetBreakpoints. - /// - /// - public override void SetBreakpoints(IEnumerable breakpoints) - { - if (breakpoints == null) - { - throw new PSArgumentNullException("breakpoints"); - } - - foreach (var breakpoint in breakpoints) - { - if (_idToBreakpoint.ContainsKey(breakpoint.Id)) { continue; } - - switch (breakpoint) - { - case LineBreakpoint lineBp: - AddLineBreakpoint(lineBp); - continue; - case CommandBreakpoint cmdBp: - AddCommandBreakpoint(cmdBp); - continue; - case VariableBreakpoint variableBp: - AddVariableBreakpoint(variableBp); - continue; - default: - // Unreachable default block - break; - } - } - } - /// /// True when debugger is active with breakpoints. /// @@ -2676,16 +2578,147 @@ namespace System.Management.Automation } } + #region Breakpoints + + /// + /// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets. + /// + /// Id of the breakpoint you want. + public override Breakpoint GetBreakpoint(int id) + { + _idToBreakpoint.TryGetValue(id, out Breakpoint breakpoint); + return breakpoint; + } + + /// + /// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet. + /// + public override List GetBreakpoints() + { + return (from bp in _idToBreakpoint.Values orderby bp.Id select bp).ToList(); + } + + /// + /// Sets a command breakpoint in the debugger. + /// + /// The name of the command that will trigger the breakpoint. This value is required and may not be null. + /// The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit. + /// The path to the script file where the breakpoint may be hit. If null, the breakpoint may be hit anywhere the command is invoked. + /// + public override CommandBreakpoint SetCommandBreakpoint(string command, ScriptBlock action = null, string path = null) + { + Diagnostics.Assert(!string.IsNullOrEmpty(command), "Caller to verify command is not null or empty."); + + WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase); + + CheckForBreakpointSupport(); + return AddCommandBreakpoint(new CommandBreakpoint(path, pattern, command, action)); + } + + /// + /// Sets a line breakpoint in the debugger. + /// + /// The path to the script file where the breakpoint may be hit. This value is required and may not be null. + /// The line in the script file where the breakpoint may be hit. This value is required and must be greater than or equal to 1. + /// The column in the script file where the breakpoint may be hit. If 0, the breakpoint will trigger on any statement on the line. + /// The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit. + /// + public override LineBreakpoint SetLineBreakpoint(string path, int line, int column = 0, ScriptBlock action = null) + { + Diagnostics.Assert(path != null, "Caller to verify path is not null."); + Diagnostics.Assert(line > 0, "Caller to verify line is greater than 0."); + + CheckForBreakpointSupport(); + return AddLineBreakpoint(new LineBreakpoint(path, line, column, action)); + } + + /// + /// Sets a variable breakpoint in the debugger. + /// + /// The name of the variable that will trigger the breakpoint. This value is required and may not be null. + /// The variable access mode that will trigger the breakpoint. By default variable breakpoints will trigger only when the variable is updated. + /// The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit. + /// The path to the script file where the breakpoint may be hit. If null, the breakpoint may be hit anywhere the variable is accessed using the specified access mode. + /// + public override VariableBreakpoint SetVariableBreakpoint(string variableName, VariableAccessMode accessMode = VariableAccessMode.Write, ScriptBlock action = null, string path = null) + { + Diagnostics.Assert(!string.IsNullOrEmpty(variableName), "Caller to verify variableName is not null or empty."); + + CheckForBreakpointSupport(); + return AddVariableBreakpoint(new VariableBreakpoint(path, variableName, accessMode, action)); + } + + // This is the implementation of the Remove-PSBreakpoint cmdlet. + public override bool RemoveBreakpoint(Breakpoint breakpoint) + { + Diagnostics.Assert(breakpoint != null, "Caller to verify the breakpoint is not null."); + + if (_idToBreakpoint.Remove(breakpoint.Id, out _)) + { + breakpoint.RemoveSelf(this); + + if (CanDisableDebugger) + { + SetInternalDebugMode(InternalDebugMode.Disabled); + } + + OnBreakpointUpdated(new BreakpointUpdatedEventArgs(breakpoint, BreakpointUpdateType.Removed, _idToBreakpoint.Count)); + + return true; + } + + return false; + } + + + // This is the implementation of the Enable-PSBreakpoint cmdlet. + public override Breakpoint EnableBreakpoint(Breakpoint breakpoint) + { + Diagnostics.Assert(breakpoint != null, "Caller to verify the breakpoint is not null."); + + if (_idToBreakpoint.TryGetValue(breakpoint.Id, out _)) + { + breakpoint.SetEnabled(true); + OnBreakpointUpdated(new BreakpointUpdatedEventArgs(breakpoint, BreakpointUpdateType.Enabled, _idToBreakpoint.Count)); + + return breakpoint; + } + + return null; + } + + // This is the implementation of the Disable-PSBreakpoint cmdlet. + public override Breakpoint DisableBreakpoint(Breakpoint breakpoint) + { + Diagnostics.Assert(breakpoint != null, "Caller to verify the breakpoint is not null."); + + if (_idToBreakpoint.TryGetValue(breakpoint.Id, out _)) + { + breakpoint.SetEnabled(false); + OnBreakpointUpdated(new BreakpointUpdatedEventArgs(breakpoint, BreakpointUpdateType.Disabled, _idToBreakpoint.Count)); + + return breakpoint; + } + + return null; + } + + #endregion Breakpoints + #region Job Debugging /// /// Sets up debugger to debug provided job or its child jobs. /// /// - /// Job object that is either a debuggable job or a container - /// of debuggable child jobs. + /// Job object that is either a debuggable job or a container of + /// debuggable child jobs. /// - internal override void DebugJob(Job job) + /// + /// If true, the debugger automatically invokes a break all when it + /// attaches to the job. + /// + internal override void DebugJob(Job job, bool breakAll) { if (job == null) { throw new PSArgumentNullException("job"); } @@ -2699,13 +2732,13 @@ namespace System.Management.Automation // If a debuggable job was passed in then add it to the // job running list. - bool jobsAdded = TryAddDebugJob(job); + bool jobsAdded = TryAddDebugJob(job, breakAll); if (!jobsAdded) { // Otherwise treat as parent Job and iterate over child jobs. foreach (Job childJob in job.ChildJobs) { - if (TryAddDebugJob(childJob) && !jobsAdded) + if (TryAddDebugJob(childJob, breakAll) && !jobsAdded) { jobsAdded = true; } @@ -2718,7 +2751,7 @@ namespace System.Management.Automation } } - private bool TryAddDebugJob(Job job) + private bool TryAddDebugJob(Job job, bool breakAll) { IJobDebugger debuggableJob = job as IJobDebugger; if ((debuggableJob != null) && (debuggableJob.Debugger != null) && @@ -2731,7 +2764,7 @@ namespace System.Management.Automation SetDebugJobAsync(debuggableJob, false); AddToJobRunningList( new PSJobStartEventArgs(job, debuggableJob.Debugger, false), - DebuggerResumeAction.StepInto); + breakAll ? DebuggerResumeAction.StepInto : DebuggerResumeAction.Continue); // Raise debug stop event if job is already in stopped state. if (jobDebugAlreadyStopped) @@ -2794,17 +2827,17 @@ namespace System.Management.Automation #region Runspace Debugging - internal override void DebugRunspace(Runspace runspace) - { - DebugRunspace(runspace, disableBreakAll: false); - } - /// /// Sets up debugger to debug provided Runspace in a nested debug session. /// - /// Runspace to debug. - /// When specified, it will not turn on BreakAll. - internal override void DebugRunspace(Runspace runspace, bool disableBreakAll) + /// + /// Runspace to debug. + /// + /// + /// When true, this command will invoke a BreakAll when the debugger is + /// first attached. + /// + internal override void DebugRunspace(Runspace runspace, bool breakAll) { if (runspace == null) { @@ -2839,7 +2872,7 @@ namespace System.Management.Automation AddToRunningRunspaceList(new PSStandaloneMonitorRunspaceInfo(runspace)); - if (!runspace.Debugger.InBreakpoint && !disableBreakAll) + if (!runspace.Debugger.InBreakpoint && breakAll) { EnableDebuggerStepping(EnableNestedType.NestedRunspace); } @@ -3745,7 +3778,7 @@ namespace System.Management.Automation { WaitForReadyDebug(); - DebugRunspace(runspace); + DebugRunspace(runspace, breakAll:true); // Block this event thread until debugging has ended. WaitForDebugComplete(); @@ -4077,28 +4110,30 @@ namespace System.Management.Automation return _wrappedDebugger.ProcessCommand(command, output); } - /// - /// Adds the provided set of breakpoints to the debugger. - /// - /// Breakpoints. - public override void SetBreakpoints(IEnumerable breakpoints) - { - _wrappedDebugger.SetBreakpoints(breakpoints); - } - - /// - /// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets. - /// - /// Id of the breakpoint you want. public override Breakpoint GetBreakpoint(int id) => _wrappedDebugger.GetBreakpoint(id); - /// - /// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet. - /// public override List GetBreakpoints() => _wrappedDebugger.GetBreakpoints(); + public override CommandBreakpoint SetCommandBreakpoint(string command, ScriptBlock action = null, string path = null) => + _wrappedDebugger.SetCommandBreakpoint(command, action, path); + + public override LineBreakpoint SetLineBreakpoint(string path, int line, int column = 0, ScriptBlock action = null) => + _wrappedDebugger.SetLineBreakpoint(path, line, column, action); + + public override VariableBreakpoint SetVariableBreakpoint(string variableName, VariableAccessMode accessMode = VariableAccessMode.Write, ScriptBlock action = null, string path = null) => + _wrappedDebugger.SetVariableBreakpoint(variableName, accessMode, action, path); + + public override bool RemoveBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.RemoveBreakpoint(breakpoint); + + public override Breakpoint EnableBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.EnableBreakpoint(breakpoint); + + public override Breakpoint DisableBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.DisableBreakpoint(breakpoint); + /// /// SetDebuggerAction. /// @@ -5397,12 +5432,6 @@ namespace System.Management.Automation.Internal [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Needed Internal use only")] public static class DebuggerUtils { - internal const string SetDebugModeFunctionName = "__Set-PSDebugMode"; - internal const string SetDebuggerActionFunctionName = "__Set-PSDebuggerAction"; - internal const string GetDebuggerStopArgsFunctionName = "__Get-PSDebuggerStopArgs"; - internal const string SetDebuggerStepMode = "__Set-PSDebuggerStepMode"; - internal const string SetPSUnhandledBreakpointMode = "__Set-PSUnhandledBreakpointMode"; - private static SortedSet s_noHistoryCommandNames = new SortedSet(StringComparer.OrdinalIgnoreCase) { "prompt", diff --git a/src/System.Management.Automation/engine/hostifaces/PSTask.cs b/src/System.Management.Automation/engine/hostifaces/PSTask.cs index 6ca12b0405..59652393b1 100644 --- a/src/System.Management.Automation/engine/hostifaces/PSTask.cs +++ b/src/System.Management.Automation/engine/hostifaces/PSTask.cs @@ -1060,15 +1060,6 @@ namespace System.Management.Automation.PSTasks return _wrappedDebugger.ProcessCommand(command, output); } - /// - /// Adds the provided set of breakpoints to the debugger. - /// - /// List of breakpoints. - public override void SetBreakpoints(IEnumerable breakpoints) - { - _wrappedDebugger.SetBreakpoints(breakpoints); - } - /// /// Sets the debugger resume action. /// @@ -1078,6 +1069,27 @@ namespace System.Management.Automation.PSTasks _wrappedDebugger.SetDebuggerAction(resumeAction); } + public override Breakpoint GetBreakpoint(int id) => + _wrappedDebugger.GetBreakpoint(id); + + public override CommandBreakpoint SetCommandBreakpoint(string command, ScriptBlock action = null, string path = null) => + _wrappedDebugger.SetCommandBreakpoint(command, action, path); + + public override VariableBreakpoint SetVariableBreakpoint(string variableName, VariableAccessMode accessMode = VariableAccessMode.Write, ScriptBlock action = null, string path = null) => + _wrappedDebugger.SetVariableBreakpoint(variableName, accessMode, action, path); + + public override LineBreakpoint SetLineBreakpoint(string path, int line, int column = 0, ScriptBlock action = null) => + _wrappedDebugger.SetLineBreakpoint(path, line, column, action); + + public override Breakpoint EnableBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.EnableBreakpoint(breakpoint); + + public override Breakpoint DisableBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.DisableBreakpoint(breakpoint); + + public override bool RemoveBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.RemoveBreakpoint(breakpoint); + /// /// Stops a running command. /// diff --git a/src/System.Management.Automation/engine/remoting/client/Job.cs b/src/System.Management.Automation/engine/remoting/client/Job.cs index 8331f12085..18a5094228 100644 --- a/src/System.Management.Automation/engine/remoting/client/Job.cs +++ b/src/System.Management.Automation/engine/remoting/client/Job.cs @@ -3909,28 +3909,30 @@ namespace System.Management.Automation return _wrappedDebugger.ProcessCommand(command, output); } - /// - /// Adds the provided set of breakpoints to the debugger. - /// - /// Breakpoints. - public override void SetBreakpoints(IEnumerable breakpoints) - { - _wrappedDebugger.SetBreakpoints(breakpoints); - } - - /// - /// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets. - /// - /// Id of the breakpoint you want. public override Breakpoint GetBreakpoint(int id) => _wrappedDebugger.GetBreakpoint(id); - /// - /// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet. - /// public override List GetBreakpoints() => _wrappedDebugger.GetBreakpoints(); + public override CommandBreakpoint SetCommandBreakpoint(string command, ScriptBlock action = null, string path = null) => + _wrappedDebugger.SetCommandBreakpoint(command, action, path); + + public override LineBreakpoint SetLineBreakpoint(string path, int line, int column = 0, ScriptBlock action = null) => + _wrappedDebugger.SetLineBreakpoint(path, line, column, action); + + public override VariableBreakpoint SetVariableBreakpoint(string variableName, VariableAccessMode accessMode = VariableAccessMode.Write, ScriptBlock action = null, string path = null) => + _wrappedDebugger.SetVariableBreakpoint(variableName, accessMode, action, path); + + public override bool RemoveBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.RemoveBreakpoint(breakpoint); + + public override Breakpoint EnableBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.EnableBreakpoint(breakpoint); + + public override Breakpoint DisableBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.DisableBreakpoint(breakpoint); + /// /// Sets the debugger resume action. /// diff --git a/src/System.Management.Automation/engine/remoting/client/remoterunspace.cs b/src/System.Management.Automation/engine/remoting/client/remoterunspace.cs index 086094fc0c..594ab4a525 100644 --- a/src/System.Management.Automation/engine/remoting/client/remoterunspace.cs +++ b/src/System.Management.Automation/engine/remoting/client/remoterunspace.cs @@ -1809,7 +1809,8 @@ namespace System.Management.Automation private bool _remoteDebugSupported; private bool _isActive; private int _breakpointCount; - private Version _serverPSVersion; + private RemoteDebuggingCapability _remoteDebuggingCapability; + private bool? _remoteBreakpointManagementIsSupported; private volatile bool _handleDebuggerStop; private bool _isDebuggerSteppingEnabled; private UnhandledBreakpointProcessingMode _unhandledBreakpointMode; @@ -2005,27 +2006,165 @@ namespace System.Management.Automation } } - /// - /// Adds the provided set of breakpoints to the debugger. - /// - /// Breakpoints. - public override void SetBreakpoints(IEnumerable breakpoints) - { - _runspace.Debugger?.SetBreakpoints(breakpoints); - } - /// /// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets. /// /// Id of the breakpoint you want. - public override Breakpoint GetBreakpoint(int id) => - _runspace.Debugger?.GetBreakpoint(id); + public override Breakpoint GetBreakpoint(int id) + { + // This is supported only for PowerShell versions >= 7.0 + CheckRemoteBreakpointManagementSupport(RemoteDebuggingCommands.GetBreakpoint); + + return InvokeRemoteBreakpointFunction( + RemoteDebuggingCommands.GetBreakpoint, + new Dictionary + { + { "Id", id }, + }); + } /// /// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet. /// - public override List GetBreakpoints() => - _runspace.Debugger?.GetBreakpoints(); + public override List GetBreakpoints() + { + // This is supported only for PowerShell versions >= 7.0 + CheckRemoteBreakpointManagementSupport(RemoteDebuggingCommands.GetBreakpoint); + + CheckForValidateState(); + + var breakpoints = new List(); + + using (PowerShell ps = GetNestedPowerShell()) + { + ps.AddCommand(RemoteDebuggingCommands.GetBreakpoint); + + Collection output = ps.Invoke(); + foreach (var item in output) + { + if (item?.BaseObject is Breakpoint bp) + { + breakpoints.Add(bp); + } + } + } + + return breakpoints; + } + + public override CommandBreakpoint SetCommandBreakpoint(string command, ScriptBlock action = null, string path = null) + { + // This is supported only for PowerShell versions >= 7.0 + CheckRemoteBreakpointManagementSupport(RemoteDebuggingCommands.SetBreakpoint); + + var functionParameters = new Dictionary + { + { "Command", command }, + }; + + if (action != null) + { + functionParameters.Add("Action", action); + } + + if (path != null) + { + functionParameters.Add("Script", path); + } + + return InvokeRemoteBreakpointFunction(RemoteDebuggingCommands.SetBreakpoint, functionParameters); + } + + public override LineBreakpoint SetLineBreakpoint(string path, int line, int column = 0, ScriptBlock action = null) + { + // This is supported only for PowerShell versions >= 7.0 + CheckRemoteBreakpointManagementSupport(RemoteDebuggingCommands.SetBreakpoint); + + var functionParameters = new Dictionary + { + { "Script", path }, + { "Line", line }, + }; + + if (column != 0) + { + functionParameters.Add("Column", column); + } + + if (action != null) + { + functionParameters.Add("Action", action); + } + + return InvokeRemoteBreakpointFunction(RemoteDebuggingCommands.SetBreakpoint, functionParameters); + } + + public override VariableBreakpoint SetVariableBreakpoint(string variableName, VariableAccessMode accessMode = VariableAccessMode.Write, ScriptBlock action = null, string path = null) + { + // This is supported only for PowerShell versions >= 7.0 + CheckRemoteBreakpointManagementSupport(RemoteDebuggingCommands.SetBreakpoint); + + var functionParameters = new Dictionary + { + { "Variable", variableName }, + }; + + if (accessMode != VariableAccessMode.Write) + { + functionParameters.Add("Mode", accessMode); + } + + if (action != null) + { + functionParameters.Add("Action", action); + } + + if (path != null) + { + functionParameters.Add("Script", path); + } + + return InvokeRemoteBreakpointFunction(RemoteDebuggingCommands.SetBreakpoint, functionParameters); + } + + public override bool RemoveBreakpoint(Breakpoint breakpoint) + { + // This is supported only for PowerShell versions >= 7.0 + CheckRemoteBreakpointManagementSupport(RemoteDebuggingCommands.RemoveBreakpoint); + + return InvokeRemoteBreakpointFunction( + RemoteDebuggingCommands.RemoveBreakpoint, + new Dictionary + { + { "Id", breakpoint.Id }, + }); + } + + public override Breakpoint EnableBreakpoint(Breakpoint breakpoint) + { + // This is supported only for PowerShell versions >= 7.0 + CheckRemoteBreakpointManagementSupport(RemoteDebuggingCommands.EnableBreakpoint); + + return InvokeRemoteBreakpointFunction( + RemoteDebuggingCommands.EnableBreakpoint, + new Dictionary + { + { "Id", breakpoint.Id }, + }); + } + + public override Breakpoint DisableBreakpoint(Breakpoint breakpoint) + { + // This is supported only for PowerShell versions >= 7.0 + CheckRemoteBreakpointManagementSupport(RemoteDebuggingCommands.DisableBreakpoint); + + return InvokeRemoteBreakpointFunction( + RemoteDebuggingCommands.DisableBreakpoint, + new Dictionary + { + { "Id", breakpoint.Id }, + }); + } /// /// SetDebuggerAction. @@ -2039,7 +2178,7 @@ namespace System.Management.Automation using (PowerShell ps = GetNestedPowerShell()) { - ps.AddCommand(DebuggerUtils.SetDebuggerActionFunctionName).AddParameter("ResumeAction", resumeAction); + ps.AddCommand(RemoteDebuggingCommands.SetDebuggerAction).AddParameter("ResumeAction", resumeAction); ps.Invoke(); // If an error exception is returned then throw it here. @@ -2065,7 +2204,7 @@ namespace System.Management.Automation { using (PowerShell ps = GetNestedPowerShell()) { - ps.AddCommand(DebuggerUtils.GetDebuggerStopArgsFunctionName); + ps.AddCommand(RemoteDebuggingCommands.GetDebuggerStopArgs); Collection output = ps.Invoke(); foreach (var item in output) { @@ -2102,7 +2241,7 @@ namespace System.Management.Automation using (PowerShell ps = GetNestedPowerShell()) { ps.SetIsNested(false); - ps.AddCommand(DebuggerUtils.SetDebugModeFunctionName).AddParameter("Mode", mode); + ps.AddCommand(RemoteDebuggingCommands.SetDebugMode).AddParameter("Mode", mode); ps.Invoke(); } @@ -2120,8 +2259,7 @@ namespace System.Management.Automation CheckForValidateState(); // This is supported only for PowerShell versions >= 5.0 - if ((_serverPSVersion == null) || - (_serverPSVersion.Major < PSVersionInfo.PSV5Version.Major)) + if (!_remoteDebuggingCapability.IsCommandSupported(RemoteDebuggingCommands.SetDebuggerStepMode)) { return; } @@ -2134,7 +2272,7 @@ namespace System.Management.Automation // Send Enable-DebuggerStepping virtual command. using (PowerShell ps = GetNestedPowerShell()) { - ps.AddCommand(DebuggerUtils.SetDebuggerStepMode).AddParameter("Enabled", enabled); + ps.AddCommand(RemoteDebuggingCommands.SetDebuggerStepMode).AddParameter("Enabled", enabled); ps.Invoke(); _isDebuggerSteppingEnabled = enabled; } @@ -2202,8 +2340,7 @@ namespace System.Management.Automation CheckForValidateState(); // This is supported only for PowerShell versions >= 5.0 - if ((_serverPSVersion == null) || - (_serverPSVersion < PSVersionInfo.PSV5Version)) + if (!_remoteDebuggingCapability.IsCommandSupported(RemoteDebuggingCommands.SetUnhandledBreakpointMode)) { return; } @@ -2213,7 +2350,7 @@ namespace System.Management.Automation // Send Set-PSUnhandledBreakpointMode virtual command. using (PowerShell ps = GetNestedPowerShell()) { - ps.AddCommand(DebuggerUtils.SetPSUnhandledBreakpointMode).AddParameter("UnhandledBreakpointMode", value); + ps.AddCommand(RemoteDebuggingCommands.SetUnhandledBreakpointMode).AddParameter("UnhandledBreakpointMode", value); ps.Invoke(); } @@ -2303,7 +2440,7 @@ namespace System.Management.Automation SetRemoteDebug(true, RunspaceAvailability.RemoteDebug); } - _serverPSVersion = serverPSVersion; + _remoteDebuggingCapability = RemoteDebuggingCapability.CreateDebuggingCapability(serverPSVersion); _breakpointCount = breakpointCount; _isDebuggerSteppingEnabled = breakAll; @@ -2610,6 +2747,59 @@ namespace System.Management.Automation } } + private T InvokeRemoteBreakpointFunction(string functionName, Dictionary parameters) + { + CheckForValidateState(); + + using (PowerShell ps = GetNestedPowerShell()) + { + ps.AddCommand(functionName); + foreach (var parameterName in parameters.Keys) + { + ps.AddParameter(parameterName, parameters[parameterName]); + } + + Collection output = ps.Invoke(); + + // If an error exception is returned then throw it here. + if (ps.ErrorBuffer.Count > 0) + { + Exception e = ps.ErrorBuffer[0].Exception; + if (e != null) + { + throw e; + } + } + + // This helper is only used to return a single output item of type T. + foreach (var item in output) + { + if (item?.BaseObject is T) + { + return (T)item.BaseObject; + } + } + + return default(T); + } + } + + private void CheckRemoteBreakpointManagementSupport(string breakpointCommandNameToCheck) + { + if (_remoteBreakpointManagementIsSupported == null) + { + _remoteBreakpointManagementIsSupported = _remoteDebuggingCapability.IsCommandSupported(breakpointCommandNameToCheck); + } + + if (!_remoteBreakpointManagementIsSupported.Value) + { + throw new PSNotSupportedException( + StringUtil.Format( + DebuggerStrings.CommandNotSupportedForRemoteUseInServerDebugger, + RemoteDebuggingCommands.CleanCommandName(breakpointCommandNameToCheck))); + } + } + #endregion } diff --git a/src/System.Management.Automation/engine/remoting/commands/DebugJob.cs b/src/System.Management.Automation/engine/remoting/commands/DebugJob.cs index 954bf415f1..385182fc12 100644 --- a/src/System.Management.Automation/engine/remoting/commands/DebugJob.cs +++ b/src/System.Management.Automation/engine/remoting/commands/DebugJob.cs @@ -97,6 +97,13 @@ namespace Microsoft.PowerShell.Commands set; } + /// + /// Gets or sets a flag that tells PowerShell to automatically perform a BreakAll when the debugger is attached to the remote target. + /// + [Experimental("Microsoft.PowerShell.Utility.PSManageBreakpointsInRunspace", ExperimentAction.Show)] + [Parameter] + public SwitchParameter BreakAll { get; set; } + #endregion #region Overrides @@ -177,7 +184,7 @@ namespace Microsoft.PowerShell.Commands // Set up host script debugger to debug the job. _debugger = runspace.Debugger; - _debugger.DebugJob(_job); + _debugger.DebugJob(_job, breakAll: BreakAll); // Blocking call. Send job output to host UI while debugging and wait for Job completion. WaitAndReceiveJobOutput(); diff --git a/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteDebuggingCapability.cs b/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteDebuggingCapability.cs new file mode 100644 index 0000000000..c093e09263 --- /dev/null +++ b/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteDebuggingCapability.cs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Management.Automation.Remoting +{ + /// + /// This class contains information about the debugging capability of the server side of the + /// MS-PSRDP connection. The functionality that is supported is determined by the PowerShell + /// version on the server. These capabilities will be used in remote debugging sessions to + /// determine what is supported by the server. + /// + internal class RemoteDebuggingCapability + { + private readonly HashSet _supportedCommands = new HashSet(); + + internal Version PSVersion { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The version of PowerShell used on the remote server debugger. + /// + /// + /// This should only be invoked by the static create method. + /// + private RemoteDebuggingCapability(Version powerShellVersion) + { + PSVersion = powerShellVersion; + + // Commands available in all server versions + _supportedCommands.Add(RemoteDebuggingCommands.GetDebuggerStopArgs); + _supportedCommands.Add(RemoteDebuggingCommands.SetDebuggerAction); + _supportedCommands.Add(RemoteDebuggingCommands.SetDebugMode); + + if (PSVersion == null) + { + return; + } + + // Commands added in v5 + if (PSVersion.Major >= PSVersionInfo.PSV5Version.Major) + { + _supportedCommands.Add(RemoteDebuggingCommands.SetDebuggerStepMode); + _supportedCommands.Add(RemoteDebuggingCommands.SetUnhandledBreakpointMode); + } + + // Commands added in v7 + if (PSVersion.Major >= PSVersionInfo.PSV7Version.Major) + { + _supportedCommands.Add(RemoteDebuggingCommands.GetBreakpoint); + _supportedCommands.Add(RemoteDebuggingCommands.SetBreakpoint); + _supportedCommands.Add(RemoteDebuggingCommands.EnableBreakpoint); + _supportedCommands.Add(RemoteDebuggingCommands.DisableBreakpoint); + _supportedCommands.Add(RemoteDebuggingCommands.RemoveBreakpoint); + } + } + + /// + /// Creates a instance that can be + /// used to identify the remoting capabilities of the server debugger. + /// + /// + /// The version of PowerShell used on the remote server debugger. + /// + /// + /// A new RemoteDebuggingCapability instance that is based on the version + /// of PowerShell used on the remote server debugger. + /// + internal static RemoteDebuggingCapability CreateDebuggingCapability(Version powerShellVersion) => + new RemoteDebuggingCapability(powerShellVersion); + + /// + /// Checks if a command is supported in the server version used to create + /// this instance. + /// + /// + /// The name of the command to check. + /// + /// + /// True if the command is supported; false otherwise. + /// + internal bool IsCommandSupported(string commandName) => + _supportedCommands.Contains(commandName); + } + + internal static class RemoteDebuggingCommands + { + #region DO NOT REMOVE OR CHANGE THE VALUES OF THESE CONSTANTS - it will break remote debugging compatibility with PowerShell + + // Commands related to debugger stop events + internal const string GetDebuggerStopArgs = "__Get-PSDebuggerStopArgs"; + internal const string SetDebuggerAction = "__Set-PSDebuggerAction"; + + // Miscellaneous debug commands + internal const string SetDebuggerStepMode = "__Set-PSDebuggerStepMode"; + internal const string SetDebugMode = "__Set-PSDebugMode"; + internal const string SetUnhandledBreakpointMode = "__Set-PSUnhandledBreakpointMode"; + + // Breakpoint commands + internal const string GetBreakpoint = "__Get-PSBreakpoint"; + internal const string SetBreakpoint = "__Set-PSBreakpoint"; + internal const string EnableBreakpoint = "__Enable-PSBreakpoint"; + internal const string DisableBreakpoint = "__Disable-PSBreakpoint"; + internal const string RemoveBreakpoint = "__Remove-PSBreakpoint"; + + #endregion + + internal static string CleanCommandName(string commandName) + { + return commandName.TrimStart('_'); + } + } +} diff --git a/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs b/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs index 42005deac1..c082f0a76c 100644 --- a/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs +++ b/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs @@ -704,9 +704,9 @@ namespace System.Management.Automation /// /// Handle the invocation of powershell. /// - /// Sender of this event, unused. + /// Sender of this event, unused. /// Arguments describing this event. - private void HandleCreateAndInvokePowerShell(object sender, RemoteDataEventArgs> eventArgs) + private void HandleCreateAndInvokePowerShell(object _, RemoteDataEventArgs> eventArgs) { RemoteDataObject data = eventArgs.Data; @@ -1194,7 +1194,8 @@ namespace System.Management.Automation private enum PreProcessCommandResult { /// - /// No debugger pre-processing. + /// No debugger pre-processing. "Get" commands use this so that the + /// data they retrieve can be sent back to the caller. /// None = 0, @@ -1204,11 +1205,6 @@ namespace System.Management.Automation /// ValidNotProcessed, - /// - /// GetDebuggerStopArgs. - /// - GetDebuggerStopArgs, - /// /// SetDebuggerAction. /// @@ -1227,7 +1223,7 @@ namespace System.Management.Automation /// /// SetPreserveUnhandledBreakpointMode. /// - SetPreserveUnhandledBreakpointMode + SetPreserveUnhandledBreakpointMode, }; private class DebuggerCommandArgument @@ -1266,35 +1262,31 @@ namespace System.Management.Automation var command = commands.Commands[0]; string commandText = command.CommandText; - if (commandText.Equals(DebuggerUtils.GetDebuggerStopArgsFunctionName, StringComparison.OrdinalIgnoreCase)) + if (commandText.Equals(RemoteDebuggingCommands.GetDebuggerStopArgs, StringComparison.OrdinalIgnoreCase)) { - // // __Get-PSDebuggerStopArgs private virtual command. // No input parameters. // Returns DebuggerStopEventArgs object. - // // Evaluate this command only if the debugger is activated. - if (!isDebuggerActive) { return PreProcessCommandResult.ValidNotProcessed; } + if (!isDebuggerActive) + { + return PreProcessCommandResult.ValidNotProcessed; + } - // Translate into debugger method call. - ScriptBlock scriptBlock = ScriptBlock.Create("$host.Runspace.Debugger.GetDebuggerStopArgs()"); - scriptBlock.LanguageMode = PSLanguageMode.FullLanguage; - commands.Clear(); - commands.AddCommand("Invoke-Command").AddParameter("ScriptBlock", scriptBlock).AddParameter("NoNewScope", true); - - result = PreProcessCommandResult.GetDebuggerStopArgs; + ReplaceVirtualCommandWithScript(commands, "$host.Runspace.Debugger.GetDebuggerStopArgs()"); } - else if (commandText.Equals(DebuggerUtils.SetDebuggerActionFunctionName, StringComparison.OrdinalIgnoreCase)) + else if (commandText.Equals(RemoteDebuggingCommands.SetDebuggerAction, StringComparison.OrdinalIgnoreCase)) { - // // __Set-PSDebuggerAction private virtual command. // DebuggerResumeAction enum input parameter. // Returns void. - // // Evaluate this command only if the debugger is activated. - if (!isDebuggerActive) { return PreProcessCommandResult.ValidNotProcessed; } + if (!isDebuggerActive) + { + return PreProcessCommandResult.ValidNotProcessed; + } if ((command.Parameters == null) || (command.Parameters.Count == 0) || (!command.Parameters[0].Name.Equals("ResumeAction", StringComparison.OrdinalIgnoreCase))) @@ -1313,22 +1305,14 @@ namespace System.Management.Automation catch (InvalidCastException) { } } - if (resumeAction == null) - { - throw new PSArgumentException("ResumeAction"); - } - - commandArgument.ResumeAction = resumeAction; + commandArgument.ResumeAction = resumeAction ?? throw new PSArgumentException("ResumeAction"); result = PreProcessCommandResult.SetDebuggerAction; } - else if (commandText.Equals(DebuggerUtils.SetDebugModeFunctionName, StringComparison.OrdinalIgnoreCase)) + else if (commandText.Equals(RemoteDebuggingCommands.SetDebugMode, StringComparison.OrdinalIgnoreCase)) { - // // __Set-PSDebugMode private virtual command. // DebugModes enum input parameter. // Returns void. - // - if ((command.Parameters == null) || (command.Parameters.Count == 0) || (!command.Parameters[0].Name.Equals("Mode", StringComparison.OrdinalIgnoreCase))) { @@ -1346,22 +1330,14 @@ namespace System.Management.Automation catch (InvalidCastException) { } } - if (mode == null) - { - throw new PSArgumentException("Mode"); - } - - commandArgument.Mode = mode; + commandArgument.Mode = mode ?? throw new PSArgumentException("Mode"); result = PreProcessCommandResult.SetDebugMode; } - else if (commandText.Equals(DebuggerUtils.SetDebuggerStepMode, StringComparison.OrdinalIgnoreCase)) + else if (commandText.Equals(RemoteDebuggingCommands.SetDebuggerStepMode, StringComparison.OrdinalIgnoreCase)) { - // // __Set-PSDebuggerStepMode private virtual command. // Boolean Enabled input parameter. // Returns void. - // - if ((command.Parameters == null) || (command.Parameters.Count == 0) || (!command.Parameters[0].Name.Equals("Enabled", StringComparison.OrdinalIgnoreCase))) { @@ -1372,14 +1348,11 @@ namespace System.Management.Automation commandArgument.DebuggerStepEnabled = enabled; result = PreProcessCommandResult.SetDebuggerStepMode; } - else if (commandText.Equals(DebuggerUtils.SetPSUnhandledBreakpointMode, StringComparison.OrdinalIgnoreCase)) + else if (commandText.Equals(RemoteDebuggingCommands.SetUnhandledBreakpointMode, StringComparison.OrdinalIgnoreCase)) { - // // __Set-PSUnhandledBreakpointMode private virtual command. // UnhandledBreakpointMode input parameter. // Returns void. - // - if ((command.Parameters == null) || (command.Parameters.Count == 0) || (!command.Parameters[0].Name.Equals("UnhandledBreakpointMode", StringComparison.OrdinalIgnoreCase))) { @@ -1397,18 +1370,168 @@ namespace System.Management.Automation catch (InvalidCastException) { } } - if (mode == null) + commandArgument.UnhandledBreakpointMode = mode ?? throw new PSArgumentException("Mode"); + result = PreProcessCommandResult.SetPreserveUnhandledBreakpointMode; + } + else if (commandText.Equals(RemoteDebuggingCommands.GetBreakpoint, StringComparison.OrdinalIgnoreCase)) + { + // __Get-PSBreakpoint private virtual command. + // Input parameters: + // [-Id ] + // Returns Breakpoint object(s). + string script = null; + + if (command.Parameters?.Count > 0) { - throw new PSArgumentException("Mode"); + int breakpointId = CheckBreakpointIdParameter(command); + script = $"$host.Runspace.Debugger.GetBreakpoint({breakpointId})"; + } + else + { + script = $"$host.Runspace.Debugger.GetBreakpoints()"; } - commandArgument.UnhandledBreakpointMode = mode; - result = PreProcessCommandResult.SetPreserveUnhandledBreakpointMode; + ReplaceVirtualCommandWithScript(commands, script); + } + else if (commandText.Equals(RemoteDebuggingCommands.SetBreakpoint, StringComparison.OrdinalIgnoreCase)) + { + // __Set-PSBreakpoint private virtual command. + // Input parameters: + // [-Script] [-Line] [[-Column] ] [-Action ] + // [[-Script] ] -Command [-Action ] + // [[-Script] ] -Variable [-Action ] [-Mode ] + // Returns Breakpoint object. + if (command.Parameters == null || command.Parameters.Count == 0) + { + throw new PSArgumentException("You must provide at least one parameter."); + } + + string breakpointType = null; + foreach (var commandParameter in command.Parameters) + { + if (!commandParameter.Name.Equals("Script", StringComparison.OrdinalIgnoreCase) && + !commandParameter.Name.Equals("Line", StringComparison.OrdinalIgnoreCase) && + !commandParameter.Name.Equals("Column", StringComparison.OrdinalIgnoreCase) && + !commandParameter.Name.Equals("Action", StringComparison.OrdinalIgnoreCase) && + !commandParameter.Name.Equals("Command", StringComparison.OrdinalIgnoreCase) && + !commandParameter.Name.Equals("Variable", StringComparison.OrdinalIgnoreCase) && + !commandParameter.Name.Equals("Mode", StringComparison.OrdinalIgnoreCase)) + { + throw new PSArgumentException(commandParameter.Name); + } + + if (commandParameter.Name.Equals("Line", StringComparison.OrdinalIgnoreCase) || + commandParameter.Name.Equals("Column", StringComparison.OrdinalIgnoreCase)) + { + if (breakpointType != null && breakpointType != "Line") + { + throw new PSArgumentException(commandParameter.Name); + } + + breakpointType = "Line"; + } + else if (commandParameter.Name.Equals("Command", StringComparison.OrdinalIgnoreCase)) + { + if (breakpointType != null) + { + throw new PSArgumentException(commandParameter.Name); + } + + breakpointType = "Command"; + } + else if (commandParameter.Name.Equals("Variable", StringComparison.OrdinalIgnoreCase)) + { + if (breakpointType != null) + { + throw new PSArgumentException(commandParameter.Name); + } + + breakpointType = "Variable"; + } + } + + commands.Clear(); + commands.AddCommand("Set-PSBreakpoint"); + foreach (var commandParameter in command.Parameters) + { + var parameterValue = commandParameter.Name.Equals("Action", StringComparison.OrdinalIgnoreCase) + ? ScriptBlock.Create(commandParameter.Value as string) + : commandParameter.Value; + + commands.AddParameter(commandParameter.Name, parameterValue); + } + } + else if (commandText.Equals(RemoteDebuggingCommands.RemoveBreakpoint, StringComparison.OrdinalIgnoreCase)) + { + // __Remove-PSBreakpoint private virtual command. + // Input parameters: + // -Id + // Returns bool. + + int breakpointId = CheckBreakpointIdParameter(command); + + string script = $"$bp = $host.Runspace.Debugger.GetBreakpoint({breakpointId}); $bp -ne $null -and $host.Runspace.Debugger.RemoveBreakpoint($bp)"; + + ReplaceVirtualCommandWithScript(commands, script); + } + else if (commandText.Equals(RemoteDebuggingCommands.EnableBreakpoint, StringComparison.OrdinalIgnoreCase)) + { + // __Enable-PSBreakpoint private virtual command. + // Input parameters: + // -Id + // Returns Breakpoint. + + int breakpointId = CheckBreakpointIdParameter(command); + + string script = $"$bp = $host.Runspace.Debugger.GetBreakpoint({breakpointId}); if ($bp -ne $null) {{$host.Runspace.Debugger.EnableBreakpoint($bp)}}"; + + ReplaceVirtualCommandWithScript(commands, script); + } + else if (commandText.Equals(RemoteDebuggingCommands.DisableBreakpoint, StringComparison.OrdinalIgnoreCase)) + { + // __Disable-PSBreakpoint private virtual command. + // Input parameters: + // -Id + // Returns Breakpoint. + + int breakpointId = CheckBreakpointIdParameter(command); + + string script = $"$bp = $host.Runspace.Debugger.GetBreakpoint({breakpointId}); if ($bp -ne $null) {{$host.Runspace.Debugger.DisableBreakpoint($bp)}}"; + + ReplaceVirtualCommandWithScript(commands, script); } return result; } + private static void ReplaceVirtualCommandWithScript(PSCommand commands, string script) + { + ScriptBlock scriptBlock = ScriptBlock.Create(script); + scriptBlock.LanguageMode = PSLanguageMode.FullLanguage; + commands.Clear(); + commands.AddCommand("Invoke-Command") + .AddParameter("ScriptBlock", scriptBlock) + .AddParameter("NoNewScope", true); + } + + private static int CheckBreakpointIdParameter(Command command) + { + if (command.Parameters == null || + command.Parameters.Count == 0 || + !command.Parameters[0].Name.Equals("Id", StringComparison.OrdinalIgnoreCase)) + { + throw new PSArgumentException("Id"); + } + + int? breakpointId = command.Parameters[0].Value as int?; + if (breakpointId == null) + { + throw new PSArgumentException("Id"); + } + + return breakpointId.Value; + } + #endregion #region Private Classes @@ -1706,28 +1829,30 @@ namespace System.Management.Automation get { return _inDebugMode; } } - /// - /// Adds the provided set of breakpoints to the debugger. - /// - /// Breakpoints. - public override void SetBreakpoints(IEnumerable breakpoints) - { - _wrappedDebugger.Value.SetBreakpoints(breakpoints); - } - - /// - /// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets. - /// - /// Id of the breakpoint you want. public override Breakpoint GetBreakpoint(int id) => _wrappedDebugger.Value.GetBreakpoint(id); - /// - /// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet. - /// public override List GetBreakpoints() => _wrappedDebugger.Value.GetBreakpoints(); + public override CommandBreakpoint SetCommandBreakpoint(string command, ScriptBlock action = null, string path = null) => + _wrappedDebugger.Value.SetCommandBreakpoint(command, action, path); + + public override LineBreakpoint SetLineBreakpoint(string path, int line, int column = 0, ScriptBlock action = null) => + _wrappedDebugger.Value.SetLineBreakpoint(path, line, column, action); + + public override VariableBreakpoint SetVariableBreakpoint(string variableName, VariableAccessMode accessMode = VariableAccessMode.Write, ScriptBlock action = null, string path = null) => + _wrappedDebugger.Value.SetVariableBreakpoint(variableName, accessMode, action, path); + + public override bool RemoveBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.Value.RemoveBreakpoint(breakpoint); + + public override Breakpoint EnableBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.Value.EnableBreakpoint(breakpoint); + + public override Breakpoint DisableBreakpoint(Breakpoint breakpoint) => + _wrappedDebugger.Value.DisableBreakpoint(breakpoint); + /// /// Exits debugger mode with the provided resume action. /// @@ -1855,13 +1980,15 @@ namespace System.Management.Automation /// Sets up debugger to debug provided job or its child jobs. /// /// - /// Job object that is either a debuggable job or a container - /// of debuggable child jobs. + /// Job object that is either a debuggable job or a container of + /// debuggable child jobs. /// - internal override void DebugJob(Job job) - { - _wrappedDebugger.Value.DebugJob(job); - } + /// + /// If true, the debugger automatically invokes a break all when it + /// attaches to the job. + /// + internal override void DebugJob(Job job, bool breakAll) => + _wrappedDebugger.Value.DebugJob(job, breakAll); /// /// Removes job from debugger job list and pops its @@ -1876,20 +2003,16 @@ namespace System.Management.Automation /// /// Sets up debugger to debug provided Runspace in a nested debug session. /// - /// Runspace to debug. - internal override void DebugRunspace(Runspace runspace) + /// + /// Runspace to debug. + /// + /// + /// When true, this command will invoke a BreakAll when the debugger is + /// first attached. + /// + internal override void DebugRunspace(Runspace runspace, bool breakAll) { - _wrappedDebugger.Value.DebugRunspace(runspace); - } - - /// - /// Sets up debugger to debug provided Runspace in a nested debug session. - /// - /// Runspace to debug. - /// - internal override void DebugRunspace(Runspace runspace, bool disableBreakAll) - { - _wrappedDebugger.Value.DebugRunspace(runspace, disableBreakAll); + _wrappedDebugger.Value.DebugRunspace(runspace, breakAll); } /// diff --git a/src/System.Management.Automation/resources/DebuggerStrings.resx b/src/System.Management.Automation/resources/DebuggerStrings.resx index 90cb124142..79b0cf42a4 100644 --- a/src/System.Management.Automation/resources/DebuggerStrings.resx +++ b/src/System.Management.Automation/resources/DebuggerStrings.resx @@ -237,6 +237,9 @@ The current session does not support debugging; operation will continue. Cannot push a debugger object onto itself. + + The {0} command is not supported for remote use in the version of PowerShell that is running in the remote runspace. + Process diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/EnableDisablePSBreakpoint.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/EnableDisablePSBreakpoint.Tests.ps1 deleted file mode 100644 index 155dc1b900..0000000000 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/EnableDisablePSBreakpoint.Tests.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. -Describe "Enable and Disable PSBreakpoints" -Tag "CI" { - BeforeAll { - $path = Setup -F testbp.ps1 -content "get-command`nget-date`nget-location" -pass - $bp = set-psbreakpoint -script $path -line 2 - disable-psbreakpoint $bp - } - AfterAll { - $bp | remove-psbreakpoint - } - It "Enable-PSBreakpoint should enable the breakpoint" { - $bp.Enabled | Should -BeFalse - Enable-PSBreakpoint $bp - $bp.Enabled | Should -BeTrue - } - It "Disable-PSBreakpoint should disable the breakpoint" { - Enable-PSBreakpoint $bp - $bp.Enabled | Should -BeTrue - Disable-PSBreakpoint $bp - $bp.Enabled | Should -BeFalse - } -} diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/New-PSBreakpoint.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/New-PSBreakpoint.Tests.ps1 deleted file mode 100644 index 544cdff290..0000000000 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/New-PSBreakpoint.Tests.ps1 +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -$FeatureEnabled = $EnabledExperimentalFeatures.Contains('Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints') - -Describe "New-PSBreakpoint Unit Tests - Feature-Enabled" -Tags "CI" { - - BeforeAll { - if (!$FeatureEnabled) { - Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints' to be enabled." -Verbose - $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() - $PSDefaultParameterValues["it:skip"] = $true - return - } - - #Set up script file 1 - $scriptFileName1 = Join-Path $TestDrive -ChildPath breakpointTestScript.ps1 - - $contents = @" -function Hello -{ - `$greeting = 'Hello, world!' - write-host `$greeting -} - -function Goodbye -{ - `$message = 'Good bye, cruel world!' - write-host `$message -} - -Hello -Goodbye - -# The following 2 statements produce null tokens (needed to verify 105473) -# -`$table = @{} - -return -"@ - - $contents > $scriptFileName1 - - # Set up script file 2 - $scriptFileName2 = Join-Path -Path $TestDrive -ChildPath psbreakpointtestscript.ps1 - - "`$var = 1 " > $scriptFileName2 - } - - AfterAll { - if (!$FeatureEnabled) { - $global:PSDefaultParameterValues = $originalDefaultParameterValues - return - } - } - - It "Should be able to set psbreakpoints for -Line" { - $brk = New-PSBreakpoint -Line 13 -Script $scriptFileName1 - $brk.Line | Should -Be 13 - } - - It "Should be able to set psbreakpoints for -Line and -column" { - $brk = New-PSBreakpoint -line 13 -column 1 -script $scriptFileName1 - $brk.Line | Should -Be 13 - $brk.Column | Should -Be 1 - } - - It "Should be able to set psbreakpoints for -Line and -action" { - $brk = New-PSBreakpoint -line 13 -action {{ break; }} -script $scriptFileName1 - $brk.Line | Should -Be 13 - $brk.Action | Should -Match "break" - } - - It "Should be able to set psbreakpoints for -Line, -column and -action" { - $brk = New-PSBreakpoint -line 13 -column 1 -action {{ break; }} -script $scriptFileName1 - $brk.Line | Should -Be 13 - $brk.Column | Should -Be 1 - $brk.Action | Should -Match "break" - } - - It "-script and -line can take multiple items" { - $brk = New-PSBreakpoint -line 11,12,13 -column 1 -script $scriptFileName1,$scriptFileName1 - $brk.Line | Should -BeIn 11,12,13 - $brk.Column | Should -BeIn 1 - } - - It "-script and -line are positional" { - $brk = New-PSBreakpoint $scriptFileName1 13 - $brk.Line | Should -Be 13 - } - - It "-script, -line and -column are positional" { - $brk = New-PSBreakpoint $scriptFileName1 13 1 - $brk.Line | Should -Be 13 - $brk.Column | Should -Be 1 - } - - It "Should throw Exception when missing mandatory parameter -line" -Pending { - $output = pwsh -noninteractive -command "nbp -column 1 -script $scriptFileName1" - [system.string]::Join(" ", $output) | Should -Match "MissingMandatoryParameter,Microsoft.PowerShell.Commands.NewPSBreakpointCommand" - } - - It "Should throw Exception when missing mandatory parameter" -Pending { - $output = pwsh -noprofile -noninteractive -command "nbp -line 1" - [system.string]::Join(" ", $output) | Should -Match "MissingMandatoryParameter,Microsoft.PowerShell.Commands.NewPSBreakpointCommand" - } - - It "Should be able to set psbreakpoints for -command" { - $brk = New-PSBreakpoint -command "write-host" - $brk.Command | Should -BeExactly "write-host" - } - - It "Should be able to set psbreakpoints for -command, -script" { - $brk = New-PSBreakpoint -command "write-host" -script $scriptFileName1 - $brk.Command | Should -BeExactly "write-host" - } - - It "Should be able to set psbreakpoints for -command, -action and -script" { - $brk = New-PSBreakpoint -command "write-host" -action {{ break; }} -script $scriptFileName1 - $brk.Action | Should -Match "break" - } - - It "-Command can take multiple items" { - $brk = New-PSBreakpoint -command write-host,Hello - $brk.Command | Should -Be write-host,Hello - } - - It "-Script is positional" { - $brk = New-PSBreakpoint -command "Hello" $scriptFileName1 - $brk.Command | Should -BeExactly "Hello" - - $brk = New-PSBreakpoint $scriptFileName1 -command "Hello" - $brk.Command | Should -BeExactly "Hello" - } - - It "Should be able to set breakpoints on functions" { - $brk = New-PSBreakpoint -command Hello,Goodbye -script $scriptFileName1 - $brk.Command | Should -Be Hello,Goodbye - } - - It "Should be throw Exception when Column number less than 1" { - { New-PSBreakpoint -line 1 -column -1 -script $scriptFileName1 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.NewPSBreakpointCommand" - } - - It "Should be throw Exception when Line number less than 1" { - $ErrorActionPreference = "Stop" - { New-PSBreakpoint -line -1 -script $scriptFileName1 } | Should -Throw -ErrorId "NewPSBreakpoint:LineLessThanOne,Microsoft.PowerShell.Commands.NewPSBreakpointCommand" - $ErrorActionPreference = "SilentlyContinue" - } - - It "Fail to set psbreakpoints when script is a file of wrong type" { - $tempFile = [System.IO.Path]::GetTempFileName() - $ErrorActionPreference = "Stop" - { - New-PSBreakpoint -Script $tempFile -Line 1 - } | Should -Throw - $ErrorActionPreference = "SilentlyContinue" - Remove-Item $tempFile -Force - } - - It "Fail to set psbreakpoints when script file does not exist" { - $ErrorActionPreference = "Stop" - ${script.ps1} = 10 - { - New-PSBreakpoint -Script variable:\script.ps1 -Line 1 - } | Should -Throw - $ErrorActionPreference = "SilentlyContinue" - } - - It "Should be able to set a psbreakpoint on a line" { - $lineNumber = 1 - $brk = New-PSBreakpoint -Line $lineNumber -Script $scriptFileName2 - $brk.Line | Should -Be $lineNumber - } - - It "Should throw when a string is entered for a line number" { - { - $lineNumber = "one" - New-PSBreakpoint -Line $lineNumber -Script $scriptFileName2 - - } | Should -Throw - } - - It "Should be able to set a psbreakpoint on a Command" { - $command = "theCommand" - $brk = New-PSBreakpoint -Command $command -Script $scriptFileName2 - $brk.Command | Should -Be $command - } - - It "Should be able to set a psbreakpoint on a variable" { - $var = "theVariable" - $brk = New-PSBreakpoint -Command $var -Script $scriptFileName2 - $brk.Command | Should -Be $var - } -} diff --git a/test/powershell/SDK/Breakpoint.Tests.ps1 b/test/powershell/SDK/Breakpoint.Tests.ps1 new file mode 100644 index 0000000000..599b8b4561 --- /dev/null +++ b/test/powershell/SDK/Breakpoint.Tests.ps1 @@ -0,0 +1,168 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +Describe 'Breakpoint SDK Unit Tests' -Tags 'CI' { + + BeforeAll { + # Start a job; this will create a runspace in which we can manage breakpoints + $job = Start-Job -ScriptBlock { + Set-PSBreakpoint -Command Start-Sleep + 1..240 | ForEach-Object { + Start-Sleep -Milliseconds 250 + $_ + Write-Error 'boo' + Write-Verbose 'Verbose' -Verbose + $DebugPreference = 'Continue' + Write-Debug 'Debug' + Write-Warning 'Warning' + } + } + + # Wait for the child job that gets created to hit the breakpoint. This is the + # only safe way to know that the job has actually entered a running state and + # that the remote runspace is listening for requests. + Wait-UntilTrue { $job.ChildJobs.Count -gt 0 -and $job.ChildJobs[0].State -eq 'AtBreakpoint' } -TimeoutInMilliseconds 10000 -IntervalInMilliseconds 250 + + # Get the runspace for the running job + $jobRunspace = $job.ChildJobs[0].Runspace + } + + AfterAll { + # Remove the running job forcibly (whether it has finished or not) + Remove-Job -Job $job -Force + } + + Context 'Managing breakpoints in the host runspace via the SDK' { + + It 'Can set command breakpoints' { + $host.Runspace.Debugger.SetCommandBreakpoint('Test-ThisCommandDoesNotExist') | Should -BeOfType [System.Management.Automation.CommandBreakpoint] + } + + It 'Can set variable breakpoints' { + $host.Runspace.Debugger.SetVariableBreakpoint('DebugPreference', 'ReadWrite', { continue }) | Should -BeOfType [System.Management.Automation.VariableBreakpoint] + } + + It 'Can set line breakpoints' { + $host.Runspace.Debugger.SetLineBreakpoint($PSCommandPath, 1, 1, { continue }) | Should -BeOfType [System.Management.Automation.LineBreakpoint] + } + + It 'Can get breakpoints' { + $host.Runspace.Debugger.GetBreakpoints() | Should -HaveCount 3 + } + + It 'Can disable breakpoints' { + foreach ($bp in $host.Runspace.Debugger.GetBreakpoints()) { + $bp = $host.Runspace.Debugger.DisableBreakpoint($bp) + $bp.Enabled | Should -BeFalse + } + } + + It 'Can enable breakpoints' { + foreach ($bp in $host.Runspace.Debugger.GetBreakpoints()) { + $bp = $host.Runspace.Debugger.EnableBreakpoint($bp) + $bp.Enabled | Should -BeTrue + } + } + + It 'Can remove breakpoints' { + foreach ($bp in $host.Runspace.Debugger.GetBreakpoints()) { + $host.Runspace.Debugger.RemoveBreakpoint($bp) | Should -BeTrue + } + } + + It 'Returns an empty collection when there are no breakpoints' { + $host.Runspace.Debugger.GetBreakpoints() | Should -HaveCount 0 + } + } + + Context 'Managing breakpoints in a remote runspace via the SDK' { + + AfterAll { + # Get rid of any breakpoints that were created in the default runspace. + # This is necessary due to a known bug that causes breakpoints with the + # same id to be created or updated in the default runspace. + Get-PSBreakpoint | Remove-PSBreakpoint + } + + It 'Can set command breakpoints' { + $jobRunspace.Debugger.SetCommandBreakpoint('Write-Verbose', { break }) | Should -BeOfType [System.Management.Automation.CommandBreakpoint] + } + + It 'Can set variable breakpoints' { + $jobRunspace.Debugger.SetVariableBreakpoint('DebugPreference', 'ReadWrite', { break }) | Should -BeOfType [System.Management.Automation.VariableBreakpoint] + } + + It 'Can set line breakpoints' { + $jobRunspace.Debugger.SetLineBreakpoint($PSCommandPath, 1, 1, { break }) | Should -BeOfType [System.Management.Automation.LineBreakpoint] + } + + It 'Can get breakpoints' { + # This is 4, not 3, because we set a breakpoint in our job script + $jobRunspace.Debugger.GetBreakpoints() | Should -HaveCount 4 + } + + It 'Can disable breakpoints' { + foreach ($bp in $jobRunspace.Debugger.GetBreakpoints()) { + $bp = $jobRunspace.Debugger.DisableBreakpoint($bp) + $bp.Enabled | Should -BeFalse + } + } + + It 'Can enable breakpoints' { + foreach ($bp in $jobRunspace.Debugger.GetBreakpoints()) { + $bp = $jobRunspace.Debugger.EnableBreakpoint($bp) + $bp.Enabled | Should -BeTrue + } + } + + It 'Can remove breakpoints' { + foreach ($bp in $jobRunspace.Debugger.GetBreakpoints()) { + $jobRunspace.Debugger.RemoveBreakpoint($bp) | Should -BeTrue + } + } + + It 'Returns an empty collection when there are no breakpoints' { + $jobRunspace.Debugger.GetBreakpoints() | Should -HaveCount 0 + } + } + + Context 'Handling empty collections and errors while managing breakpoints in the host runspace via the SDK' { + + BeforeAll { + $bp = $host.Runspace.Debugger.SetCommandBreakpoint('Test-ThisCommandDoesNotExist') + $host.Runspace.Debugger.RemoveBreakpoint($bp) > $null + } + + It 'Returns false when trying to disable a breakpoint that does not exist' { + $host.Runspace.Debugger.DisableBreakpoint($bp) | Should -Be $null + } + + It 'Returns false when trying to enable a breakpoint that does not exist' { + $host.Runspace.Debugger.EnableBreakpoint($bp) | Should -Be $null + } + + It 'Returns false when trying to remove a breakpoint that does not exist' { + $host.Runspace.Debugger.RemoveBreakpoint($bp) | Should -BeFalse + } + } + + Context 'Handling errors while managing breakpoints in a remote runspace via the SDK' { + + BeforeAll { + $bp = $jobRunspace.Debugger.SetCommandBreakpoint('Test-ThisCommandDoesNotExist') + $jobRunspace.Debugger.RemoveBreakpoint($bp) > $null + } + + It 'Returns false when trying to disable a breakpoint that does not exist' { + $jobRunspace.Debugger.DisableBreakpoint($bp) | Should -Be $null + } + + It 'Returns false when trying to enable a breakpoint that does not exist' { + $jobRunspace.Debugger.EnableBreakpoint($bp) | Should -Be $null + } + + It 'Returns false when trying to remove a breakpoint that does not exist' { + $jobRunspace.Debugger.RemoveBreakpoint($bp) | Should -BeFalse + } + } +} diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 index b164c116e0..f08f9dc54d 100644 --- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 +++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 @@ -116,7 +116,6 @@ Describe "Verify approved aliases list" -Tags "CI" { "Alias", "move", "Move-Item", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "AllScope", "" "Alias", "mp", "Move-ItemProperty", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", "" "Alias", "mv", "Move-Item", $($FullCLR -or $CoreWindows ), "", "", "" -"Alias", "nbp", "New-PSBreakpoint", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", "" "Alias", "nal", "New-Alias", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", "" "Alias", "ndr", "New-PSDrive", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", "" "Alias", "ni", "New-Item", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", "" diff --git a/test/tools/TestMetadata.json b/test/tools/TestMetadata.json index 59c68cff18..605057a873 100644 --- a/test/tools/TestMetadata.json +++ b/test/tools/TestMetadata.json @@ -1,6 +1,5 @@ { "ExperimentalFeatures": { - "Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints": ["test/powershell/Modules/Microsoft.PowerShell.Utility/New-PSBreakpoint.Tests.ps1"], "ExpTest.FeatureOne": [ "test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1" ], "PSForEachObjectParallel": [ "test/powershell/Modules/Microsoft.PowerShell.Utility/Foreach-Object-Parallel.Tests.ps1", "test/powershell/Modules/Microsoft.PowerShell.Security/ConstrainedLanguageRestriction.Tests.ps1" ] } diff --git a/test/xUnit/csharp/test_Runspace.cs b/test/xUnit/csharp/test_Runspace.cs index 38a6dfd1bf..2042ee7d6f 100644 --- a/test/xUnit/csharp/test_Runspace.cs +++ b/test/xUnit/csharp/test_Runspace.cs @@ -101,31 +101,5 @@ namespace PSTests.Sequential runspace.Close(); } } - - [Fact] - public void TestRunspaceSetBreakpoints() - { - using (var runspace = RunspaceFactory.CreateRunspace()) - { - var expectedBreakpoints = new Breakpoint[] { - new LineBreakpoint(@"./path/to/some/file.ps1", 1), - new CommandBreakpoint(@"./path/to/some/file.ps1", new WildcardPattern("Write-Host"), "Write-Host"), - }; - - runspace.Open(); - - try - { - runspace.Debugger.SetBreakpoints(expectedBreakpoints); - List actualBreakpoints = runspace.Debugger.GetBreakpoints(); - Assert.Equal(expectedBreakpoints.Length, actualBreakpoints.Count); - Assert.Equal(expectedBreakpoints, actualBreakpoints); - } - finally - { - runspace.Close(); - } - } - } } }