Skip to content

Commit 29b4aa7

Browse files
committed
Improve detection of session changes when debugging ScriptBlocks
This change improves the logic in PushRunspaceIfSessionChanged to do a better job of detecting local and remote session changes when debugging ScriptBlocks and runspaces.
1 parent 6807e9d commit 29b4aa7

File tree

2 files changed

+65
-104
lines changed

2 files changed

+65
-104
lines changed

src/PowerShellEditorServices/Debugging/DebugService.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class DebugService
2525
#region Fields
2626

2727
private const string PsesGlobalVariableNamePrefix = "__psEditorServices_";
28-
private const string TemporaryScriptFileName = "TemporaryScript.ps1";
28+
private const string TemporaryScriptFileName = "Script Listing.ps1";
2929

3030
private PowerShellContext powerShellContext;
3131
private RemoteFileManager remoteFileManager;
@@ -1052,7 +1052,7 @@ await this.powerShellContext.ExecuteCommand<PSObject>(
10521052

10531053
this.temporaryScriptListingPath =
10541054
this.remoteFileManager.CreateTemporaryFile(
1055-
TemporaryScriptFileName,
1055+
$"[{this.powerShellContext.CurrentRunspace.SessionDetails.ComputerName}] {TemporaryScriptFileName}",
10561056
scriptListing,
10571057
this.powerShellContext.CurrentRunspace);
10581058

src/PowerShellEditorServices/Session/PowerShellContext.cs

+63-102
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ await Task.Factory.StartNew<IEnumerable<TResult>>(
520520

521521

522522
// Check if the runspace has changed
523-
this.PushRunspaceIfSessionChanged(sessionDetails);
523+
this.UpdateRunspaceDetailsIfSessionChanged(sessionDetails);
524524
}
525525

526526
// Dispose of the execution context
@@ -1290,9 +1290,12 @@ private SessionDetails GetSessionDetailsInDebugger()
12901290
return this.GetSessionDetails(
12911291
command =>
12921292
{
1293+
// Use LastOrDefault to get the last item returned. This
1294+
// is necessary because advanced prompt functions (like those
1295+
// in posh-git) may return multiple objects in the result.
12931296
return
12941297
this.ExecuteCommandInDebugger<PSObject>(command, false)
1295-
.FirstOrDefault();
1298+
.LastOrDefault();
12961299
});
12971300
}
12981301

@@ -1331,7 +1334,7 @@ private void WritePromptInDebugger(SessionDetails sessionDetails, DebuggerStopEv
13311334
string promptPrefix = string.Empty;
13321335
string promptString = sessionDetails.PromptString;
13331336

1334-
if (eventArgs != null)
1337+
if (eventArgs != null && promptString.Length > 0)
13351338
{
13361339
// Is there a prompt prefix worth keeping?
13371340
const string promptSeparator = "]: ";
@@ -1459,7 +1462,7 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e)
14591462
this.WritePromptInDebugger(sessionDetails, e);
14601463

14611464
// Push the current runspace if the session has changed
1462-
this.PushRunspaceIfSessionChanged(sessionDetails);
1465+
this.UpdateRunspaceDetailsIfSessionChanged(sessionDetails, isDebuggerStop: true);
14631466

14641467
// Raise the event for the debugger service
14651468
this.DebuggerStop?.Invoke(sender, e);
@@ -1586,6 +1589,13 @@ private void PushRunspace(RunspaceDetails newRunspaceDetails)
15861589

15871590
RunspaceDetails previousRunspace = this.CurrentRunspace;
15881591

1592+
if (newRunspaceDetails.Context == RunspaceContext.DebuggedRunspace)
1593+
{
1594+
this.WriteOutput(
1595+
$"Entering debugged runspace on {newRunspaceDetails.Location.ToString().ToLower()} machine {newRunspaceDetails.SessionDetails.ComputerName}",
1596+
true);
1597+
}
1598+
15891599
// Switch out event handlers if necessary
15901600
if (CheckIfRunspaceNeedsEventHandlers(newRunspaceDetails))
15911601
{
@@ -1604,119 +1614,63 @@ private void PushRunspace(RunspaceDetails newRunspaceDetails)
16041614
this.CurrentRunspace));
16051615
}
16061616

1607-
private void PushRunspaceIfSessionChanged(SessionDetails sessionDetails, bool isDebuggerStop = false)
1617+
private void UpdateRunspaceDetailsIfSessionChanged(SessionDetails sessionDetails, bool isDebuggerStop = false)
16081618
{
1609-
bool needsPop = false;
1610-
RunspaceContext? newRunspaceContext = null;
1619+
RunspaceDetails newRunspaceDetails = null;
16111620

1612-
if (this.CurrentRunspace.Location == RunspaceLocation.Local &&
1613-
!string.Equals(
1614-
this.CurrentRunspace.SessionDetails.ComputerName,
1615-
sessionDetails.ComputerName,
1616-
StringComparison.CurrentCultureIgnoreCase))
1621+
// If we've exited an entered process or debugged runspace, pop what we've
1622+
// got before we evaluate where we're at
1623+
if (
1624+
(this.CurrentRunspace.Context == RunspaceContext.DebuggedRunspace &&
1625+
this.CurrentRunspace.SessionDetails.InstanceId != sessionDetails.InstanceId) ||
1626+
(this.CurrentRunspace.Context == RunspaceContext.EnteredProcess &&
1627+
this.CurrentRunspace.SessionDetails.ProcessId != sessionDetails.ProcessId))
16171628
{
1618-
// This is a special case scenario when debugging remote ScriptBlocks.
1619-
// Normally entering a remote session will cause PushRunspace to be
1620-
// called. If we suddenly find ourselves in a different machine without
1621-
// a prior PushRunspace call it means that the debugger has stepped into
1622-
// the execution of a remote ScriptBlock.
1623-
this.PushRunspace(
1624-
RunspaceDetails.CreateFromDebugger(
1625-
this.CurrentRunspace,
1626-
RunspaceLocation.Remote,
1627-
RunspaceContext.DebuggedRunspace,
1628-
sessionDetails));
1629+
this.PopRunspace();
16291630
}
1630-
else if (
1631-
this.CurrentRunspace.Location == RunspaceLocation.Remote &&
1632-
!string.Equals(
1633-
this.CurrentRunspace.SessionDetails.ComputerName,
1634-
sessionDetails.ComputerName,
1635-
StringComparison.CurrentCultureIgnoreCase))
1636-
{
1637-
// This is the "exit" case of the previous case. If we hit a breakpoint
1638-
// and we're suddenly in a different runspace, react accordingly. This can
1639-
// happen if we are now debugging the same ScriptBlock on another machine or
1640-
// if we are now back in the original local runspace.
16411631

1642-
// First, pop the remote runspace in either case
1643-
this.PopRunspace();
1632+
// Are we in a new session that the PushRunspace command won't
1633+
// notify us about?
1634+
//
1635+
// Possible cases:
1636+
// - Debugged runspace in a local or remote session
1637+
// - Entered process in a remote session
1638+
//
1639+
// We don't need additional logic to check for the cases that
1640+
// PowerShell would have notified us about because the CurrentRunspace
1641+
// will already be updated by PowerShell by the time we reach
1642+
// these checks.
16441643

1645-
// If the new computer name doesn't match that of the current machine, we're
1646-
// on another remote computer
1647-
if (!string.Equals(
1644+
if (this.CurrentRunspace.SessionDetails.InstanceId != sessionDetails.InstanceId && isDebuggerStop)
1645+
{
1646+
// Are we on a local or remote computer?
1647+
bool differentComputer =
1648+
!string.Equals(
16481649
sessionDetails.ComputerName,
16491650
this.initialRunspace.SessionDetails.ComputerName,
1650-
StringComparison.CurrentCultureIgnoreCase))
1651-
{
1652-
// Push the runspace for another remote session
1653-
this.PushRunspace(
1654-
RunspaceDetails.CreateFromDebugger(
1655-
this.CurrentRunspace,
1656-
RunspaceLocation.Remote,
1657-
RunspaceContext.DebuggedRunspace,
1658-
sessionDetails));
1659-
}
1660-
}
1661-
1662-
// This logic checks for any session change that is never communicated
1663-
// via a call to PushRunspace:
1664-
// - Remote session entering a different process
1665-
// - Local or remote session debugging a runspace in the current process
1666-
// - Local or remote session debugging a runspace in the current process
1651+
StringComparison.CurrentCultureIgnoreCase);
16671652

1668-
if (this.CurrentRunspace.Location == RunspaceLocation.Local &&
1669-
this.CurrentRunspace.SessionDetails.InstanceId != sessionDetails.InstanceId)
1670-
{
1671-
// Did we start or stop debugging a runspace?
1672-
if (this.CurrentRunspace.Context != RunspaceContext.DebuggedRunspace)
1673-
{
1674-
newRunspaceContext = RunspaceContext.DebuggedRunspace;
1675-
}
1676-
else
1677-
{
1678-
needsPop = true;
1679-
}
1680-
}
1681-
else if (this.CurrentRunspace.Location == RunspaceLocation.Remote)
1682-
{
1683-
if (this.CurrentRunspace.SessionDetails.ProcessId != sessionDetails.ProcessId)
1684-
{
1685-
// Did we attach to or detach from a process?
1686-
if (this.CurrentRunspace.Context != RunspaceContext.EnteredProcess)
1687-
{
1688-
newRunspaceContext = RunspaceContext.EnteredProcess;
1689-
}
1690-
else
1691-
{
1692-
needsPop = true;
1693-
}
1694-
}
1695-
else if (this.CurrentRunspace.SessionDetails.InstanceId != sessionDetails.InstanceId)
1696-
{
1697-
// Debugging a different runspace
1698-
if (this.CurrentRunspace.Context != RunspaceContext.DebuggedRunspace)
1699-
{
1700-
newRunspaceContext = RunspaceContext.DebuggedRunspace;
1701-
}
1702-
else
1703-
{
1704-
needsPop = true;
1705-
}
1706-
}
1653+
// We started debugging a runspace
1654+
newRunspaceDetails =
1655+
RunspaceDetails.CreateFromDebugger(
1656+
this.CurrentRunspace,
1657+
differentComputer ? RunspaceLocation.Remote : RunspaceLocation.Local,
1658+
RunspaceContext.DebuggedRunspace,
1659+
sessionDetails);
17071660
}
1708-
1709-
if (newRunspaceContext.HasValue)
1661+
else if (this.CurrentRunspace.SessionDetails.ProcessId != sessionDetails.ProcessId)
17101662
{
1711-
this.PushRunspace(
1663+
// We entered a different PowerShell host process
1664+
newRunspaceDetails =
17121665
RunspaceDetails.CreateFromContext(
17131666
this.CurrentRunspace,
1714-
newRunspaceContext.Value,
1715-
sessionDetails));
1667+
RunspaceContext.EnteredProcess,
1668+
sessionDetails);
17161669
}
1717-
else if (needsPop)
1670+
1671+
if (newRunspaceDetails != null)
17181672
{
1719-
this.PopRunspace();
1673+
this.PushRunspace(newRunspaceDetails);
17201674
}
17211675
}
17221676

@@ -1733,6 +1687,13 @@ private void PopRunspace()
17331687
LogLevel.Verbose,
17341688
$"Popping {previousRunspace.Location} ({previousRunspace.Context}), new runspace is {this.CurrentRunspace.Location} ({this.CurrentRunspace.Context}), connection: {this.CurrentRunspace.ConnectionString}");
17351689

1690+
if (previousRunspace.Context == RunspaceContext.DebuggedRunspace)
1691+
{
1692+
this.WriteOutput(
1693+
$"Leaving debugged runspace on {previousRunspace.Location.ToString().ToLower()} machine {previousRunspace.SessionDetails.ComputerName}",
1694+
true);
1695+
}
1696+
17361697
// Switch out event handlers if necessary
17371698
if (CheckIfRunspaceNeedsEventHandlers(previousRunspace))
17381699
{

0 commit comments

Comments
 (0)