Fix for Linux remote script debugging hang (#2213)

* Fix for Linux remote debugging hang bug.

* Removing dependency in PSReadLine on new engine helper API since it needs to be compatible with V3.
This commit is contained in:
Paul Higinbotham 2016-09-13 11:31:48 -07:00 committed by Jason Shirk
parent fd4552bfb8
commit 79ddafcc85
3 changed files with 168 additions and 12 deletions

View file

@ -881,6 +881,9 @@ namespace Microsoft.PowerShell
_singleton.Render();
}
private const string PromptCommand = "prompt";
private const string DefaultPrompt = "PS>";
/// <summary>
/// Gets the current prompt as possibly defined by the user through the
/// prompt function, and returns a default prompt if no other is
@ -888,23 +891,45 @@ namespace Microsoft.PowerShell
/// </summary>
public static string GetPrompt()
{
string newPrompt;
var runspaceIsRemote = _singleton._mockableMethods.RunspaceIsRemote(_singleton._runspace);
System.Management.Automation.PowerShell ps;
if (!runspaceIsRemote)
if ((_singleton._runspace.Debugger != null) && _singleton._runspace.Debugger.InBreakpoint)
{
ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
// Run prompt command in debugger API to ensure it is run correctly on the runspace.
// This handles remote runspace debugging and nested debugger scenarios.
PSDataCollection<PSObject> results = new PSDataCollection<PSObject>();
var command = new PSCommand();
command.AddCommand(PromptCommand);
_singleton._runspace.Debugger.ProcessCommand(
command,
results);
newPrompt = (results.Count == 1) ? (results[0].BaseObject as string) : DefaultPrompt;
}
else
{
ps = System.Management.Automation.PowerShell.Create();
ps.Runspace = _singleton._runspace;
System.Management.Automation.PowerShell ps;
if (!runspaceIsRemote)
{
ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
}
else
{
ps = System.Management.Automation.PowerShell.Create();
ps.Runspace = _singleton._runspace;
}
using (ps)
{
ps.AddCommand(PromptCommand);
var result = ps.Invoke<string>();
newPrompt = result.Count == 1 ? result[0] : DefaultPrompt;
}
}
string newPrompt;
using (ps)
if (string.IsNullOrEmpty(newPrompt))
{
ps.AddCommand("prompt");
var result = ps.Invoke<string>();
newPrompt = result.Count == 1 ? result[0] : "PS>";
newPrompt = DefaultPrompt;
}
if (runspaceIsRemote)
@ -915,6 +940,7 @@ namespace Microsoft.PowerShell
newPrompt = string.Format(CultureInfo.InvariantCulture, "[{0}]: {1}", connectionInfo.ComputerName, newPrompt);
}
}
return newPrompt;
}

View file

@ -1,5 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using Microsoft.PowerShell.Commands;
using System.Management.Automation.Host;
@ -863,6 +864,65 @@ namespace System.Management.Automation
#region Public Access
#region Runspace Invoke
/// <summary>
/// Helper method to invoke a PSCommand on a given runspace. This method correctly invokes the command for
/// these runspace cases:
/// 1. Local runspace. If the local runspace is busy it will invoke as a nested command.
/// 2. Remote runspace.
/// 3. Runspace that is stopped in the debugger at a breakpoint.
///
/// Error and information streams are ignored and only the command result output is returned.
///
/// This method is NOT thread safe. It does not support running commands from different threads on the
/// provided runspace. It assumes the thread invoking this method is the same that runs all other
/// commands on the provided runspace.
/// </summary>
/// <param name="runspace">Runspace to invoke the command on</param>
/// <param name="command">Command to invoke</param>
/// <returns>Collection of command output result objects</returns>
public static Collection<PSObject> InvokeOnRunspace(PSCommand command, Runspace runspace)
{
if (command == null)
{
throw new PSArgumentNullException("command");
}
if (runspace == null)
{
throw new PSArgumentNullException("runspace");
}
if ((runspace.Debugger != null) && runspace.Debugger.InBreakpoint)
{
// Use the Debugger API to run the command when a runspace is stopped in the debugger.
PSDataCollection<PSObject> output = new PSDataCollection<PSObject>();
runspace.Debugger.ProcessCommand(
command,
output);
return new Collection<PSObject>(output);
}
// Otherwise run command directly in runspace.
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.IsRunspaceOwner = false;
if (runspace.ConnectionInfo == null)
{
// Local runspace. Make a nested PowerShell object as needed.
ps.SetIsNested(runspace.GetCurrentlyRunningPipeline() != null);
}
using (ps)
{
ps.Commands = command;
return ps.Invoke<PSObject>();
}
}
#endregion
#region PSEdit Support
/// <summary>
@ -878,10 +938,10 @@ namespace System.Management.Automation
dir $file -File | foreach {
$filePathName = $_.FullName
# Get file contents
# Get file contents
$contentBytes = Get-Content -Path $filePathName -Raw -Encoding Byte
# Notify client for file open.
# Notify client for file open.
New-Event -SourceIdentifier PSISERemoteSessionOpenFile -EventArguments @($filePathName, $contentBytes) > $null
}
}

View file

@ -0,0 +1,70 @@
Describe "InvokeOnRunspace method argument error handling" -tags "Feature" {
BeforeAll {
$command = [System.Management.Automation.PSCommand]::new()
$localRunspace = $host.Runspace
}
It "Null argument exception should be thrown for null PSCommand argument" {
try
{
[System.Management.Automation.HostUtilities]::InvokeOnRunspace($null, $localRunspace)
throw "InvokeOnRunspace method did not throw expected PSArgumentNullException exception"
}
catch
{
$_.FullyQualifiedErrorId | Should Be "PSArgumentNullException"
}
}
It "Null argument exception should be thrown for null Runspace argument" {
try
{
[System.Management.Automation.HostUtilities]::InvokeOnRunspace($command, $null)
throw "InvokeOnRunspace method did not throw expected PSArgumentNullException exception"
}
catch
{
$_.FullyQualifiedErrorId | Should Be "PSArgumentNullException"
}
}
}
Describe "InvokeOnRunspace method as nested command" -tags "Feature" {
It "Method should successfully invoke command as nested on busy runspace" {
$command = [System.Management.Automation.PSCommand]::new()
$command.AddScript('"Hello!"')
$currentRunspace = $host.Runspace
$results = [System.Management.Automation.HostUtilities]::InvokeOnRunspace($command, $currentRunspace)
$results[0] | Should Be "Hello!"
}
}
Describe "InvokeOnRunspace method on remote runspace" -tags "Feature" {
BeforeAll {
$wc = [System.Management.Automation.Runspaces.WSManConnectionInfo]::new()
$remoteRunspace = [runspacefactory]::CreateRunspace($host, $wc)
$remoteRunspace.Open()
}
AfterAll {
$remoteRunspace.Dispose();
}
It "Method should successfully invoke command on remote runspace" {
$command = [System.Management.Automation.PSCommand]::new()
$command.AddScript('"Hello!"')
$results = [System.Management.Automation.HostUtilities]::InvokeOnRunspace($command, $remoteRunspace)
$results[0] | Should Be "Hello!"
}
}