| /*--------------------------------------------------------- | |
| * 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; | |
| } | |
| } |