blob: a28dbe0c5b34d71411d6638c4d9162ba65ff4d9b [file] [log] [blame]
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
* Modification copyright 2021 The Go Authors. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------*/
// Modified from:
// https://github.com/microsoft/vscode-python/blob/main/src/client/debugger/extension/attachQuickPick/psProcessParser.ts
// - Added the executable path '/proc/{PID}/exe' for linux processes.
'use strict';
import { AttachItem, ProcessListCommand } from '../pickProcess';
const secondColumnCharacters = 50;
const commColumnTitle = ''.padStart(secondColumnCharacters, 'a');
// Perf numbers:
// OS X 10.10
// | # of processes | Time (ms) |
// |----------------+-----------|
// | 272 | 52 |
// | 296 | 49 |
// | 384 | 53 |
// | 784 | 116 |
//
// Ubuntu 16.04
// | # of processes | Time (ms) |
// |----------------+-----------|
// | 232 | 26 |
// | 336 | 34 |
// | 736 | 62 |
// | 1039 | 115 |
// | 1239 | 182 |
// ps outputs as a table. With the option "ww", ps will use as much width as necessary.
// However, that only applies to the right-most column. Here we use a hack of setting
// the column header to 50 a's so that the second column will have at least that many
// characters. 50 was chosen because that's the maximum length of a "label" in the
// QuickPick UI in VS Code.
// the BSD version of ps uses '-c' to have 'comm' only output the executable name and not
// the full path. The Linux version of ps has 'comm' to only display the name of the executable
// Note that comm on Linux systems is truncated to 16 characters:
// https://bugzilla.redhat.com/show_bug.cgi?id=429565
// Since 'args' contains the full path to the executable, even if truncated, searching will work as desired.
export const psLinuxCommand: ProcessListCommand = {
command: 'ps',
args: ['axww', '-o', `pid=,comm=${commColumnTitle},args=`]
};
export const psDarwinCommand: ProcessListCommand = {
command: 'ps',
args: ['axww', '-o', `pid=,comm=${commColumnTitle},args=`, '-c']
};
export function parsePsProcesses(processes: string): AttachItem[] {
const lines: string[] = processes.split('\n');
return parseProcessesFromPsArray(lines);
}
function parseProcessesFromPsArray(processArray: string[]): AttachItem[] {
const processEntries: AttachItem[] = [];
// lines[0] is the header of the table
for (let i = 1; i < processArray.length; i += 1) {
const line = processArray[i];
if (!line) {
continue;
}
const processEntry = parseLineFromPs(line);
if (processEntry) {
processEntries.push(processEntry);
}
}
return processEntries;
}
function parseLineFromPs(line: string): AttachItem | undefined {
// Explanation of the regex:
// - any leading whitespace
// - PID
// - 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+(.{${secondColumnCharacters - 1}})\\s+(.*)$`);
const matches = psEntry.exec(line);
if (matches?.length === 4) {
const pid = matches[1].trim();
const executable = matches[2].trim();
const cmdline = matches[3].trim();
const attachItem: AttachItem = {
label: executable,
description: pid,
detail: cmdline,
id: pid,
processName: executable,
commandLine: cmdline
};
if (process.platform === 'linux') {
attachItem.executable = `/proc/${pid}/exe`;
}
return attachItem;
}
}