From 0013a0dcb4071bf07f33bc2560f0f54c5d89540e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andre=CC=81=20Weinand?= Date: Sat, 17 Mar 2018 19:05:28 +0100 Subject: [PATCH] process picker overhaul for Microsoft/vscode/issues/42521 --- package.json | 10 +- package.nls.json | 1 + src/node/extension/autoAttach.ts | 6 +- .../{childProcesses.ts => cluster.ts} | 52 ++-- src/node/extension/configurationProvider.ts | 97 +------- src/node/extension/extension.ts | 20 +- src/node/extension/processPicker.ts | 235 +++++++++++++----- src/node/extension/processTree.ts | 34 ++- 8 files changed, 249 insertions(+), 206 deletions(-) rename src/node/extension/{childProcesses.ts => cluster.ts} (52%) diff --git a/package.json b/package.json index 4861209f..df7f790c 100644 --- a/package.json +++ b/package.json @@ -81,10 +81,11 @@ "activationEvents": [ "onDebugInitialConfigurations", "onDebugResolve:node", + "onCommand:extension.pickNodeProcess", "onCommand:extension.node-debug.toggleSkippingFile", "onCommand:extension.node-debug.pickLoadedScript", - "onCommand:extension.pickNodeProcess", - "onCommand:extension.node-debug.toggleAutoAttach" + "onCommand:extension.node-debug.toggleAutoAttach", + "onCommand:extension.node-debug.attachNodeProcess" ], "contributes": { "views": { @@ -111,6 +112,11 @@ "title": "%open.loaded.script%", "category": "Debug" }, + { + "command": "extension.node-debug.attachNodeProcess", + "title": "%attach.node.process%", + "category": "Debug" + }, { "command": "extension.node-debug.toggleSkippingFile", "title": "%toggle.skipping.this.file%" diff --git a/package.nls.json b/package.nls.json index 7b7d99f9..93e03199 100644 --- a/package.nls.json +++ b/package.nls.json @@ -4,6 +4,7 @@ "node.label": "Node.js", "open.loaded.script": "Open Loaded Script", + "attach.node.process": "Attach to Node Process", "toggle.skipping.this.file": "Toggle Skipping this File", "loaded.scripts.view.name": "Loaded Scripts", diff --git a/src/node/extension/autoAttach.ts b/src/node/extension/autoAttach.ts index 6ff4a6a1..6ddc245b 100644 --- a/src/node/extension/autoAttach.ts +++ b/src/node/extension/autoAttach.ts @@ -11,13 +11,11 @@ import { pollProcesses, attachToProcess } from './nodeProcessTree'; const localize = nls.loadMessageBundle(); -export function startAutoAttach() : vscode.Disposable { - - const rootPid = parseInt(process.env['VSCODE_PID']); +export function startAutoAttach(rootPid: number) : vscode.Disposable { return pollProcesses(rootPid, (pid, cmd) => { if (cmd.indexOf('node ') >= 0) { - const name = localize('processWithPid', "Process {0}", pid); + const name = localize('process.with.pid.label', "Process {0}", pid); attachToProcess(undefined, name, pid, cmd); } }); diff --git a/src/node/extension/childProcesses.ts b/src/node/extension/cluster.ts similarity index 52% rename from src/node/extension/childProcesses.ts rename to src/node/extension/cluster.ts index 427bdf8c..a3df4310 100644 --- a/src/node/extension/childProcesses.ts +++ b/src/node/extension/cluster.ts @@ -11,40 +11,36 @@ import { pollProcesses, attachToProcess } from './nodeProcessTree'; const localize = nls.loadMessageBundle(); -const clusters = new Map(); +export class Cluster { -export function prepareAutoAttachChildProcesses(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration) { - clusters.set(config.name, new Cluster(folder, config)); -} + static clusters = new Map(); -export function startSession(session: vscode.DebugSession) { - const cluster = clusters.get(session.name); - if (cluster) { - cluster.startWatching(session); - } -} + private poller?: vscode.Disposable; -export function stopSession(session: vscode.DebugSession) { - const cluster = clusters.get(session.name); - if (cluster) { - cluster.stopWatching(); - clusters.delete(session.name); + + public static prepareAutoAttachChildProcesses(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration) { + this.clusters.set(config.name, new Cluster(folder, config)); } -} -//---- private + static startSession(session: vscode.DebugSession) { + const cluster = this.clusters.get(session.name); + if (cluster) { + cluster.startWatching(session); + } + } -class Cluster { - folder: vscode.WorkspaceFolder | undefined; - config: vscode.DebugConfiguration; - poller?: vscode.Disposable; + static stopSession(session: vscode.DebugSession) { + const cluster = this.clusters.get(session.name); + if (cluster) { + cluster.stopWatching(); + this.clusters.delete(session.name); + } + } - constructor(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration) { - this.folder = folder; - this.config = config; + private constructor(private _folder: vscode.WorkspaceFolder | undefined, private _config: vscode.DebugConfiguration) { } - startWatching(session: vscode.DebugSession) { + private startWatching(session: vscode.DebugSession) { setTimeout(_ => { // get the process ID from the debuggee @@ -60,7 +56,7 @@ class Cluster { }, session.type === 'node2' ? 500 : 100); } - stopWatching() { + private stopWatching() { if (this.poller) { this.poller.dispose(); this.poller = undefined; @@ -69,8 +65,8 @@ class Cluster { private attachChildProcesses(rootPid: number) { this.poller = pollProcesses(rootPid, (pid, cmd) => { - const name = localize('childProcessWithPid', "Child Process {0}", pid); - attachToProcess(this.folder, name, pid, cmd, this.config); + const name = localize('child.process.with.pid.label', "Child Process {0}", pid); + attachToProcess(this._folder, name, pid, cmd, this._config); }); } } diff --git a/src/node/extension/configurationProvider.ts b/src/node/extension/configurationProvider.ts index a286d91b..2ee78e2e 100644 --- a/src/node/extension/configurationProvider.ts +++ b/src/node/extension/configurationProvider.ts @@ -6,14 +6,13 @@ import * as nls from 'vscode-nls'; import * as vscode from 'vscode'; -import { execSync } from 'child_process'; import { join, isAbsolute, dirname } from 'path'; import * as fs from 'fs'; import { writeToConsole } from './utilities'; -import { detectDebugType, detectProtocolForPid, INSPECTOR_PORT_DEFAULT, LEGACY_PORT_DEFAULT } from './protocolDetection'; -import { pickProcess } from './processPicker'; -import { prepareAutoAttachChildProcesses } from './childProcesses'; +import { detectDebugType } from './protocolDetection'; +import { pickProcessForConfig } from './processPicker'; +import { Cluster } from './cluster'; const localize = nls.loadMessageBundle(); @@ -21,9 +20,8 @@ const localize = nls.loadMessageBundle(); export class NodeConfigurationProvider implements vscode.DebugConfigurationProvider { - constructor( - private _extensionContext: vscode.ExtensionContext - ) { } + constructor(private _extensionContext: vscode.ExtensionContext) { + } /** * Returns an initial debug configuration based on contextual information, e.g. package.json or folder. @@ -103,14 +101,17 @@ export class NodeConfigurationProvider implements vscode.DebugConfigurationProvi // "auto attach child process" support if (config.autoAttachChildProcesses) { - prepareAutoAttachChildProcesses(folder, config); + Cluster.prepareAutoAttachChildProcesses(folder, config); } - // "attach to process via pid" support + // "attach to process via picker" support if (config.request === 'attach' && typeof config.processId === 'string') { - // we resolve Process Picker early (before VS Code) so that we can probe the process for its protocol here - if (await this.resolveProcessPicker(config)) { - return undefined; // abort launch + // we resolve Process Picker early (before VS Code) so that we can probe the process for its protocol + const processId = config.processId.trim(); + if (processId === '${command:PickProcess}' || processId === '${command:extension.pickNodeProcess}') { + if (await pickProcessForConfig(config)) { + return undefined; // abort launch + } } } @@ -130,42 +131,6 @@ export class NodeConfigurationProvider implements vscode.DebugConfigurationProvi return config; } - /** - * returns true if UI was cancelled - */ - private async resolveProcessPicker(config: vscode.DebugConfiguration) : Promise { - - const processId = config.processId.trim(); - if (processId === '${command:PickProcess}' || processId === '${command:extension.pickNodeProcess}') { - - const pidResult = await pickProcess(); - if (pidResult === null) { - // UI dismissed (cancelled) - return true; - } - - if (pidResult && pidResult.match(/^[0-9]+$/)) { - const pid = Number(pidResult); - - putPidInDebugMode(pid); - - const debugType = await determineDebugTypeForPidInDebugMode(config, pid); - if (debugType) { - // processID is handled, so turn this config into a normal port attach configuration - delete config.processId; - config.port = debugType === 'node2' ? INSPECTOR_PORT_DEFAULT : LEGACY_PORT_DEFAULT; - config.protocol = debugType === 'node2' ? 'inspector' : 'legacy'; - } else { - throw new Error(localize('pid.error', "Attach to process: cannot put process '{0}' in debug mode.", pidResult)); - } - } else { - throw new Error(localize('VSND2006', "Attach to process: '{0}' doesn't look like a process id.", pidResult)); - } - } - - return false; - } - /** * if a runtime version is specified we prepend env.PATH with the folder that corresponds to the version. * Returns false on error @@ -408,42 +373,6 @@ function determineDebugType(config: any, logger: vscode.Logger): Promiseprocess)._debugProcess(pid); - // But since we are running on Electron's node, process._debugProcess doesn't work (for unknown reasons). - // So we use a regular node instead: - const command = `node -e process._debugProcess(${pid})`; - execSync(command); - } else { - process.kill(pid, 'SIGUSR1'); - } - } catch (e) { - throw new Error(localize('VSND2021', "Attach to process: cannot enable debug mode for process '{0}' ({1}).", pid, e)); - } -} - -function determineDebugTypeForPidInDebugMode(config: any, pid: number): Promise { - let debugProtocolP: Promise; - if (config.port === INSPECTOR_PORT_DEFAULT) { - debugProtocolP = Promise.resolve('inspector'); - } else if (config.port === LEGACY_PORT_DEFAULT) { - debugProtocolP = Promise.resolve('legacy'); - } else if (config.protocol) { - debugProtocolP = Promise.resolve(config.protocol); - } else { - debugProtocolP = detectProtocolForPid(pid); - } - - return debugProtocolP.then(debugProtocol => { - return debugProtocol === 'inspector' ? 'node2' : - debugProtocol === 'legacy' ? 'node' : - null; - }); -} - function nvsStandardArchName(arch) { switch (arch) { case '32': diff --git a/src/node/extension/extension.ts b/src/node/extension/extension.ts index bc1be550..55b8d5ed 100644 --- a/src/node/extension/extension.ts +++ b/src/node/extension/extension.ts @@ -8,8 +8,8 @@ import * as vscode from 'vscode'; import { NodeConfigurationProvider } from './configurationProvider'; import { LoadedScriptsProvider, pickLoadedScript, openScript } from './loadedScripts'; -import { pickProcess } from './processPicker'; -import { startSession, stopSession } from './childProcesses'; +import { pickProcess, attachProcess } from './processPicker'; +import { Cluster } from './cluster'; import { startAutoAttach } from './autoAttach'; import * as nls from 'vscode-nls'; @@ -23,17 +23,20 @@ export function activate(context: vscode.ExtensionContext) { // toggle skipping file action context.subscriptions.push(vscode.commands.registerCommand('extension.node-debug.toggleSkippingFile', toggleSkippingFile)); - // process quickpicker - context.subscriptions.push(vscode.commands.registerCommand('extension.pickNodeProcess', () => pickProcess())); + // process picker command + context.subscriptions.push(vscode.commands.registerCommand('extension.pickNodeProcess', pickProcess)); + + // attach process command + context.subscriptions.push(vscode.commands.registerCommand('extension.node-debug.attachNodeProcess', attachProcess)); // loaded scripts vscode.window.registerTreeDataProvider('extension.node-debug.loadedScriptsExplorer', new LoadedScriptsProvider(context)); - context.subscriptions.push(vscode.commands.registerCommand('extension.node-debug.pickLoadedScript', () => pickLoadedScript())); + context.subscriptions.push(vscode.commands.registerCommand('extension.node-debug.pickLoadedScript', pickLoadedScript)); context.subscriptions.push(vscode.commands.registerCommand('extension.node-debug.openScript', (session: vscode.DebugSession, source) => openScript(session, source))); // cluster - context.subscriptions.push(vscode.debug.onDidStartDebugSession(session => startSession(session))); - context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(session => stopSession(session))); + context.subscriptions.push(vscode.debug.onDidStartDebugSession(session => Cluster.startSession(session))); + context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(session => Cluster.stopSession(session))); // auto attach in terminal const statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); @@ -43,6 +46,7 @@ export function activate(context: vscode.ExtensionContext) { statusItem.show(); context.subscriptions.push(statusItem); + const rootPid = parseInt(process.env['VSCODE_PID']); let autoAttacher: vscode.Disposable | undefined; context.subscriptions.push(vscode.commands.registerCommand('extension.node-debug.toggleAutoAttach', _ => { const icon = '$(plug) '; @@ -52,7 +56,7 @@ export function activate(context: vscode.ExtensionContext) { autoAttacher = undefined; } else { statusItem.text = icon + statusItem.text; - autoAttacher = startAutoAttach(); + autoAttacher = startAutoAttach(rootPid); } })); } diff --git a/src/node/extension/processPicker.ts b/src/node/extension/processPicker.ts index 8e7402c5..8c37d40f 100644 --- a/src/node/extension/processPicker.ts +++ b/src/node/extension/processPicker.ts @@ -8,100 +8,213 @@ import * as nls from 'vscode-nls'; import * as vscode from 'vscode'; import { basename } from 'path'; import { getProcesses } from './processTree'; +import { execSync } from 'child_process'; +import { detectProtocolForPid, INSPECTOR_PORT_DEFAULT, LEGACY_PORT_DEFAULT } from './protocolDetection'; const localize = nls.loadMessageBundle(); //---- extension.pickNodeProcess interface ProcessItem extends vscode.QuickPickItem { - pid: string; // payload for the QuickPick UI + pidOrPort: string; // picker result + pid: number; // used for sorting } -export function pickProcess(): Promise { - return listProcesses().then(items => { - let options : vscode.QuickPickOptions = { +/** + * end user action for picking a process and attaching debugger to it + */ +export async function attachProcess() { + + const config = { + type: 'node', + request: 'attach', + name: 'process' + }; + + if (!await pickProcessForConfig(config)) { + return vscode.debug.startDebugging(undefined, config); + } + return undefined; +} + +/** + * returns true if UI was cancelled + */ +export async function pickProcessForConfig(config: vscode.DebugConfiguration) : Promise { + + const pidResult = await pickProcess(true); // ask for pids and ports! + if (!pidResult) { + // UI dismissed (cancelled) + return true; + } + const matches = /^(inspector|legacy)?([0-9]+)$/.exec(pidResult); + if (matches && matches.length === 3) { + + if (matches[1]) { + + // debug port + config.port = Number(matches[2]); + config.protocol = matches[1]; + delete config.processId; + + } else { + + // process id + const pid = Number(matches[2]); + putPidInDebugMode(pid); + + const debugType = await determineDebugTypeForPidInDebugMode(config, pid); + if (debugType) { + // processID is handled, so turn this config into a normal port attach configuration + delete config.processId; + config.port = debugType === 'node2' ? INSPECTOR_PORT_DEFAULT : LEGACY_PORT_DEFAULT; + config.protocol = debugType === 'node2' ? 'inspector' : 'legacy'; + } else { + throw new Error(localize('pid.error', "Attach to process: cannot put process '{0}' in debug mode.", pidResult)); + } + } + + } else { + throw new Error(localize('VSND2006', "Attach to process: '{0}' doesn't look like a process id.", pidResult)); + } + + return false; +} + +/** + * Process picker command (for launch config variable) + * Returns as a string with these formats: + * - "12345": process id + * - "inspector12345": port number and inspector protocol + * - "legacy12345": port number and legacy protocol + * - null: abort launch silently + */ +export function pickProcess(ports?): Promise { + + return listProcesses(ports).then(items => { + let options: vscode.QuickPickOptions = { placeHolder: localize('pickNodeProcess', "Pick the node.js or gulp process to attach to"), matchOnDescription: true, matchOnDetail: true, ignoreFocusOut: true }; - return vscode.window.showQuickPick(items, options).then(item => item ? item.pid : null); + return vscode.window.showQuickPick(items, options).then(item => item ? item.pidOrPort : null); }).catch(err => { return vscode.window.showErrorMessage(localize('process.picker.error', "Process picker failed ({0})", err.message), { modal: true }).then(_ => null); }); } -function listProcesses() : Promise { +//---- private - const NODE = new RegExp('^(?:node|iojs|gulp)$', 'i'); +function listProcesses(ports: boolean): Promise { - const items : ProcessItem[]= []; - let promise : Promise; + const items: ProcessItem[] = []; - if (process.platform === 'win32') { + const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/; + const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk)?(=(\d+))?/; - const EXECUTABLE_ARGS = new RegExp('^(?:"([^"]+)"|([^ ]+))(?: (.+))?$'); + const NODE = new RegExp('^(?:node|iojs)$', 'i'); - promise = getProcesses((pid: number, ppid: number, cmd: string) => { + return getProcesses((pid: number, ppid: number, command: string, args: string) => { + if (process.platform === 'win32' && command.indexOf('\\??\\') === 0) { // remove leading device specifier - if (cmd.indexOf('\\??\\') === 0) { - cmd = cmd.replace('\\??\\', ''); - } + command = command.replace('\\??\\', ''); + } - let executable_path: string | undefined; - const matches2 = EXECUTABLE_ARGS.exec(cmd); - if (matches2 && matches2.length >= 2) { - if (matches2.length >= 3) { - executable_path = matches2[1] || matches2[2]; - } else { - executable_path = matches2[1]; - } - } + const executable_name = basename(command); - if (executable_path) { - - let executable_name = basename(executable_path); - executable_name = executable_name.split('.')[0]; - if (NODE.test(executable_name)) { - items.push({ - label: executable_name, - description: pid.toString(), - detail: cmd, - pid: pid.toString() - }); + let port = -1; + let protocol = ''; + + if (ports) { + // match --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect, --inspect=1234, --inspect-brk, --inspect-brk=1234 + let matches = DEBUG_FLAGS_PATTERN.exec(args); + if (matches && matches.length >= 2) { + // attach via port + if (matches.length === 5 && matches[4]) { + port = parseInt(matches[4]); } + protocol = matches[1] === 'debug' ? 'legacy' : 'inspector'; } - }); - - } else { - const MAC_APPS = new RegExp('^.*/(.*).(?:app|bundle)/Contents/.*$'); - - promise = getProcesses((pid: number, ppid: number, cmd: string) => { - const parts = cmd.split(' '); // this will break paths with spaces - const executable_path = parts[0]; - const executable_name = basename(executable_path); + // a debug-port=1234 or --inspect-port=1234 overrides the port + matches = DEBUG_PORT_PATTERN.exec(args); + if (matches && matches.length === 3) { + // override port + port = parseInt(matches[2]); + protocol = matches[1] === 'debug' ? 'legacy' : 'inspector'; + } + } + let description = ''; + let pidOrPort = ''; + if (protocol) { + if (port < 0) { + port = protocol === 'inspector' ? INSPECTOR_PORT_DEFAULT : LEGACY_PORT_DEFAULT; + } + if (protocol === 'inspector') { + description = `Debug Port: ${port}`; + } else { + description = `Debug Port: ${port} (legacy protocol)`; + } + pidOrPort = `${protocol}${port}`; + } else { if (NODE.test(executable_name)) { - let application = cmd; - // try to show the correct name for OS X applications and bundles - const matches2 = MAC_APPS.exec(cmd); - if (matches2 && matches2.length === 2) { - application = matches2[1]; - } else { - application = executable_name; - } - - items.unshift({ // build up list reverted - label: application, - description: pid.toString(), - detail: cmd, - pid: pid.toString() - }); + description = `Process Id: ${pid}`; + pidOrPort = pid.toString(); } - }); + } + + if (description && pidOrPort) { + items.push({ + // render data + label: executable_name, + description: args, + detail: description, + + // picker result + pidOrPort: pidOrPort, + // sort key + pid: pid + }); + } + + }).then(() => items.sort((a, b) => b.pid - a.pid)); // sort items by process id, newest first +} + +function putPidInDebugMode(pid: number): void { + try { + if (process.platform === 'win32') { + // regular node has an undocumented API function for forcing another node process into debug mode. + // (process)._debugProcess(pid); + // But since we are running on Electron's node, process._debugProcess doesn't work (for unknown reasons). + // So we use a regular node instead: + const command = `node -e process._debugProcess(${pid})`; + execSync(command); + } else { + process.kill(pid, 'SIGUSR1'); + } + } catch (e) { + throw new Error(localize('VSND2021', "Attach to process: cannot enable debug mode for process '{0}' ({1}).", pid, e)); + } +} + +function determineDebugTypeForPidInDebugMode(config: any, pid: number): Promise { + let debugProtocolP: Promise; + if (config.port === INSPECTOR_PORT_DEFAULT) { + debugProtocolP = Promise.resolve('inspector'); + } else if (config.port === LEGACY_PORT_DEFAULT) { + debugProtocolP = Promise.resolve('legacy'); + } else if (config.protocol) { + debugProtocolP = Promise.resolve(config.protocol); + } else { + debugProtocolP = detectProtocolForPid(pid); } - return promise.then(() => items); + return debugProtocolP.then(debugProtocol => { + return debugProtocol === 'inspector' ? 'node2' : + debugProtocol === 'legacy' ? 'node' : + null; + }); } diff --git a/src/node/extension/processTree.ts b/src/node/extension/processTree.ts index f6291e08..e4d7f25a 100644 --- a/src/node/extension/processTree.ts +++ b/src/node/extension/processTree.ts @@ -9,15 +9,9 @@ import { spawn, ChildProcess } from 'child_process'; import { join } from 'path'; export class ProcessTreeNode { - pid: number; - ppid: number; children: ProcessTreeNode[]; - args: string; - constructor(pid: number, ppid: number, args: string) { - this.pid = pid; - this.ppid = ppid; - this.args = args; + constructor(public pid: number, public ppid: number, public command: string, public args: string) { } } @@ -25,11 +19,11 @@ export async function getProcessTree(rootPid: number) : Promise(); - map.set(0, new ProcessTreeNode(0, 0, '')); + map.set(0, new ProcessTreeNode(0, 0, '???', '')); try { - await getProcesses((pid: number, ppid: number, cmd: string) => { - map.set(pid, new ProcessTreeNode(pid, ppid, cmd)); + await getProcesses((pid: number, ppid: number, command: string, args: string) => { + map.set(pid, new ProcessTreeNode(pid, ppid, command, args)); }); } catch (err) { return undefined; @@ -52,7 +46,7 @@ export async function getProcessTree(rootPid: number) : Promise void) : Promise { +export function getProcesses(one: (pid: number, ppid: number, command: string, args: string) => void) : Promise { // returns a function that aggregates chunks of data until one or more complete lines are received and passes them to a callback. function lines(callback: (a: string) => void) { @@ -83,21 +77,23 @@ export function getProcesses(one: (pid: number, ppid: number, cmdline: string) = let matches = CMD_PAT.exec(line.trim()); if (matches && matches.length === 4) { const pid = parseInt(matches[3]); - one(pid, parseInt(matches[2]), matches[1].trim()); + one(pid, parseInt(matches[2]), matches[1].trim(), matches[1].trim()); } })); } else { // OS X & Linux - const CMD_PAT = /^\s*([0-9]+)\s+([0-9]+)\s+(.+)$/; - - proc = spawn('/bin/ps', [ '-ax', '-o', 'pid=,ppid=,command=' ]); + proc = spawn('/bin/ps', [ '-x', '-o', `pid,ppid,comm=${'a'.repeat(256)},command` ]); proc.stdout.setEncoding('utf8'); proc.stdout.on('data', lines(line => { - let matches = CMD_PAT.exec(line.trim()); - if (matches && matches.length === 4) { - const pid = parseInt(matches[1]); - one(pid, parseInt(matches[2]), matches[3]); + + const pid = Number(line.substr(0, 5)); + const ppid = Number(line.substr(6, 5)); + const command = line.substr(12, 256).trim(); + const args = line.substr(269 + command.length); + + if (!isNaN(pid) && !isNaN(ppid)) { + one(pid, ppid, command, args); } })); }