| /*--------------------------------------------------------- |
| * Copyright 2020 The Go Authors. All rights reserved. |
| * Licensed under the MIT License. See LICENSE in the project root for license information. |
| *--------------------------------------------------------*/ |
| |
| import cp = require('child_process'); |
| import util = require('util'); |
| import { QuickPickItem } from 'vscode'; |
| import vscode = require('vscode'); |
| import { toolExecutionEnvironment } from './goEnv'; |
| import { getBinPath, getWorkspaceFolderPath } from './util'; |
| import { envPath, getCurrentGoRoot } from './utils/pathUtils'; |
| import { parsePsProcesses, psDarwinCommand, psLinuxCommand } from './utils/psProcessParser'; |
| import { parseWmicProcesses, wmicCommand } from './utils/wmicProcessParser'; |
| |
| // TODO(suzmue): create a command pickGoProcess to filter |
| // to processes that are using go. |
| export async function pickProcess(): Promise<string> { |
| const allProcesses = await getAllProcesses(); |
| const id = await processPicker(allProcesses); |
| return id; |
| } |
| |
| export async function pickGoProcess(): Promise<string> { |
| const allProcesses = await getGoProcesses(); |
| const id = await processPicker(allProcesses); |
| return id; |
| } |
| |
| async function processPicker(processes: AttachItem[]): Promise<string> { |
| const selection = await vscode.window.showQuickPick( |
| processes, |
| { |
| placeHolder: 'Choose a process to attach to', |
| matchOnDescription: true, |
| matchOnDetail: true, |
| } |
| ); |
| if (!selection) { |
| return '0'; |
| } |
| return selection.id; |
| } |
| |
| // Taken from: |
| // https://github.com/microsoft/vscode-python/blob/main/src/client/debugger/extension/attachQuickPick/provider.ts |
| |
| export interface AttachItem extends QuickPickItem { |
| id: string; |
| processName: string; |
| commandLine: string; |
| executable?: string; |
| isGo?: boolean; |
| } |
| |
| export interface ProcessListCommand { |
| command: string; |
| args: string[]; |
| } |
| |
| async function getGoProcesses(): Promise<AttachItem[]> { |
| const processes = await getAllProcesses(); |
| // TODO(suzmue): set the executable path for and darwin. |
| if (process.platform === 'darwin') { |
| return processes; |
| } |
| |
| // Run 'go version' on all executable paths to find 'go' processes |
| const goRuntimePath = getBinPath('go'); |
| if (!goRuntimePath) { |
| vscode.window.showErrorMessage( |
| `Failed to run "go version" as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})` |
| ); |
| return processes; |
| } |
| const args = ['version']; |
| processes.forEach((item, i) => { |
| args.push(item.executable); |
| }); |
| const {stdout} = cp.spawnSync(goRuntimePath, args, { env: toolExecutionEnvironment(), cwd: getWorkspaceFolderPath() }); |
| |
| // Parse the process ids from stdout. Ignore stderr, since we expect many to fail. |
| const goProcessExes: string[] = []; |
| const lines = stdout.toString().split('\n'); |
| |
| const goVersionRegexp = /: go\d+.\d+.\d+$/; |
| lines.forEach((line) => { |
| const match = line.match(goVersionRegexp); |
| if (match && match.length > 0) { |
| const exe = line.substr(0, line.length - match[0].length); |
| goProcessExes.push(exe); |
| } |
| }); |
| |
| const goProcesses: AttachItem[] = []; |
| processes.forEach((item) => { |
| if (goProcessExes.indexOf(item.executable) >= 0) { |
| item.isGo = true; |
| goProcesses.push(item); |
| } |
| }); |
| return goProcesses; |
| } |
| |
| async function getAllProcesses(): Promise<AttachItem[]> { |
| let processCmd: ProcessListCommand; |
| switch (process.platform) { |
| case 'win32': |
| processCmd = wmicCommand; |
| break; |
| case 'darwin': |
| processCmd = psDarwinCommand; |
| break; |
| case 'linux': |
| processCmd = psLinuxCommand; |
| break; |
| default: |
| // Other operating systems are not supported. |
| return []; |
| } |
| |
| const execFile = util.promisify(cp.execFile); |
| const { stdout } = await execFile(processCmd.command, processCmd.args); |
| |
| return process.platform === 'win32' |
| ? parseWmicProcesses(stdout) |
| : parsePsProcesses(stdout); |
| } |