Skip to content

Commit 5dd00e9

Browse files
committed
Fix more debugging issues
1 parent c736d90 commit 5dd00e9

File tree

4 files changed

+119
-70
lines changed

4 files changed

+119
-70
lines changed

src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ public class DebugAdapter : DebugAdapterBase
2525
private EditorSession editorSession;
2626

2727
private bool noDebug;
28+
private bool isRemoteAttach;
2829
private bool isAttachSession;
2930
private bool waitingForAttach;
3031
private string scriptToLaunch;
3132
private bool ownsEditorSession;
33+
private bool executionCompleted;
3234
private string arguments;
35+
private RequestContext<object> disconnectRequestContext = null;
3336

3437
public DebugAdapter(HostDetails hostDetails, ProfilePaths profilePaths)
3538
: this(hostDetails, profilePaths, new StdioServerChannel(), null)
@@ -90,17 +93,57 @@ protected Task LaunchScript(RequestContext<object> requestContext)
9093
{
9194
return editorSession.PowerShellContext
9295
.ExecuteScriptWithArgs(this.scriptToLaunch, this.arguments)
93-
.ContinueWith(
94-
async (t) => {
95-
Logger.Write(LogLevel.Verbose, "Execution completed, flushing output then terminating...");
96+
.ContinueWith(this.OnExecutionCompleted);
97+
}
98+
99+
private async Task OnExecutionCompleted(Task executeTask)
100+
{
101+
Logger.Write(LogLevel.Verbose, "Execution completed, terminating...");
102+
103+
this.executionCompleted = true;
104+
105+
if (this.isAttachSession)
106+
{
107+
// Ensure the read loop is stopped
108+
this.editorSession.ConsoleService.CancelReadLoop();
109+
110+
// Pop the sessions
111+
if (this.editorSession.PowerShellContext.CurrentRunspace.Context == RunspaceContext.EnteredProcess)
112+
{
113+
try
114+
{
115+
await this.editorSession.PowerShellContext.ExecuteScriptString("Exit-PSHostProcess");
116+
117+
if (this.isRemoteAttach &&
118+
this.editorSession.PowerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote)
119+
{
120+
await this.editorSession.PowerShellContext.ExecuteScriptString("Exit-PSSession");
121+
}
122+
}
123+
catch (Exception e)
124+
{
125+
Logger.WriteException("Caught exception while popping attached process after debugging", e);
126+
}
127+
}
96128

97-
await this.SendEvent(
98-
TerminatedEvent.Type,
99-
new TerminatedEvent());
129+
if (!this.ownsEditorSession)
130+
{
131+
this.editorSession.ConsoleService.StartReadLoop();
132+
}
133+
}
100134

101-
// Stop the server
102-
await this.Stop();
103-
});
135+
if (this.disconnectRequestContext != null)
136+
{
137+
// Respond to the disconnect request and stop the server
138+
await this.disconnectRequestContext.SendResult(null);
139+
await this.Stop();
140+
}
141+
else
142+
{
143+
await this.SendEvent(
144+
TerminatedEvent.Type,
145+
new TerminatedEvent());
146+
}
104147
}
105148

106149
protected override void Shutdown()
@@ -214,9 +257,9 @@ protected async Task HandleLaunchRequest(
214257
// machine if necessary
215258
if (this.editorSession.PowerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote)
216259
{
217-
this.scriptPathToLaunch =
260+
this.scriptToLaunch =
218261
this.editorSession.RemoteFileManager.GetMappedPath(
219-
this.scriptPathToLaunch,
262+
this.scriptToLaunch,
220263
this.editorSession.PowerShellContext.CurrentRunspace);
221264
}
222265

@@ -273,6 +316,13 @@ await requestContext.SendError(
273316

274317
return;
275318
}
319+
else if (this.editorSession.PowerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote)
320+
{
321+
await requestContext.SendError(
322+
$"Cannot attach to a process in a remote session when already in a remote session.");
323+
324+
return;
325+
}
276326

277327
await this.editorSession.PowerShellContext.ExecuteScriptString(
278328
$"Enter-PSSession -ComputerName \"{attachParams.ComputerName}\"",
@@ -285,6 +335,8 @@ await requestContext.SendError(
285335

286336
return;
287337
}
338+
339+
this.isRemoteAttach = true;
288340
}
289341

290342
if (int.TryParse(attachParams.ProcessId, out int processId) && (processId > 0))
@@ -319,8 +371,9 @@ await requestContext.SendError(
319371
int runspaceId = attachParams.RunspaceId > 0 ? attachParams.RunspaceId : 1;
320372
this.waitingForAttach = true;
321373
Task nonAwaitedTask =
322-
this.editorSession.PowerShellContext.ExecuteScriptString(
323-
$"\nDebug-Runspace -Id {runspaceId}");
374+
this.editorSession.PowerShellContext
375+
.ExecuteScriptString($"\nDebug-Runspace -Id {runspaceId}")
376+
.ContinueWith(this.OnExecutionCompleted);
324377
}
325378
else
326379
{
@@ -341,40 +394,13 @@ protected async Task HandleDisconnectRequest(
341394
object disconnectParams,
342395
RequestContext<object> requestContext)
343396
{
344-
EventHandler<SessionStateChangedEventArgs> handler = null;
345-
346-
// Define a handler that encapsulates the RequestContext so
347-
// that it can be completed once the executed script is
348-
// shutting down.
349-
handler =
350-
async (o, e) =>
351-
{
352-
if (e.NewSessionState == PowerShellContextState.Ready)
353-
{
354-
await requestContext.SendResult(null);
355-
this.editorSession.PowerShellContext.SessionStateChanged -= handler;
356-
357-
if (this.isAttachSession)
358-
{
359-
// Start up the read loop again if this is a shared session
360-
if (!this.ownsEditorSession)
361-
{
362-
this.editorSession.ConsoleService.StartReadLoop();
363-
}
364-
365-
await this.Stop();
366-
}
367-
}
368-
};
369-
370397
// In some rare cases, the EditorSession will already be disposed
371398
// so we shouldn't try to abort because PowerShellContext will be null
372399
if (this.editorSession != null && this.editorSession.PowerShellContext != null)
373400
{
374-
if (this.editorSession.PowerShellContext.SessionState == PowerShellContextState.Running ||
375-
this.editorSession.PowerShellContext.IsDebuggerStopped)
401+
if (this.executionCompleted == false)
376402
{
377-
this.editorSession.PowerShellContext.SessionStateChanged += handler;
403+
this.disconnectRequestContext = requestContext;
378404
this.editorSession.PowerShellContext.AbortExecution();
379405
}
380406
else
@@ -772,7 +798,7 @@ async void powerShellContext_RunspaceChanged(object sender, RunspaceChangedEvent
772798
await this.SendEvent(InitializedEvent.Type, null);
773799
}
774800
else if (
775-
e.ChangeAction == RunspaceChangeAction.Exit &&
801+
e.ChangeAction == RunspaceChangeAction.Exit &&
776802
(this.editorSession == null ||
777803
this.editorSession.PowerShellContext.IsDebuggerStopped))
778804
{

src/PowerShellEditorServices/Console/ConsoleService.cs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,27 @@ public ConsoleService(
103103
/// </summary>
104104
public void StartReadLoop()
105105
{
106-
this.readLineCancellationToken = new CancellationTokenSource();
107-
108-
var terminalThreadTask =
109-
Task.Factory.StartNew(
110-
async () =>
111-
{
112-
// Set the thread's name to help with debugging
113-
Thread.CurrentThread.Name = "Terminal Input Loop Thread";
114-
115-
await this.StartReplLoop(this.readLineCancellationToken.Token);
116-
},
117-
CancellationToken.None,
118-
TaskCreationOptions.LongRunning,
119-
TaskScheduler.Default);
106+
if (this.readLineCancellationToken == null)
107+
{
108+
this.readLineCancellationToken = new CancellationTokenSource();
109+
110+
var terminalThreadTask =
111+
Task.Factory.StartNew(
112+
async () =>
113+
{
114+
// Set the thread's name to help with debugging
115+
Thread.CurrentThread.Name = "Terminal Input Loop Thread";
116+
117+
await this.StartReplLoop(this.readLineCancellationToken.Token);
118+
},
119+
CancellationToken.None,
120+
TaskCreationOptions.LongRunning,
121+
TaskScheduler.Default);
122+
}
123+
else
124+
{
125+
Logger.Write(LogLevel.Verbose, "StartReadLoop called while read loop is already running");
126+
}
120127
}
121128

122129
/// <summary>

src/PowerShellEditorServices/Session/PowerShellContext.cs

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ public bool IsDebuggerStopped
6666
{
6767
get
6868
{
69-
return this.debuggerStoppedTask != null;
69+
return
70+
this.debuggerStoppedTask != null &&
71+
this.CurrentRunspace.Runspace.RunspaceAvailability != RunspaceAvailability.Available;
7072
}
7173
}
7274

@@ -504,8 +506,16 @@ await Task.Factory.StartNew<IEnumerable<TResult>>(
504506
SessionDetails sessionDetails = null;
505507

506508
// Get the SessionDetails and then write the prompt
507-
if (runspaceHandle != null)
509+
if (this.CurrentRunspace.Runspace.RunspaceAvailability == RunspaceAvailability.Available)
508510
{
511+
// This state can happen if the user types a command that causes the
512+
// debugger to exit before we reach this point. No RunspaceHandle
513+
// will exist already so we need to create one and then use it
514+
if (runspaceHandle == null)
515+
{
516+
runspaceHandle = await this.GetRunspaceHandle();
517+
}
518+
509519
sessionDetails = this.GetSessionDetailsInRunspace(runspaceHandle.Runspace);
510520
}
511521
else if (this.IsDebuggerStopped)
@@ -768,14 +778,16 @@ public void AbortExecution()
768778
{
769779
Logger.Write(LogLevel.Verbose, "Execution abort requested...");
770780

781+
// Clean up the debugger
771782
if (this.IsDebuggerStopped)
772783
{
773784
this.ResumeDebugger(DebuggerResumeAction.Stop);
785+
this.debuggerStoppedTask = null;
786+
this.pipelineExecutionTask = null;
774787
}
775-
//else
776-
//{
777-
this.powerShell.BeginStop(null, null);
778-
//}
788+
789+
// Stop the running pipeline
790+
this.powerShell.BeginStop(null, null);
779791

780792
this.SessionState = PowerShellContextState.Aborting;
781793
}
@@ -1448,7 +1460,7 @@ private void HandleRunspaceStateChanged(object sender, RunspaceStateEventArgs ar
14481460
// NOTE: This event is 'internal' because the DebugService provides
14491461
// the publicly consumable event.
14501462
internal event EventHandler<DebuggerStopEventArgs> DebuggerStop;
1451-
internal event EventHandler<DebuggerResumeAction> DebuggerResumed;
1463+
public event EventHandler<DebuggerResumeAction> DebuggerResumed;
14521464

14531465
private void OnDebuggerStop(object sender, DebuggerStopEventArgs e)
14541466
{
@@ -1462,6 +1474,10 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e)
14621474
this.pipelineThreadId = Thread.CurrentThread.ManagedThreadId;
14631475
this.pipelineExecutionTask = new TaskCompletionSource<IPipelineExecutionRequest>();
14641476

1477+
// Hold on to local task vars so that the fields can be cleared independently
1478+
Task<DebuggerResumeAction> localDebuggerStoppedTask = this.debuggerStoppedTask.Task;
1479+
Task<IPipelineExecutionRequest> localPipelineExecutionTask = this.pipelineExecutionTask.Task;
1480+
14651481
// Update the session state
14661482
this.OnSessionStateChanged(
14671483
this,
@@ -1486,15 +1502,15 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e)
14861502
{
14871503
int taskIndex =
14881504
Task.WaitAny(
1489-
this.debuggerStoppedTask.Task,
1490-
this.pipelineExecutionTask.Task);
1505+
localDebuggerStoppedTask,
1506+
localPipelineExecutionTask);
14911507

14921508
if (taskIndex == 0)
14931509
{
14941510
// Write a new output line before continuing
14951511
this.WriteOutput("", true);
14961512

1497-
e.ResumeAction = this.debuggerStoppedTask.Task.Result;
1513+
e.ResumeAction = localDebuggerStoppedTask.Result;
14981514
Logger.Write(LogLevel.Verbose, "Received debugger resume action " + e.ResumeAction.ToString());
14991515

15001516
// Notify listeners that the debugger has resumed
@@ -1524,10 +1540,10 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e)
15241540
{
15251541
Logger.Write(LogLevel.Verbose, "Received pipeline thread execution request.");
15261542

1527-
IPipelineExecutionRequest executionRequest =
1528-
this.pipelineExecutionTask.Task.Result;
1543+
IPipelineExecutionRequest executionRequest = localPipelineExecutionTask.Result;
15291544

15301545
this.pipelineExecutionTask = new TaskCompletionSource<IPipelineExecutionRequest>();
1546+
localPipelineExecutionTask = this.pipelineExecutionTask.Task;
15311547

15321548
executionRequest.Execute().Wait();
15331549

src/PowerShellEditorServices/Session/RemoteFileManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ public bool IsRemotePathOpened(string remotePath)
497497

498498
public string GetMappedPath(string filePath)
499499
{
500-
string mappedPath = null;
500+
string mappedPath = filePath;
501501

502502
if (!this.pathMappings.TryGetValue(filePath.ToLower(), out mappedPath))
503503
{

0 commit comments

Comments
 (0)