Skip to content
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

Handle ProcessPicker via resolveDebugConfiguration #4509

Merged
merged 4 commits into from
Apr 21, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions src/coreclr-debug/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { EventStream } from '../EventStream';
import CSharpExtensionExports from '../CSharpExtensionExports';
import { getRuntimeDependencyPackageWithId } from '../tools/RuntimeDependencyPackageUtils';
import { getDotnetInfo, DotnetInfo } from '../utils/getDotnetInfo';
import { CoreCLRConfigurationProvider } from './debugConfigurationProvider';

let _debugUtil: CoreClrDebugUtil = null;

Expand All @@ -30,6 +31,7 @@ export async function activate(thisExtension: vscode.Extension<CSharpExtensionEx
}

const factory = new DebugAdapterExecutableFactory(platformInformation, eventStream, thisExtension.packageJSON, thisExtension.extensionPath);
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('coreclr', new CoreCLRConfigurationProvider(platformInformation)));
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('coreclr', factory));
context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('clr', factory));
}
Expand Down
51 changes: 51 additions & 0 deletions src/coreclr-debug/debugConfigurationProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';

import { DotNetAttachItemsProviderFactory, AttachPicker, AttachItem } from '../features/processPicker';
import { PlatformInformation } from '../platform';

export class CoreCLRConfigurationProvider implements vscode.DebugConfigurationProvider {
constructor(public platformInformation: PlatformInformation) {}

public async resolveDebugConfigurationWithSubstitutedVariables(folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration>
{
if (!debugConfiguration)
{
return null;
}

// Process Id is empty, handle Attach to Process Dialog.
if (debugConfiguration.request === "attach" && !debugConfiguration.processId)
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
{
let attachItemsProvider = DotNetAttachItemsProviderFactory.Get();
let attacher = new AttachPicker(attachItemsProvider);
const process: AttachItem = await attacher.SelectProcess();

if (process)
{
debugConfiguration.processId = process.id;
if (this.platformInformation.isMacOS() && this.platformInformation.architecture == 'arm64')
{
if (process.flags & 0x20000)
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
{
debugConfiguration.targetArchitecture = "x86_64";
}
else
{
debugConfiguration.targetArchitecture = "arm64";
}
}
}
else
{
throw new Error("No process was selected.");
}
}

return debugConfiguration;
}
}
9 changes: 4 additions & 5 deletions src/features/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as fs from 'fs';
import * as path from 'path';
import * as protocol from '../omnisharp/protocol';
import * as vscode from 'vscode';
import { DotNetAttachItemsProviderFactory, AttachPicker, RemoteAttachPicker } from './processPicker';
import { RemoteAttachPicker } from './processPicker';
import { generateAssets } from '../assets';
import { ShowOmniSharpChannel, CommandDotNetRestoreStart, CommandDotNetRestoreProgress, CommandDotNetRestoreSucceeded, CommandDotNetRestoreFailed } from '../omnisharp/loggingEvents';
import { EventStream } from '../EventStream';
Expand Down Expand Up @@ -40,10 +40,9 @@ export default function registerCommands(context: vscode.ExtensionContext, serve
// running the command activates the extension, which is all we need for installation to kickoff
disposable.add(vscode.commands.registerCommand('csharp.downloadDebugger', () => { }));

// register process picker for attach
let attachItemsProvider = DotNetAttachItemsProviderFactory.Get();
let attacher = new AttachPicker(attachItemsProvider);
disposable.add(vscode.commands.registerCommand('csharp.listProcess', async () => attacher.ShowAttachEntries()));
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
// register process picker for attach for legacy configurations.
disposable.add(vscode.commands.registerCommand('csharp.listProcess', async () => ""));
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved

// Register command for generating tasks.json and launch.json assets.
disposable.add(vscode.commands.registerCommand('dotnet.generateAssets', async (selectedIndex) => generateAssets(server, selectedIndex)));
// Register command for remote process picker for attach
Expand Down
44 changes: 33 additions & 11 deletions src/features/processPicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { getExtensionPath } from '../common';

export interface AttachItem extends vscode.QuickPickItem {
id: string;
flags: number;
}

export interface AttachItemsProvider {
Expand All @@ -39,6 +40,23 @@ export class AttachPicker {
});
});
}
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved

public async SelectProcess(): Promise<AttachItem> {
return this.attachItemsProvider.getAttachItems()
.then(processEntries => {
let attachPickOptions: vscode.QuickPickOptions = {
ignoreFocusOut: true,
matchOnDescription: true,
matchOnDetail: true,
placeHolder: "Select the process to attach to"
};

return vscode.window.showQuickPick(processEntries, attachPickOptions)
.then(chosenProcess => {
return chosenProcess;
});
});
}
}

interface IPipeTransportOptions {
Expand All @@ -50,8 +68,8 @@ interface IPipeTransportOptions {

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

Expand Down Expand Up @@ -266,14 +284,15 @@ export class RemoteAttachPicker {
}

class Process {
constructor(public name: string, public pid: string, public commandLine: string) { }
constructor(public name: string, public pid: string, public commandLine: string, public flags: number) { }

public toAttachItem(): AttachItem {
return {
label: this.name,
description: this.pid,
detail: this.commandLine,
id: this.pid
id: this.pid,
flags: this.flags
};
}
}
Expand Down Expand Up @@ -404,17 +423,20 @@ export class PsOutputParser {
// - any leading whitespace
// - PID
// - whitespace
// - flags
// - whitespace
// - executable name --> this is PsAttachItemsProvider.secondColumnCharacters - 1 because ps reserves one character
// for the whitespace separator
// - whitespace
// - args (might be empty)
const psEntry = new RegExp(`^\\s*([0-9]+)\\s+(.{${PsOutputParser.secondColumnCharacters - 1}})\\s+(.*)$`);
const psEntry = new RegExp(`^\\s*([0-9]+)\\s+([0-9a-fA-F]+)\\s+(.{${PsOutputParser.secondColumnCharacters - 1}})\\s+(.*)$`);
const matches = psEntry.exec(line);
if (matches && matches.length === 4) {
if (matches && matches.length === 5) {
const pid = matches[1].trim();
const executable = matches[2].trim();
const cmdline = matches[3].trim();
return new Process(executable, pid, cmdline);
const flags = parseInt(matches[2].trim(), 16); // flags comes in as hex
const executable = matches[3].trim();
const cmdline = matches[4].trim();
return new Process(executable, pid, cmdline, flags);
}
}
}
Expand Down Expand Up @@ -444,7 +466,7 @@ export class WmicOutputParser {
// Only public for tests.
public static parseProcessFromWmic(processes: string): Process[] {
let lines = processes.split(os.EOL);
let currentProcess: Process = new Process(null, null, null);
let currentProcess: Process = new Process(null, null, null, null);
let processEntries: Process[] = [];

for (let i = 0; i < lines.length; i++) {
Expand All @@ -458,7 +480,7 @@ export class WmicOutputParser {
// Each entry of processes has ProcessId as the last line
if (line.startsWith(WmicOutputParser.wmicPidTitle)) {
processEntries.push(currentProcess);
currentProcess = new Process(null, null, null);
currentProcess = new Process(null, null, null, null);
}
}

Expand Down