Skip to content

Commit 5497895

Browse files
authored
Handle ProcessPicker via resolveDebugConfiguration (#4509)
* Handle ProcessPicker via resolveDebugConfiguration VS Code commands are limited to only being able to have a single output. We will handle having an empty processId and show the dialog for ProcessPicker in resolveDebugConfigurationWithSubstitutedVariables. We need multiple outputs to handle the latest macOS on Apple M1. For Apple Silicon M1 (ARM64), we need to determine if we need to use the x86_64 or arm64 debugger. We are able to detect if the process is using S_TRANSLATED using the ps commandline with 'flags', if it is set with 0x20000, it is emulated. * Addressing PR issues Migrate remoteProcessPicker to also be in resolve configuration Handle 'clr' type for remote process picking. Addressing PR issues. Added comment about 0x20000 * Remove 'processId' from default config * Remove 'processId' from assets.ts
1 parent ad19c37 commit 5497895

File tree

9 files changed

+108
-42
lines changed

9 files changed

+108
-42
lines changed

debugger.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ The C# debugger supports attaching to processes. To do this, switch to the Debug
7777

7878
![Debug launch configuration drop down](https://raw.githubusercontent.com/wiki/OmniSharp/omnisharp-vscode/images/debug-launch-configurations.png)
7979

80-
Select the '.NET Core Attach' configuration. Clicking the play button (or pressing <kbd>F5</kbd>) will then try to attach. In launch.json, if `processId` is set to `"${command:pickProcess}"` this will provide UI to select which process to attach to.
80+
Select the '.NET Core Attach' configuration. Clicking the play button (or pressing <kbd>F5</kbd>) will then try to attach. In launch.json, if `processId` is set to `""` this will provide UI to select which process to attach to.
8181

8282
#### Remote Debugging
8383

package.json

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,12 +1624,12 @@
16241624
"anyOf": [
16251625
{
16261626
"type": "string",
1627-
"description": "The process id to attach to. Use \"${command:pickProcess}\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
1628-
"default": "${command:pickProcess}"
1627+
"description": "The process id to attach to. Use \"\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
1628+
"default": ""
16291629
},
16301630
{
16311631
"type": "integer",
1632-
"description": "The process id to attach to. Use \"${command:pickProcess}\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
1632+
"description": "The process id to attach to. Use \"\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
16331633
"default": 0
16341634
}
16351635
]
@@ -2061,8 +2061,7 @@
20612061
"body": {
20622062
"name": ".NET Core Attach",
20632063
"type": "coreclr",
2064-
"request": "attach",
2065-
"processId": "^\"\\${command:pickProcess}\""
2064+
"request": "attach"
20662065
}
20672066
},
20682067
{
@@ -2117,7 +2116,6 @@
21172116
"name": ".NET Core Attach",
21182117
"type": "coreclr",
21192118
"request": "attach",
2120-
"processId": "^\"\\${command:pickRemoteProcess}\"",
21212119
"pipeTransport": {
21222120
"pipeCwd": "^\"\\${workspaceFolder}\"",
21232121
"pipeProgram": "^\"${1:enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'}\"",
@@ -2725,12 +2723,12 @@
27252723
"anyOf": [
27262724
{
27272725
"type": "string",
2728-
"description": "The process id to attach to. Use \"${command:pickProcess}\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
2729-
"default": "${command:pickProcess}"
2726+
"description": "The process id to attach to. Use \"\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
2727+
"default": ""
27302728
},
27312729
{
27322730
"type": "integer",
2733-
"description": "The process id to attach to. Use \"${command:pickProcess}\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
2731+
"description": "The process id to attach to. Use \"\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
27342732
"default": 0
27352733
}
27362734
]

scripts/remoteProcessPickerScript

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
uname && if [ "$(uname)" = "Linux" ] ; then ps -axww -o pid=,comm=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,args= ; exit; elif [ "$(uname)" = "Darwin" ] ; then ps -axww -o pid=,comm=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,args= -c; fi
1+
uname && if [ "$(uname)" = "Linux" ] ; then ps -axww -o pid=,flags=,comm=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,args= ; exit; elif [ "$(uname)" = "Darwin" ] ; then ps -axww -o pid=,flags=,comm=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,args= -c; fi

src/assets.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,8 +420,7 @@ export function createAttachConfiguration(): string {
420420
const configuration = {
421421
"name": ".NET Core Attach",
422422
"type": "coreclr",
423-
"request": "attach",
424-
"processId": "\${command:pickProcess}"
423+
"request": "attach"
425424
};
426425

427426
return JSON.stringify(configuration);

src/coreclr-debug/activate.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { EventStream } from '../EventStream';
1313
import CSharpExtensionExports from '../CSharpExtensionExports';
1414
import { getRuntimeDependencyPackageWithId } from '../tools/RuntimeDependencyPackageUtils';
1515
import { getDotnetInfo, DotnetInfo } from '../utils/getDotnetInfo';
16+
import { DotnetDebugConfigurationProvider } from './debugConfigurationProvider';
1617

1718
let _debugUtil: CoreClrDebugUtil = null;
1819

@@ -30,6 +31,8 @@ export async function activate(thisExtension: vscode.Extension<CSharpExtensionEx
3031
}
3132

3233
const factory = new DebugAdapterExecutableFactory(platformInformation, eventStream, thisExtension.packageJSON, thisExtension.extensionPath);
34+
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('coreclr', new DotnetDebugConfigurationProvider(platformInformation)));
35+
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('clr', new DotnetDebugConfigurationProvider(platformInformation)));
3336
context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('coreclr', factory));
3437
context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('clr', factory));
3538
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
8+
import { RemoteAttachPicker, DotNetAttachItemsProviderFactory, AttachPicker, AttachItem } from '../features/processPicker';
9+
import { PlatformInformation } from '../platform';
10+
11+
export class DotnetDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
12+
constructor(public platformInformation: PlatformInformation) {}
13+
14+
public async resolveDebugConfigurationWithSubstitutedVariables(folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration>
15+
{
16+
if (!debugConfiguration)
17+
{
18+
return null;
19+
}
20+
21+
// Process Id is empty, handle Attach to Process Dialog.
22+
if (debugConfiguration.request === "attach" && !debugConfiguration.processId && !debugConfiguration.processName)
23+
{
24+
let process: AttachItem = undefined;
25+
if (debugConfiguration.pipeTransport)
26+
{
27+
process = await RemoteAttachPicker.ShowAttachEntries(debugConfiguration, this.platformInformation);
28+
}
29+
else
30+
{
31+
let attachItemsProvider = DotNetAttachItemsProviderFactory.Get();
32+
let attacher = new AttachPicker(attachItemsProvider);
33+
process = await attacher.ShowAttachEntries();
34+
}
35+
36+
if (process)
37+
{
38+
debugConfiguration.processId = process.id;
39+
40+
if (debugConfiguration.type == "coreclr" &&
41+
this.platformInformation.isMacOS() &&
42+
this.platformInformation.architecture == 'arm64')
43+
{
44+
// For Apple Silicon M1, it is possible that the process we are attaching to is being emulated as x86_64.
45+
// The process is emulated if it has process flags has P_TRANSLATED (0x20000).
46+
if (process.flags & 0x20000)
47+
{
48+
debugConfiguration.targetArchitecture = "x86_64";
49+
}
50+
else
51+
{
52+
debugConfiguration.targetArchitecture = "arm64";
53+
}
54+
}
55+
}
56+
else
57+
{
58+
throw new Error("No process was selected.");
59+
}
60+
}
61+
62+
return debugConfiguration;
63+
}
64+
}

src/features/commands.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import * as fs from 'fs';
1111
import * as path from 'path';
1212
import * as protocol from '../omnisharp/protocol';
1313
import * as vscode from 'vscode';
14-
import { DotNetAttachItemsProviderFactory, AttachPicker, RemoteAttachPicker } from './processPicker';
1514
import { generateAssets } from '../assets';
1615
import { ShowOmniSharpChannel, CommandDotNetRestoreStart, CommandDotNetRestoreProgress, CommandDotNetRestoreSucceeded, CommandDotNetRestoreFailed } from '../omnisharp/loggingEvents';
1716
import { EventStream } from '../EventStream';
@@ -40,14 +39,13 @@ export default function registerCommands(context: vscode.ExtensionContext, serve
4039
// running the command activates the extension, which is all we need for installation to kickoff
4140
disposable.add(vscode.commands.registerCommand('csharp.downloadDebugger', () => { }));
4241

43-
// register process picker for attach
44-
let attachItemsProvider = DotNetAttachItemsProviderFactory.Get();
45-
let attacher = new AttachPicker(attachItemsProvider);
46-
disposable.add(vscode.commands.registerCommand('csharp.listProcess', async () => attacher.ShowAttachEntries()));
42+
// register process picker for attach for legacy configurations.
43+
disposable.add(vscode.commands.registerCommand('csharp.listProcess', () => ""));
44+
disposable.add(vscode.commands.registerCommand('csharp.listRemoteProcess', () => ""));
45+
46+
4747
// Register command for generating tasks.json and launch.json assets.
4848
disposable.add(vscode.commands.registerCommand('dotnet.generateAssets', async (selectedIndex) => generateAssets(server, selectedIndex)));
49-
// Register command for remote process picker for attach
50-
disposable.add(vscode.commands.registerCommand('csharp.listRemoteProcess', async (args) => RemoteAttachPicker.ShowAttachEntries(args, platformInfo)));
5149

5250
disposable.add(vscode.commands.registerCommand('csharp.reportIssue', async () => reportIssue(vscode, eventStream, getDotnetInfo, platformInfo.isValidPlatformForMono(), optionProvider.GetLatestOptions(), monoResolver)));
5351

src/features/processPicker.ts

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { getExtensionPath } from '../common';
1414

1515
export interface AttachItem extends vscode.QuickPickItem {
1616
id: string;
17+
flags: number;
1718
}
1819

1920
export interface AttachItemsProvider {
@@ -23,7 +24,7 @@ export interface AttachItemsProvider {
2324
export class AttachPicker {
2425
constructor(private attachItemsProvider: AttachItemsProvider) { }
2526

26-
public async ShowAttachEntries(): Promise<string> {
27+
public async ShowAttachEntries(): Promise<AttachItem> {
2728
return this.attachItemsProvider.getAttachItems()
2829
.then(processEntries => {
2930
let attachPickOptions: vscode.QuickPickOptions = {
@@ -35,7 +36,7 @@ export class AttachPicker {
3536

3637
return vscode.window.showQuickPick(processEntries, attachPickOptions)
3738
.then(chosenProcess => {
38-
return chosenProcess ? chosenProcess.id : null;
39+
return chosenProcess;
3940
});
4041
});
4142
}
@@ -50,8 +51,8 @@ interface IPipeTransportOptions {
5051

5152
export class RemoteAttachPicker {
5253
public static get commColumnTitle() { return Array(PsOutputParser.secondColumnCharacters).join("a"); }
53-
public static get linuxPsCommand() { return `ps axww -o pid=,comm=${RemoteAttachPicker.commColumnTitle},args=`; }
54-
public static get osxPsCommand() { return `ps axww -o pid=,comm=${RemoteAttachPicker.commColumnTitle},args= -c`; }
54+
public static get linuxPsCommand() { return `ps axww -o pid=,flags=,comm=${RemoteAttachPicker.commColumnTitle},args=`; }
55+
public static get osxPsCommand() { return `ps axww -o pid=,flags=,comm=${RemoteAttachPicker.commColumnTitle},args= -c`; }
5556
public static get debuggerCommand() { return "${debuggerCommand}"; }
5657
public static get scriptShellCmd() { return "sh -s"; }
5758

@@ -196,7 +197,7 @@ export class RemoteAttachPicker {
196197
return args.map(arg => this.quoteArg(arg)).join(" ");
197198
}
198199

199-
public static async ShowAttachEntries(args: any, platformInfo: PlatformInformation): Promise<string> {
200+
public static async ShowAttachEntries(args: any, platformInfo: PlatformInformation): Promise<AttachItem> {
200201
// Create remote attach output channel for errors.
201202
if (!RemoteAttachPicker._channel) {
202203
RemoteAttachPicker._channel = vscode.window.createOutputChannel('remote-attach');
@@ -210,13 +211,13 @@ export class RemoteAttachPicker {
210211

211212
if (!name) {
212213
// Config name not found.
213-
return Promise.reject<string>(new Error("Name not defined in current configuration."));
214+
return Promise.reject<AttachItem>(new Error("Name not defined in current configuration."));
214215
}
215216

216217
if (!args.pipeTransport || !args.pipeTransport.debuggerPath) {
217218
// Missing PipeTransport and debuggerPath, prompt if user wanted to just do local attach.
218-
return Promise.reject<string>(new Error("Configuration \"" + name + "\" in launch.json does not have a " +
219-
"pipeTransport argument with debuggerPath for pickRemoteProcess. Use pickProcess for local attach."));
219+
return Promise.reject<AttachItem>(new Error("Configuration \"" + name + "\" in launch.json does not have a " +
220+
"pipeTransport argument with debuggerPath for remote process listing."));
220221
} else {
221222
let pipeTransport = this.getPipeTransportOptions(args.pipeTransport, os.platform());
222223

@@ -230,8 +231,7 @@ export class RemoteAttachPicker {
230231
placeHolder: "Select the process to attach to"
231232
};
232233
return vscode.window.showQuickPick(processes, attachPickOptions);
233-
})
234-
.then(item => { return item ? item.id : Promise.reject<string>(new Error("Could not find a process id to attach.")); });
234+
});
235235
}
236236
}
237237

@@ -266,14 +266,15 @@ export class RemoteAttachPicker {
266266
}
267267

268268
class Process {
269-
constructor(public name: string, public pid: string, public commandLine: string) { }
269+
constructor(public name: string, public pid: string, public commandLine: string, public flags: number) { }
270270

271271
public toAttachItem(): AttachItem {
272272
return {
273273
label: this.name,
274274
description: this.pid,
275275
detail: this.commandLine,
276-
id: this.pid
276+
id: this.pid,
277+
flags: this.flags
277278
};
278279
}
279280
}
@@ -404,17 +405,20 @@ export class PsOutputParser {
404405
// - any leading whitespace
405406
// - PID
406407
// - whitespace
408+
// - flags (hex value)
409+
// - whitespace
407410
// - executable name --> this is PsAttachItemsProvider.secondColumnCharacters - 1 because ps reserves one character
408411
// for the whitespace separator
409412
// - whitespace
410413
// - args (might be empty)
411-
const psEntry = new RegExp(`^\\s*([0-9]+)\\s+(.{${PsOutputParser.secondColumnCharacters - 1}})\\s+(.*)$`);
414+
const psEntry = new RegExp(`^\\s*([0-9]+)\\s+([0-9a-fA-F]+)\\s+(.{${PsOutputParser.secondColumnCharacters - 1}})\\s+(.*)$`);
412415
const matches = psEntry.exec(line);
413-
if (matches && matches.length === 4) {
416+
if (matches && matches.length === 5) {
414417
const pid = matches[1].trim();
415-
const executable = matches[2].trim();
416-
const cmdline = matches[3].trim();
417-
return new Process(executable, pid, cmdline);
418+
const flags = parseInt(matches[2].trim(), 16); // flags comes in as hex
419+
const executable = matches[3].trim();
420+
const cmdline = matches[4].trim();
421+
return new Process(executable, pid, cmdline, flags);
418422
}
419423
}
420424
}
@@ -444,7 +448,7 @@ export class WmicOutputParser {
444448
// Only public for tests.
445449
public static parseProcessFromWmic(processes: string): Process[] {
446450
let lines = processes.split(os.EOL);
447-
let currentProcess: Process = new Process(null, null, null);
451+
let currentProcess: Process = new Process(null, null, null, null);
448452
let processEntries: Process[] = [];
449453

450454
for (let i = 0; i < lines.length; i++) {
@@ -458,7 +462,7 @@ export class WmicOutputParser {
458462
// Each entry of processes has ProcessId as the last line
459463
if (line.startsWith(WmicOutputParser.wmicPidTitle)) {
460464
processEntries.push(currentProcess);
461-
currentProcess = new Process(null, null, null);
465+
currentProcess = new Process(null, null, null, null);
462466
}
463467
}
464468

src/tools/OptionsSchema.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,12 @@
406406
"anyOf": [
407407
{
408408
"type": "string",
409-
"description": "The process id to attach to. Use \"${command:pickProcess}\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
410-
"default": "${command:pickProcess}"
409+
"description": "The process id to attach to. Use \"\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
410+
"default": ""
411411
},
412412
{
413413
"type": "integer",
414-
"description": "The process id to attach to. Use \"${command:pickProcess}\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
414+
"description": "The process id to attach to. Use \"\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
415415
"default": 0
416416
}
417417
]

0 commit comments

Comments
 (0)