Skip to content

Commit

Permalink
move to runspaceId
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerLeonhardt committed Jan 16, 2020
1 parent dfc6fe0 commit ff35f29
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,30 @@ internal class BreakpointService
{
private readonly ILogger<BreakpointService> _logger;
private readonly PowerShellContextService _powerShellContextService;
private readonly DebugStateService _debugStateService;

// TODO: This needs to be managed per nested session
internal readonly Dictionary<string, HashSet<Breakpoint>> BreakpointsPerFile =
new Dictionary<string, HashSet<Breakpoint>>();

internal readonly HashSet<Breakpoint> CommandBreakpoints =
new HashSet<Breakpoint>();

public BreakpointService(
ILoggerFactory factory,
PowerShellContextService powerShellContextService)
PowerShellContextService powerShellContextService,
DebugStateService debugStateService)
{
_logger = factory.CreateLogger<BreakpointService>();
_powerShellContextService = powerShellContextService;
_debugStateService = debugStateService;
}

public List<Breakpoint> GetBreakpoints()
{
return BreakpointApiUtils.GetBreakpoints(
_powerShellContextService.CurrentRunspace.Runspace.Debugger,
_debugStateService.RunspaceId);
}

public async Task<IEnumerable<BreakpointDetails>> SetBreakpointsAsync(string escapedScriptPath, IEnumerable<BreakpointDetails> breakpoints)
Expand All @@ -39,7 +56,7 @@ public async Task<IEnumerable<BreakpointDetails>> SetBreakpointsAsync(string esc
{
try
{
BreakpointApiUtils.SetBreakpoint(_powerShellContextService.CurrentRunspace.Runspace.Debugger, breakpointDetails);
BreakpointApiUtils.SetBreakpoint(_powerShellContextService.CurrentRunspace.Runspace.Debugger, breakpointDetails, _debugStateService.RunspaceId);

}
catch(InvalidOperationException e)
Expand Down Expand Up @@ -129,7 +146,7 @@ public async Task<IEnumerable<CommandBreakpointDetails>> SetCommandBreakpoints(I
{
try
{
BreakpointApiUtils.SetBreakpoint(_powerShellContextService.CurrentRunspace.Runspace.Debugger, commandBreakpointDetails);
BreakpointApiUtils.SetBreakpoint(_powerShellContextService.CurrentRunspace.Runspace.Debugger, commandBreakpointDetails, _debugStateService.RunspaceId);
}
catch(InvalidOperationException e)
{
Expand Down Expand Up @@ -195,18 +212,23 @@ public async Task<IEnumerable<CommandBreakpointDetails>> SetCommandBreakpoints(I
/// <summary>
/// Clears all breakpoints in the current session.
/// </summary>
public async Task RemoveAllBreakpointsAsync()
public async Task RemoveAllBreakpointsAsync(string scriptPath = null)
{
try
{
if (VersionUtils.IsPS7OrGreater)
{
foreach (Breakpoint breakpoint in BreakpointApiUtils.GetBreakpoints(
_powerShellContextService.CurrentRunspace.Runspace.Debugger))
{
BreakpointApiUtils.RemoveBreakpoint(
_powerShellContextService.CurrentRunspace.Runspace.Debugger,
breakpoint);
_debugStateService.RunspaceId))
{
if (scriptPath == null || scriptPath == breakpoint.Script)
{
BreakpointApiUtils.RemoveBreakpoint(
_powerShellContextService.CurrentRunspace.Runspace.Debugger,
breakpoint,
_debugStateService.RunspaceId);
}
}

return;
Expand Down Expand Up @@ -234,7 +256,21 @@ public async Task RemoveBreakpointsAsync(IEnumerable<Breakpoint> breakpoints)
{
BreakpointApiUtils.RemoveBreakpoint(
_powerShellContextService.CurrentRunspace.Runspace.Debugger,
breakpoint);
breakpoint,
_debugStateService.RunspaceId);

switch (breakpoint)
{
case CommandBreakpoint commandBreakpoint:
CommandBreakpoints.Remove(commandBreakpoint);
break;
case LineBreakpoint lineBreakpoint:
if (BreakpointsPerFile.TryGetValue(lineBreakpoint.Script, out HashSet<Breakpoint> bps))
{
bps.Remove(lineBreakpoint);
}
break;
}
}

return;
Expand Down
33 changes: 15 additions & 18 deletions src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
using Microsoft.PowerShell.EditorServices.Services.PowerShellContext;
using Microsoft.PowerShell.EditorServices.Services.DebugAdapter;
using System.Collections.Concurrent;

namespace Microsoft.PowerShell.EditorServices.Services
{
Expand All @@ -38,11 +39,6 @@ internal class DebugService
private readonly BreakpointService _breakpointService;
private RemoteFileManagerService remoteFileManager;

// TODO: This needs to be managed per nested session
// TODO: Move to BreakpointService
private readonly Dictionary<string, List<Breakpoint>> breakpointsPerFile =
new Dictionary<string, List<Breakpoint>>();

private int nextVariableId;
private string temporaryScriptListingPath;
private List<VariableDetailsBase> variables;
Expand Down Expand Up @@ -192,7 +188,7 @@ internal class DebugService
await this.ClearBreakpointsInFileAsync(scriptFile).ConfigureAwait(false);
}

return await _breakpointService.SetBreakpointsAsync(escapedScriptPath, breakpoints).ConfigureAwait(false);
return (await _breakpointService.SetBreakpointsAsync(escapedScriptPath, breakpoints).ConfigureAwait(false)).ToArray();
}

return await dscBreakpoints.SetLineBreakpointsAsync(
Expand All @@ -216,7 +212,7 @@ internal class DebugService
if (clearExisting)
{
// Flatten dictionary values into one list and remove them all.
await _breakpointService.RemoveBreakpointsAsync(this.breakpointsPerFile.Values.SelectMany( i => i ).Where( i => i is CommandBreakpoint)).ConfigureAwait(false);
await _breakpointService.RemoveBreakpointsAsync(_breakpointService.GetBreakpoints().Where( i => i is CommandBreakpoint)).ConfigureAwait(false);
}

if (breakpoints.Length > 0)
Expand Down Expand Up @@ -672,16 +668,17 @@ public VariableScope[] GetVariableScopes(int stackFrameId)
private async Task ClearBreakpointsInFileAsync(ScriptFile scriptFile)
{
// Get the list of breakpoints for this file
if (this.breakpointsPerFile.TryGetValue(scriptFile.Id, out List<Breakpoint> breakpoints))
{
if (breakpoints.Count > 0)
{
await _breakpointService.RemoveBreakpointsAsync(breakpoints).ConfigureAwait(false);
// if (_breakpointService.BreakpointsPerFile.TryGetValue(scriptFile.Id, out HashSet<Breakpoint> breakpoints))
// {
// if (breakpoints.Count > 0)
// {
await _breakpointService.RemoveBreakpointsAsync(_breakpointService.GetBreakpoints()
.Where(bp => bp is LineBreakpoint lbp && string.Equals(lbp.Script, scriptFile.FilePath))).ConfigureAwait(false);

// Clear the existing breakpoints list for the file
breakpoints.Clear();
}
}
// breakpoints.Clear();
// }
// }
}

private async Task FetchStackFramesAndVariablesAsync(string scriptNameOverride)
Expand Down Expand Up @@ -1035,10 +1032,10 @@ private void OnBreakpointUpdated(object sender, BreakpointUpdatedEventArgs e)
string normalizedScriptName = scriptPath.ToLower();

// Get the list of breakpoints for this file
if (!this.breakpointsPerFile.TryGetValue(normalizedScriptName, out List<Breakpoint> breakpoints))
if (!_breakpointService.BreakpointsPerFile.TryGetValue(normalizedScriptName, out HashSet<Breakpoint> breakpoints))
{
breakpoints = new List<Breakpoint>();
this.breakpointsPerFile.Add(
breakpoints = new HashSet<Breakpoint>();
_breakpointService.BreakpointsPerFile.Add(
normalizedScriptName,
breakpoints);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ internal class DebugStateService

internal bool IsRemoteAttach { get; set; }

internal int? RunspaceId { get; set; }

internal bool IsAttachSession { get; set; }

internal bool WaitingForAttach { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ internal static class BreakpointApiUtils

private const string s_psesGlobalVariableNamePrefix = "__psEditorServices_";

private static readonly Lazy<Func<Debugger, string, int, int, ScriptBlock, LineBreakpoint>> s_setLineBreakpointLazy;
private static readonly Lazy<Func<Debugger, string, int, int, ScriptBlock, int?, LineBreakpoint>> s_setLineBreakpointLazy;

private static readonly Lazy<Func<Debugger, string, ScriptBlock, string, CommandBreakpoint>> s_setCommandBreakpointLazy;
private static readonly Lazy<Func<Debugger, string, ScriptBlock, string, int?, CommandBreakpoint>> s_setCommandBreakpointLazy;

private static readonly Lazy<Func<Debugger, List<Breakpoint>>> s_getBreakpointsLazy;
private static readonly Lazy<Func<Debugger, int?, List<Breakpoint>>> s_getBreakpointsLazy;

private static readonly Lazy<Func<Debugger, Breakpoint, bool>> s_removeBreakpointLazy;
private static readonly Lazy<Action<Debugger, IEnumerable<Breakpoint>, int?>> s_setBreakpointsLazy;

private static readonly Lazy<Func<Debugger, Breakpoint, int?, bool>> s_removeBreakpointLazy;

private static int breakpointHitCounter;

Expand All @@ -46,42 +48,52 @@ static BreakpointApiUtils()
return;
}

s_setLineBreakpointLazy = new Lazy<Func<Debugger, string, int, int, ScriptBlock, LineBreakpoint>>(() =>
s_setLineBreakpointLazy = new Lazy<Func<Debugger, string, int, int, ScriptBlock, int?, LineBreakpoint>>(() =>
{
MethodInfo setLineBreakpointMethod = typeof(Debugger).GetMethod("SetLineBreakpoint", BindingFlags.Public | BindingFlags.Instance);
return (Func<Debugger, string, int, int, ScriptBlock, LineBreakpoint>)Delegate.CreateDelegate(
typeof(Func<Debugger, string, int, int, ScriptBlock, LineBreakpoint>),
return (Func<Debugger, string, int, int, ScriptBlock, int?, LineBreakpoint>)Delegate.CreateDelegate(
typeof(Func<Debugger, string, int, int, ScriptBlock, int?, LineBreakpoint>),
firstArgument: null,
setLineBreakpointMethod);
});

s_setCommandBreakpointLazy = new Lazy<Func<Debugger, string, ScriptBlock, string, CommandBreakpoint>>(() =>
s_setCommandBreakpointLazy = new Lazy<Func<Debugger, string, ScriptBlock, string, int?, CommandBreakpoint>>(() =>
{
MethodInfo setCommandBreakpointMethod = typeof(Debugger).GetMethod("SetCommandBreakpoint", BindingFlags.Public | BindingFlags.Instance);
return (Func<Debugger, string, ScriptBlock, string, CommandBreakpoint>)Delegate.CreateDelegate(
typeof(Func<Debugger, string, ScriptBlock, string, CommandBreakpoint>),
return (Func<Debugger, string, ScriptBlock, string, int?, CommandBreakpoint>)Delegate.CreateDelegate(
typeof(Func<Debugger, string, ScriptBlock, string, int?, CommandBreakpoint>),
firstArgument: null,
setCommandBreakpointMethod);
});

s_getBreakpointsLazy = new Lazy<Func<Debugger, List<Breakpoint>>>(() =>
s_getBreakpointsLazy = new Lazy<Func<Debugger, int?, List<Breakpoint>>>(() =>
{
MethodInfo removeBreakpointMethod = typeof(Debugger).GetMethod("GetBreakpoints", BindingFlags.Public | BindingFlags.Instance);
return (Func<Debugger, List<Breakpoint>>)Delegate.CreateDelegate(
typeof(Func<Debugger, List<Breakpoint>>),
return (Func<Debugger, int?, List<Breakpoint>>)Delegate.CreateDelegate(
typeof(Func<Debugger, int?, List<Breakpoint>>),
firstArgument: null,
removeBreakpointMethod);
});

s_setBreakpointsLazy = new Lazy<Action<Debugger, IEnumerable<Breakpoint>, int?>>(() =>
{
MethodInfo removeBreakpointMethod = typeof(Debugger).GetMethod("SetBreakpoints", BindingFlags.Public | BindingFlags.Instance);
return (Action<Debugger, IEnumerable<Breakpoint>, int?>)Action.CreateDelegate(
typeof(Action<Debugger, IEnumerable<Breakpoint>, int?>),
firstArgument: null,
removeBreakpointMethod);
});

s_removeBreakpointLazy = new Lazy<Func<Debugger, Breakpoint, bool>>(() =>
s_removeBreakpointLazy = new Lazy<Func<Debugger, Breakpoint, int?, bool>>(() =>
{
MethodInfo removeBreakpointMethod = typeof(Debugger).GetMethod("RemoveBreakpoint", BindingFlags.Public | BindingFlags.Instance);
return (Func<Debugger, Breakpoint, bool>)Delegate.CreateDelegate(
typeof(Func<Debugger, Breakpoint, bool>),
return (Func<Debugger, Breakpoint, int?, bool>)Delegate.CreateDelegate(
typeof(Func<Debugger, Breakpoint, int?, bool>),
firstArgument: null,
removeBreakpointMethod);
});
Expand All @@ -91,19 +103,21 @@ static BreakpointApiUtils()

#region Public Static Properties

private static Func<Debugger, string, int, int, ScriptBlock, LineBreakpoint> SetLineBreakpointDelegate => s_setLineBreakpointLazy.Value;
private static Func<Debugger, string, int, int, ScriptBlock, int?, LineBreakpoint> SetLineBreakpointDelegate => s_setLineBreakpointLazy.Value;

private static Func<Debugger, string, ScriptBlock, string, int?, CommandBreakpoint> SetCommandBreakpointDelegate => s_setCommandBreakpointLazy.Value;

private static Func<Debugger, string, ScriptBlock, string, CommandBreakpoint> SetCommandBreakpointDelegate => s_setCommandBreakpointLazy.Value;
private static Func<Debugger, int?, List<Breakpoint>> GetBreakpointsDelegate => s_getBreakpointsLazy.Value;

private static Func<Debugger, List<Breakpoint>> GetBreakpointsDelegate => s_getBreakpointsLazy.Value;
private static Action<Debugger, IEnumerable<Breakpoint>, int?> SetBreakpointsDelegate => s_setBreakpointsLazy.Value;

private static Func<Debugger, Breakpoint, bool> RemoveBreakpointDelegate => s_removeBreakpointLazy.Value;
private static Func<Debugger, Breakpoint, int?, bool> RemoveBreakpointDelegate => s_removeBreakpointLazy.Value;

#endregion

#region Public Static Methods

public static Breakpoint SetBreakpoint(Debugger debugger, BreakpointDetailsBase breakpoint)
public static Breakpoint SetBreakpoint(Debugger debugger, BreakpointDetailsBase breakpoint, int? runspaceId = null)
{
ScriptBlock actionScriptBlock = null;
string logMessage = breakpoint is BreakpointDetails bd ? bd.LogMessage : null;
Expand All @@ -118,24 +132,24 @@ public static Breakpoint SetBreakpoint(Debugger debugger, BreakpointDetailsBase
switch (breakpoint)
{
case BreakpointDetails lineBreakpoint:
return SetLineBreakpointDelegate(debugger, lineBreakpoint.Source, lineBreakpoint.LineNumber, lineBreakpoint.ColumnNumber ?? 0, actionScriptBlock);
return SetLineBreakpointDelegate(debugger, lineBreakpoint.Source, lineBreakpoint.LineNumber, lineBreakpoint.ColumnNumber ?? 0, actionScriptBlock, runspaceId);

case CommandBreakpointDetails commandBreakpoint:
return SetCommandBreakpointDelegate(debugger, commandBreakpoint.Name, null, null);
return SetCommandBreakpointDelegate(debugger, commandBreakpoint.Name, null, null, runspaceId);

default:
throw new NotImplementedException("Other breakpoints not supported yet");
}
}

public static List<Breakpoint> GetBreakpoints(Debugger debugger)
public static List<Breakpoint> GetBreakpoints(Debugger debugger, int? runspaceId = null)
{
return GetBreakpointsDelegate(debugger);
return GetBreakpointsDelegate(debugger, runspaceId);
}

public static bool RemoveBreakpoint(Debugger debugger, Breakpoint breakpoint)
public static bool RemoveBreakpoint(Debugger debugger, Breakpoint breakpoint, int? runspaceId = null)
{
return RemoveBreakpointDelegate(debugger, breakpoint);
return RemoveBreakpointDelegate(debugger, breakpoint, runspaceId);
}

public static ScriptBlock GetBreakpointActionScriptBlock(string condition, string hitCondition, string logMessage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,6 @@ public async Task<Unit> Handle(PsesAttachRequestArguments request, CancellationT
throw new RpcErrorException(0, "A positive integer must be specified for the processId field.");
}

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

// Execute the Debug-Runspace command but don't await it because it
// will block the debug adapter initialization process. The
// InitializedEvent will be sent as soon as the RunspaceChanged
Expand All @@ -336,6 +333,12 @@ public async Task<Unit> Handle(PsesAttachRequestArguments request, CancellationT
string debugRunspaceCmd;
if (request.RunspaceName != null)
{
var ids = await _powerShellContextService.ExecuteScriptStringAsync($"Get-Runspace -Name {request.RunspaceName} | % Id");
foreach (var id in ids)
{
_debugStateService.RunspaceId = (int?) id;
break;
}
debugRunspaceCmd = $"\nDebug-Runspace -Name '{request.RunspaceName}'";
}
else if (request.RunspaceId != null)
Expand All @@ -348,13 +351,20 @@ public async Task<Unit> Handle(PsesAttachRequestArguments request, CancellationT
throw new RpcErrorException(0, "A positive integer must be specified for the RunspaceId field.");
}

_debugStateService.RunspaceId = runspaceId;

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

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

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

_debugStateService.WaitingForAttach = true;
Task nonAwaitedTask = _powerShellContextService
.ExecuteScriptStringAsync(debugRunspaceCmd)
Expand Down

0 comments on commit ff35f29

Please sign in to comment.