Skip to content

Add Start-DebugAttachSession function #2249

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ FunctionsToExport = @('Register-EditorCommand',
'Test-ScriptExtent',
'Open-EditorFile',
'New-EditorFile',
'Clear-Host')
'Clear-Host',
'Start-DebugAttachSession')

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

using namespace System.Collections
using namespace System.Management.Automation
using namespace System.Reflection
using namespace System.Threading
using namespace System.Threading.Tasks

function Start-DebugAttachSession {
<#
.EXTERNALHELP ..\PowerShellEditorServices.Commands-help.xml
#>
[OutputType([System.Management.Automation.Job2])]
[CmdletBinding(DefaultParameterSetName = 'ProcessId')]
param(
[Parameter()]
[string]
$Name,

[Parameter(ParameterSetName = 'ProcessId')]
[int]
$ProcessId,

[Parameter(ParameterSetName = 'CustomPipeName')]
[string]
$CustomPipeName,

[Parameter()]
[string]
$RunspaceName,

[Parameter()]
[int]
$RunspaceId,

[Parameter()]
[string]
$ComputerName,

[Parameter()]
[switch]
$AsJob
)

$ErrorActionPreference = 'Stop'

try {
if ($PSBoundParameters.ContainsKey('RunspaceId') -and $RunspaceName) {
$err = [ErrorRecord]::new(
[ArgumentException]::new("Cannot specify both RunspaceId and RunspaceName parameters"),
"InvalidRunspaceParameters",
[ErrorCategory]::InvalidArgument,
$null)
$err.ErrorDetails = [ErrorDetails]::new("")
$err.ErrorDetails.RecommendedAction = 'Specify only one of RunspaceId or RunspaceName.'
$PSCmdlet.WriteError($err)
return
}

# Var will be set by PSES in configurationDone before launching script
$debugServer = Get-Variable -Name __psEditorServices_DebugServer -ValueOnly -ErrorAction Ignore
if (-not $debugServer) {
$err = [ErrorRecord]::new(
[Exception]::new("Cannot start a new attach debug session unless running in an existing launch debug session not in a temporary console"),
"NoDebugSession",
[ErrorCategory]::InvalidOperation,
$null)
$err.ErrorDetails = [ErrorDetails]::new("")
$err.ErrorDetails.RecommendedAction = 'Launch script with debugging to ensure the debug session is available.'
$PSCmdlet.WriteError($err)
return
}

if ($AsJob -and -not (Get-Command -Name Start-ThreadJob -ErrorAction Ignore)) {
$err = [ErrorRecord]::new(
[Exception]::new("Cannot use the -AsJob parameter unless running on PowerShell 7+ or the ThreadJob module is present"),
"NoThreadJob",
[ErrorCategory]::InvalidArgument,
$null)
$err.ErrorDetails = [ErrorDetails]::new("")
$err.ErrorDetails.RecommendedAction = 'Install the ThreadJob module or run on PowerShell 7+.'
$PSCmdlet.WriteError($err)
return
}

$configuration = @{
type = 'PowerShell'
request = 'attach'
# A temp console is also needed as the current one is busy running
# this code. Failing to set this will cause a deadlock.
createTemporaryIntegratedConsole = $true
}

if ($ProcessId) {
if ($ProcessId -eq $PID) {
$err = [ErrorRecord]::new(
[ArgumentException]::new("PSES does not support attaching to the current editor process"),
"AttachToCurrentProcess",
[ErrorCategory]::InvalidArgument,
$PID)
$err.ErrorDetails = [ErrorDetails]::new("")
$err.ErrorDetails.RecommendedAction = 'Specify a different process id.'
$PSCmdlet.WriteError($err)
return
}

$configuration.name = "Attach Process $ProcessId"
$configuration.processId = $ProcessId
}
elseif ($CustomPipeName) {
$configuration.name = "Attach Pipe $CustomPipeName"
$configuration.customPipeName = $CustomPipeName
}
else {
$configuration.name = 'Attach Session'
}

if ($ComputerName) {
$configuration.computerName = $ComputerName
}

if ($PSBoundParameters.ContainsKey('RunspaceId')) {
$configuration.runspaceId = $RunspaceId
}
elseif ($RunspaceName) {
$configuration.runspaceName = $RunspaceName
}

# https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_StartDebugging
$resp = $debugServer.SendRequest(
"startDebugging",
@{
configuration = $configuration
request = 'attach'
}
)

# PipelineStopToken added in pwsh 7.6
$cancelToken = if ($PSCmdlet.PipelineStopToken) {
$PSCmdlet.PipelineStopToken
}
else {
[CancellationToken]::new($false)
}

# There is no response for a startDebugging request
$task = $resp.ReturningVoid($cancelToken)

$waitTask = {
[CmdletBinding()]
param ([Parameter(Mandatory)][Task]$Task)

while (-not $Task.AsyncWaitHandle.WaitOne(300)) {}
$null = $Task.GetAwaiter().GetResult()
}

if ($AsJob) {
# Using the Ast to build the scriptblock allows the job to inherit
# the using namespace entries and include the proper line/script
# paths in any error traces that are emitted.
Start-ThreadJob -ScriptBlock {
& ($args[0]).Ast.GetScriptBlock() $args[1]
} -ArgumentList $waitTask, $task
}
else {
& $waitTask $task
}
}
catch {
$PSCmdlet.WriteError($_)
return
}
}
4 changes: 4 additions & 0 deletions module/docs/PowerShellEditorServices.Commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ The Set-ScriptExtent function can insert or replace text at a specified position

You can use the Find-Ast function to easily find the desired extent.

### [Start-DebugAttachSession](Start-DebugAttachSession.md)

The Start-DebugAttachSession function can start a new debug session that is attached to the specified PowerShell instance.

### [Test-ScriptExtent](Test-ScriptExtent.md)

The Test-ScriptExtent function can be used to determine if a ScriptExtent object is before, after, or inside another ScriptExtent object. You can also test for any combination of these with separate ScriptExtent objects to test against.
Loading