UNPKG

@vscode/windows-process-tree

Version:
201 lines 9.82 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); exports.getProcessTree = exports.getProcessCpuUsage = exports.getProcessList = exports.filterProcessList = exports.buildProcessTree = exports.ProcessDataFlag = void 0; const util_1 = require("util"); const native = process.platform === 'win32' ? require('../build/Release/windows_process_tree.node') : undefined; var ProcessDataFlag; (function (ProcessDataFlag) { ProcessDataFlag[ProcessDataFlag["None"] = 0] = "None"; ProcessDataFlag[ProcessDataFlag["Memory"] = 1] = "Memory"; ProcessDataFlag[ProcessDataFlag["CommandLine"] = 2] = "CommandLine"; })(ProcessDataFlag = exports.ProcessDataFlag || (exports.ProcessDataFlag = {})); // requestInProgress is used for any function that uses CreateToolhelp32Snapshot, as multiple calls // to this cannot be done at the same time. let requestInProgress = false; const globalRequestQueue = []; const MAX_FILTER_DEPTH = 10; /** * Construct tree of process infos and their children, returning the requested "root" node. * This is performed in a single iteration pass and allows reasonably efficient traversal. * * @param rootPid the pid of the "root" process to search for * @param processList the list of `IProcessInfo`s * @returns the `IProcessInfoNode` representing the root and all of its connected children */ function buildRawTree(rootPid, processList) { var _a, _b; var _c, _d; let root; // Map of pid to the array of immediate children. // Each array is shared by reference, such that: // forall n. Object.is(n.children, childrenOf[n.info.pid]) const childrenOf = {}; // Iterate over processList (once). // Add each process to children of its parent pid. // Note the process corresponding to `rootPid` when we see it. for (const info of processList) { const myChildren = ((_a = childrenOf[_c = info.pid]) !== null && _a !== void 0 ? _a : (childrenOf[_c] = [])); const mySiblings = ((_b = childrenOf[_d = info.ppid]) !== null && _b !== void 0 ? _b : (childrenOf[_d] = [])); const node = { info, children: myChildren }; mySiblings.push(node); if (root === undefined && info.pid === rootPid) { root = node; } } return root; } /** * Filters a list of processes to rootPid and its descendents and creates a tree * @param rootPid The process to use as the root * @param processList The list of processes * @param maxDepth The maximum depth to search */ function buildProcessTree(rootPid, processList, maxDepth = MAX_FILTER_DEPTH) { if (process.platform !== 'win32') { throw new Error('buildProcessTree is only implemented on Windows'); } const root = buildRawTree(rootPid, processList); if (root === undefined) { return undefined; } // This differs from the "raw" tree somewhat trivially. // • the properties are inlined/splatted // • the 'ppid' field is omitted // • the depth of the tree is limited by `maxDepth` const buildNode = ({ info: { pid, name, memory, commandLine }, children }, depth) => ({ pid, name, memory, commandLine, children: depth > 0 ? children.map(c => buildNode(c, depth - 1)) : [], }); return buildNode(root, maxDepth); } exports.buildProcessTree = buildProcessTree; /** * Filters processList to contain the process with rootPid and all of its descendants * @param rootPid The root pid * @param processList The list of all processes * @param maxDepth The maximum depth to search */ function filterProcessList(rootPid, processList, maxDepth = MAX_FILTER_DEPTH) { if (process.platform !== 'win32') { throw new Error('filterProcessList is only implemented on Windows'); } const root = buildRawTree(rootPid, processList); if (root === undefined) { return undefined; } if (maxDepth < 0) { return []; } function buildList({ info, children }, depth, accum) { accum.push(info); if (depth > 0) { children.forEach(c => buildList(c, depth - 1, accum)); } return accum; } return buildList(root, maxDepth, []); } exports.filterProcessList = filterProcessList; function getRawProcessList(callback, flags) { const queue = globalRequestQueue; queue.push(callback); // Only make a new request if there is not currently a request in progress. // This prevents too many requests from being made, there is also a crash that // can occur when performing multiple calls to CreateToolhelp32Snapshot at // once. if (!requestInProgress) { requestInProgress = true; native.getProcessList((processList) => { // It is possible and valid for one callback to cause another to be added to the queue. // To avoid orphaning those callbacks, we repeat the draining until the queue is empty. // We use "queue.splice(0)" to atomically clear the queue, returning the batch to process. // If any of those also made requests, we repeat until the callback chain completes. // // An alternative would be to splice the queue once and immediately reset requestInProgress // before invoking callbacks: `CreateToolhelp32Snapshot` has safely completed at this point. // However, that would circumvent the "too many requests" rate-limiting (?) concern above. while (queue.length) { queue.splice(0).forEach(cb => cb(processList)); } requestInProgress = false; }, flags || 0); } } /** * Returns a list of processes containing the rootPid process and all of its descendants * @param rootPid The pid of the process of interest * @param callback The callback to use with the returned set of processes * @param flags The flags for what process data should be included */ function getProcessList(rootPid, callback, flags) { if (process.platform !== 'win32') { throw new Error('getProcessList is only implemented on Windows'); } getRawProcessList(procs => callback(filterProcessList(rootPid, procs)), flags); } exports.getProcessList = getProcessList; (function (getProcessList) { // tslint:disable-next-line:variable-name getProcessList.__promisify__ = (rootPid, flags) => new Promise((resolve, reject) => { const callback = (processList) => processList ? resolve(processList) : reject(new Error(`Could not find PID ${rootPid}`)); getProcessList(rootPid, callback, flags); }); })(getProcessList = exports.getProcessList || (exports.getProcessList = {})); /** * Returns the list of processes annotated with cpu usage information * @param processList The list of processes * @param callback The callback to use with the returned list of processes */ function getProcessCpuUsage(processList, callback) { if (process.platform !== 'win32') { throw new Error('getProcessCpuUsage is only implemented on Windows'); } native.getProcessCpuUsage(processList, callback); } exports.getProcessCpuUsage = getProcessCpuUsage; (function (getProcessCpuUsage) { // tslint:disable-next-line:variable-name getProcessCpuUsage.__promisify__ = (processList) => new Promise((resolve, reject) => { // NOTE: Currently this callback is *never* called with `undefined`, unlike the other functions which do PID lookups. // The handling here is just for consistency and future-proofing. const callback = (cpuInfos) => cpuInfos ? resolve(cpuInfos) : reject(new Error('Failed to collect CPU info')); getProcessCpuUsage(processList, callback); }); })(getProcessCpuUsage = exports.getProcessCpuUsage || (exports.getProcessCpuUsage = {})); /** * Returns a tree of processes with rootPid as the root * @param rootPid The pid of the process that will be the root of the tree * @param callback The callback to use with the returned list of processes * @param flags Flags indicating what process data should be written on each node */ function getProcessTree(rootPid, callback, flags) { if (process.platform !== 'win32') { throw new Error('getProcessTree is only implemented on Windows'); } getRawProcessList(procs => callback(buildProcessTree(rootPid, procs)), flags); } exports.getProcessTree = getProcessTree; (function (getProcessTree) { // tslint:disable-next-line:variable-name getProcessTree.__promisify__ = (rootPid, flags) => new Promise((resolve, reject) => { const callback = (tree) => tree ? resolve(tree) : reject(new Error(`Could not find PID ${rootPid}`)); getProcessTree(rootPid, callback, flags); }); })(getProcessTree = exports.getProcessTree || (exports.getProcessTree = {})); // Since symbol properties can't be declared via namespace merging, we just define __promisify__ that way and // and manually set the "modern" promisify symbol: https://github.com/microsoft/TypeScript/issues/36813 [getProcessTree, getProcessList, getProcessCpuUsage].forEach(func => Object.defineProperty(func, util_1.promisify.custom, { enumerable: false, value: func.__promisify__ })); //# sourceMappingURL=index.js.map