Skip to content

[Revamp pipeline thread handling] DSC breakpoint capability support #1437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
SydneyhSmith opened this issue Mar 29, 2021 · 1 comment
Closed
Labels
Area-Engine Issue-Bug A bug to squash.

Comments

@SydneyhSmith
Copy link
Collaborator

SydneyhSmith commented Mar 29, 2021

Today the extension has support for DSC breakpoints:

internal class DscBreakpointCapability : IRunspaceCapability
{
private string[] dscResourceRootPaths = Array.Empty<string>();
private Dictionary<string, int[]> breakpointsPerFile =
new Dictionary<string, int[]>();
public async Task<BreakpointDetails[]> SetLineBreakpointsAsync(
PowerShellContextService powerShellContext,
string scriptPath,
BreakpointDetails[] breakpoints)
{
List<BreakpointDetails> resultBreakpointDetails =
new List<BreakpointDetails>();
// We always get the latest array of breakpoint line numbers
// so store that for future use
if (breakpoints.Length > 0)
{
// Set the breakpoints for this scriptPath
this.breakpointsPerFile[scriptPath] =
breakpoints.Select(b => b.LineNumber).ToArray();
}
else
{
// No more breakpoints for this scriptPath, remove it
this.breakpointsPerFile.Remove(scriptPath);
}
string hashtableString =
string.Join(
", ",
this.breakpointsPerFile
.Select(file => $"@{{Path=\"{file.Key}\";Line=@({string.Join(",", file.Value)})}}"));
// Run Enable-DscDebug as a script because running it as a PSCommand
// causes an error which states that the Breakpoint parameter has not
// been passed.
await powerShellContext.ExecuteScriptStringAsync(
hashtableString.Length > 0
? $"Enable-DscDebug -Breakpoint {hashtableString}"
: "Disable-DscDebug",
false,
false).ConfigureAwait(false);
// Verify all the breakpoints and return them
foreach (var breakpoint in breakpoints)
{
breakpoint.Verified = true;
}
return breakpoints.ToArray();
}
public bool IsDscResourcePath(string scriptPath)
{
return dscResourceRootPaths.Any(
dscResourceRootPath =>
scriptPath.StartsWith(
dscResourceRootPath,
StringComparison.CurrentCultureIgnoreCase));
}
public static DscBreakpointCapability CheckForCapability(
RunspaceDetails runspaceDetails,
PowerShellContextService powerShellContext,
ILogger logger)
{
DscBreakpointCapability capability = null;
// DSC support is enabled only for Windows PowerShell.
if ((runspaceDetails.PowerShellVersion.Version.Major < 6) &&
(runspaceDetails.Context != RunspaceContext.DebuggedRunspace))
{
using (PowerShell powerShell = PowerShell.Create())
{
powerShell.Runspace = runspaceDetails.Runspace;
// Attempt to import the updated DSC module
powerShell.AddCommand("Import-Module");
powerShell.AddArgument(@"C:\Program Files\DesiredStateConfiguration\1.0.0.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psd1");
powerShell.AddParameter("PassThru");
powerShell.AddParameter("ErrorAction", "Ignore");
PSObject moduleInfo = null;
try
{
moduleInfo = powerShell.Invoke().FirstOrDefault();
}
catch (RuntimeException e)
{
logger.LogException("Could not load the DSC module!", e);
}
if (moduleInfo != null)
{
logger.LogTrace("Side-by-side DSC module found, gathering DSC resource paths...");
// The module was loaded, add the breakpoint capability
capability = new DscBreakpointCapability();
runspaceDetails.AddCapability(capability);
powerShell.Commands.Clear();
powerShell
.AddCommand("Microsoft.PowerShell.Utility\\Write-Host")
.AddArgument("Gathering DSC resource paths, this may take a while...")
.Invoke();
// Get the list of DSC resource paths
powerShell.Commands.Clear();
powerShell
.AddCommand("Get-DscResource")
.AddCommand("Select-Object")
.AddParameter("ExpandProperty", "ParentPath");
Collection<PSObject> resourcePaths = null;
try
{
resourcePaths = powerShell.Invoke();
}
catch (CmdletInvocationException e)
{
logger.LogException("Get-DscResource failed!", e);
}
if (resourcePaths != null)
{
capability.dscResourceRootPaths =
resourcePaths
.Select(o => (string)o.BaseObject)
.ToArray();
logger.LogTrace($"DSC resources found: {resourcePaths.Count}");
}
else
{
logger.LogTrace($"No DSC resources found.");
}
}
else
{
logger.LogTrace($"Side-by-side DSC module was not found.");
}
}
}
return capability;
}
}

The new pipeline thread consumer's debugger needs to also support these.

The capability has been migrated here:

internal class DscBreakpointCapability
{
private string[] dscResourceRootPaths = Array.Empty<string>();
private Dictionary<string, int[]> breakpointsPerFile =
new Dictionary<string, int[]>();
public async Task<BreakpointDetails[]> SetLineBreakpointsAsync(
PowerShellExecutionService executionService,
string scriptPath,
BreakpointDetails[] breakpoints)
{
List<BreakpointDetails> resultBreakpointDetails =
new List<BreakpointDetails>();
// We always get the latest array of breakpoint line numbers
// so store that for future use
if (breakpoints.Length > 0)
{
// Set the breakpoints for this scriptPath
this.breakpointsPerFile[scriptPath] =
breakpoints.Select(b => b.LineNumber).ToArray();
}
else
{
// No more breakpoints for this scriptPath, remove it
this.breakpointsPerFile.Remove(scriptPath);
}
string hashtableString =
string.Join(
", ",
this.breakpointsPerFile
.Select(file => $"@{{Path=\"{file.Key}\";Line=@({string.Join(",", file.Value)})}}"));
// Run Enable-DscDebug as a script because running it as a PSCommand
// causes an error which states that the Breakpoint parameter has not
// been passed.
var dscCommand = new PSCommand().AddScript(
hashtableString.Length > 0
? $"Enable-DscDebug -Breakpoint {hashtableString}"
: "Disable-DscDebug");
await executionService.ExecutePSCommandAsync(
dscCommand,
new PowerShellExecutionOptions(),
CancellationToken.None);
// Verify all the breakpoints and return them
foreach (var breakpoint in breakpoints)
{
breakpoint.Verified = true;
}
return breakpoints.ToArray();
}
public bool IsDscResourcePath(string scriptPath)
{
return dscResourceRootPaths.Any(
dscResourceRootPath =>
scriptPath.StartsWith(
dscResourceRootPath,
StringComparison.CurrentCultureIgnoreCase));
}
public static async Task<DscBreakpointCapability> GetDscCapabilityAsync(
ILogger logger,
IRunspaceInfo currentRunspace,
PowerShellExecutionService executionService,
CancellationToken cancellationToken)
{
// DSC support is enabled only for Windows PowerShell.
if ((currentRunspace.PowerShellVersionDetails.Version.Major >= 6) &&
(currentRunspace.RunspaceOrigin != RunspaceOrigin.DebuggedRunspace))
{
return null;
}
Func<SMA.PowerShell, CancellationToken, DscBreakpointCapability> getDscBreakpointCapabilityFunc = (pwsh, cancellationToken) =>
{
PSModuleInfo dscModule = null;
try
{
dscModule = pwsh.AddCommand("Import-Module")
.AddArgument(@"C:\Program Files\DesiredStateConfiguration\1.0.0.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psd1")
.AddParameter("PassThru")
.AddParameter("ErrorAction", "Ignore")
.InvokeAndClear<PSModuleInfo>()
.FirstOrDefault();
}
catch (RuntimeException e)
{
logger.LogException("Could not load the DSC module!", e);
}
if (dscModule == null)
{
logger.LogTrace($"Side-by-side DSC module was not found.");
return null;
}
logger.LogTrace("Side-by-side DSC module found, gathering DSC resource paths...");
// The module was loaded, add the breakpoint capability
var capability = new DscBreakpointCapability();
pwsh.AddCommand("Microsoft.PowerShell.Utility\\Write-Host")
.AddArgument("Gathering DSC resource paths, this may take a while...")
.InvokeAndClear();
Collection<string> resourcePaths = null;
try
{
// Get the list of DSC resource paths
resourcePaths = pwsh.AddCommand("Get-DscResource")
.AddCommand("Select-Object")
.AddParameter("ExpandProperty", "ParentPath")
.InvokeAndClear<string>();
}
catch (CmdletInvocationException e)
{
logger.LogException("Get-DscResource failed!", e);
}
if (resourcePaths == null)
{
logger.LogTrace($"No DSC resources found.");
return null;
}
capability.dscResourceRootPaths = resourcePaths.ToArray();
logger.LogTrace($"DSC resources found: {resourcePaths.Count}");
return capability;
};
return await executionService.ExecuteDelegateAsync<DscBreakpointCapability>(
getDscBreakpointCapabilityFunc,
nameof(getDscBreakpointCapabilityFunc),
cancellationToken);
}
}

But we need an equivalent of this still:

private void ConfigureRunspaceCapabilities(RunspaceDetails runspaceDetails)
{
DscBreakpointCapability.CheckForCapability(this.CurrentRunspace, this, this.logger);
}

@ghost ghost added the Needs: Triage Maintainer attention needed! label Mar 29, 2021
@SydneyhSmith SydneyhSmith added Area-Engine and removed Needs: Triage Maintainer attention needed! labels Mar 29, 2021
@SydneyhSmith SydneyhSmith changed the title [Revamp pipeline thread handling] DSC Support [Revamp pipeline thread handling] DSC breakpoint capability support Mar 29, 2021
@SydneyhSmith SydneyhSmith added the Issue-Bug A bug to squash. label Mar 29, 2021
@rjmholt
Copy link
Contributor

rjmholt commented Oct 11, 2021

Fixed in #1574

@rjmholt rjmholt closed this as completed Oct 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Engine Issue-Bug A bug to squash.
Projects
None yet
Development

No branches or pull requests

2 participants