From d8e061b4874f72c6139f500913f473a69c4ff64d Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 10:17:41 +0800 Subject: [PATCH 01/11] fix(wmic): add gwmi command to be compatible with deleted wmic --- lib/gwmi.js | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/stats.js | 10 +++++ 2 files changed, 132 insertions(+) create mode 100644 lib/gwmi.js diff --git a/lib/gwmi.js b/lib/gwmi.js new file mode 100644 index 0000000..8c30d39 --- /dev/null +++ b/lib/gwmi.js @@ -0,0 +1,122 @@ +'use strict' + +const os = require('os') +const bin = require('./bin') +const history = require('./history') + +function parseDate (datestr) { + const year = datestr.substring(0, 4) + const month = datestr.substring(4, 6) + const day = datestr.substring(6, 8) + const hour = datestr.substring(8, 10) + const minutes = datestr.substring(10, 12) + const seconds = datestr.substring(12, 14) + const useconds = datestr.substring(15, 21) + const sign = datestr.substring(21, 22) + const tmz = parseInt(datestr.substring(22, 25), 10) + const tmzh = Math.floor(tmz / 60) + const tmzm = tmz % 60 + + return new Date( + year + '-' + month + '-' + day + 'T' + hour + + ':' + minutes + ':' + seconds + + '.' + useconds + + sign + (tmzh > 9 ? tmzh : '0' + tmzh) + '' + (tmzm > 9 ? tmzm : '0' + tmzm) + ) +} + +function gwmi (pids, options, done) { + let whereClause = 'ProcessId=' + pids[0] + for (let i = 1; i < pids.length; i++) { + whereClause += ' or ' + 'ProcessId=' + pids[i] + } + + const property = 'CreationDate,KernelModeTime,ParentProcessId,ProcessId,UserModeTime,WorkingSetSize' + const args = ['win32_process', '-Filter', '\'' + whereClause + '\'', '| select ' + property, '| format-table'] + + bin('gwmi', args, { windowsHide: true, windowsVerbatimArguments: true, shell: 'powershell' }, function (err, stdout, code) { + if (err) { + if (err.message.indexOf('No Instance(s) Available.') !== -1) { + const error = new Error('No matching pid found') + error.code = 'ENOENT' + return done(error) + } + return done(err) + } + if (code !== 0) { + return done(new Error('pidusage gwmi command exited with code ' + code)) + } + const date = Date.now() + + // Note: On Windows the returned value includes fractions of a second. + // Use Math.floor() to get whole seconds. + // Fallback on current date when uptime is not allowed (see https://github.com/soyuka/pidusage/pull/130) + const uptime = Math.floor(os.uptime() || (date / 1000)) + + // Example of stdout on Windows 10 + // CreationDate: is in the format yyyymmddHHMMSS.mmmmmmsUUU + // KernelModeTime: is in units of 100 ns + // UserModeTime: is in units of 100 ns + // WorkingSetSize: is in bytes + // + // Refs: https://superuser.com/a/937401/470946 + // Refs: https://msdn.microsoft.com/en-us/library/aa394372(v=vs.85).aspx + // NB: The columns are returned in lexicographical order + // + // CreationDate KernelModeTime ParentProcessId ProcessId UserModeTime WorkingSetSize + // 20150329221650.080654+060 153750000 0 777 8556250000 110821376 + + stdout = stdout.split(os.EOL).slice(3) + + let again = false + const statistics = {} + for (let i = 1; i < stdout.length; i++) { + const line = stdout[i].trim().split(/\s+/) + + if (!line || line.length === 1) { + continue + } + + const creation = parseDate(line[0]) + const ppid = parseInt(line[2], 10) + const pid = parseInt(line[3], 10) + const kerneltime = Math.round(parseInt(line[1], 10) / 10000) + const usertime = Math.round(parseInt(line[4], 10) / 10000) + const memory = parseInt(line[5], 10) + + let hst = history.get(pid, options.maxage) + if (hst === undefined) { + again = true + hst = { ctime: kerneltime + usertime, uptime: uptime } + } + + // process usage since last call + const total = (kerneltime + usertime - hst.ctime) / 1000 + // time elapsed between calls in seconds + const seconds = uptime - hst.uptime + const cpu = seconds > 0 ? (total / seconds) * 100 : 0 + + history.set(pid, { ctime: usertime + kerneltime, uptime: uptime }, options.maxage) + + statistics[pid] = { + cpu: cpu, + memory: memory, + ppid: ppid, + pid: pid, + ctime: usertime + kerneltime, + elapsed: date - creation.getTime(), + timestamp: date + } + } + + if (again) { + return gwmi(pids, options, function (err, stats) { + if (err) return done(err) + done(null, Object.assign(statistics, stats)) + }) + } + done(null, statistics) + }) +} + +module.exports = gwmi diff --git a/lib/stats.js b/lib/stats.js index b9f83d6..2afc73f 100644 --- a/lib/stats.js +++ b/lib/stats.js @@ -2,6 +2,7 @@ const fs = require('fs') const os = require('os') +const spawn = require('child_process').spawn const platformToMethod = { aix: 'ps', @@ -50,6 +51,15 @@ function get (pids, options, callback) { if (platform !== 'win' && options.usePs === true) { fn = ps } + if (platform === 'win') { + try { + spawn('wmic', function (err) { + if (err) throw new Error(err) + }) + } catch (err) { + fn = require('./gwmi') + } + } if (stat === undefined) { return callback(new Error(os.platform() + ' is not supported yet, please open an issue (https://github.com/soyuka/pidusage)')) From e60fe06907a83461e201c6dbc47648ce4a355e5e Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 10:21:48 +0800 Subject: [PATCH 02/11] add package-lock.json --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index 6db03b8..8773872 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "pidusage", "version": "3.0.0", "license": "MIT", "dependencies": { From 9fa2dced24c0f0b2f8b904323778a74e97f6a3e7 Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 10:44:55 +0800 Subject: [PATCH 03/11] lint format --- lib/gwmi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gwmi.js b/lib/gwmi.js index 8c30d39..db1fda1 100644 --- a/lib/gwmi.js +++ b/lib/gwmi.js @@ -108,7 +108,7 @@ function gwmi (pids, options, done) { timestamp: date } } - + if (again) { return gwmi(pids, options, function (err, stats) { if (err) return done(err) From 39037f1aa6fded5a1bbc84c6d3dc490af2bd3802 Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 12:13:10 +0800 Subject: [PATCH 04/11] add gwmi test file --- lib/gwmi.js | 8 +++++- test/gwmi.js | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/gwmi.js diff --git a/lib/gwmi.js b/lib/gwmi.js index db1fda1..eb66026 100644 --- a/lib/gwmi.js +++ b/lib/gwmi.js @@ -34,7 +34,7 @@ function gwmi (pids, options, done) { const property = 'CreationDate,KernelModeTime,ParentProcessId,ProcessId,UserModeTime,WorkingSetSize' const args = ['win32_process', '-Filter', '\'' + whereClause + '\'', '| select ' + property, '| format-table'] - bin('gwmi', args, { windowsHide: true, windowsVerbatimArguments: true, shell: 'powershell' }, function (err, stdout, code) { + bin('gwmi', args, { windowsHide: true, windowsVerbatimArguments: true, shell: 'powershell.exe' }, function (err, stdout, code) { if (err) { if (err.message.indexOf('No Instance(s) Available.') !== -1) { const error = new Error('No matching pid found') @@ -68,6 +68,12 @@ function gwmi (pids, options, done) { stdout = stdout.split(os.EOL).slice(3) + if (!stdout.length) { + const error = new Error('No matching pid found') + error.code = 'ENOENT' + return done(error) + } + let again = false const statistics = {} for (let i = 1; i < stdout.length; i++) { diff --git a/test/gwmi.js b/test/gwmi.js new file mode 100644 index 0000000..500acf1 --- /dev/null +++ b/test/gwmi.js @@ -0,0 +1,76 @@ +const mockery = require('mockery') +const test = require('ava') +const os = require('os') +const mockdate = require('mockdate') +const pify = require('pify') + +const mocks = require('./helpers/_mocks') + +const timeout = ms => new Promise((resolve, reject) => setTimeout(resolve, ms)) + +test.before(() => { + mockery.enable({ + warnOnReplace: false, + warnOnUnregistered: false, + useCleanCache: true + }) + mockdate.set(new Date(1427749200000)) +}) + +test.beforeEach(() => { + mockery.resetCache() +}) + +test.after(() => { + mockery.disable() + mockdate.reset() +}) + +test('should parse gwmi output on Windows', async t => { + const stdout = '' + + 'Active code page: 936' + os.EOL + + '' + os.EOL + + '' + os.EOL + + 'CreationDate KernelModeTime ParentProcessId ProcessId UserModeTime WorkingSetSize' + os.EOL + + '20150329221650.080654+060 153750000 0 777 8556250000 110821376' + + let calls = 0 + + mockery.registerMock('child_process', { + spawn: () => { + calls++ + return mocks.spawn(stdout, '', null, 0, null) + } + }) + + const gwmi = require('../lib/gwmi') + + let result = await pify(gwmi)([6456], { maxage: 1000 }) + t.deepEqual(result, { + 777: { + cpu: 0, + memory: 110821376, + ppid: 0, + pid: 777, + ctime: (855625 + 15375), + elapsed: 1427749200000 - new Date('2015-03-29T22:16:50.080654+0100').getTime(), + timestamp: 1427749200000 + } + }) + + result = await pify(gwmi)([6456], { maxage: 1000 }) + + t.is(calls, 3, '2 first calls to put in history + 1') + + mockdate.set(new Date(1427749202000)) + + // wait 1 second, it should do 2 calls again + await timeout(1000) + + calls = 0 + result = await pify(gwmi)([6456], { maxage: 1000 }) + + t.is(calls, 2, '2 first calls') + + mockery.deregisterMock('child_process') +}) From eb98d939c3426cbc56ac1e6ad01f7850b6167ace Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 12:54:39 +0800 Subject: [PATCH 05/11] ci: test --- test/integration.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration.js b/test/integration.js index cebab56..79180f3 100644 --- a/test/integration.js +++ b/test/integration.js @@ -8,6 +8,8 @@ const m = require('..') test('should work with a single pid', async t => { const pid = process.pid + t.log('pid') + t.log(pid) const result = await m(pid) t.log(result) From 4a38b1e0aee798026c9cee3f05df21a7cfa178d7 Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 13:03:10 +0800 Subject: [PATCH 06/11] ci: test 2 --- index.js | 1 + lib/stats.js | 3 +++ test/integration.js | 2 -- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 3115bbe..8a6099c 100644 --- a/index.js +++ b/index.js @@ -32,6 +32,7 @@ function pidusage (pids, options, callback) { } return new Promise(function (resolve, reject) { + console.log('pidusage promise - ' + pids) stats(pids, options, function (err, data) { if (err) return reject(err) resolve(data) diff --git a/lib/stats.js b/lib/stats.js index 2afc73f..d377ff4 100644 --- a/lib/stats.js +++ b/lib/stats.js @@ -47,6 +47,8 @@ try { * @param {pidCallback} callback Called when the statistics are ready. */ function get (pids, options, callback) { + console.log('get pids - ' + pids) + console.log('get platform - ' + platform) let fn = stat if (platform !== 'win' && options.usePs === true) { fn = ps @@ -57,6 +59,7 @@ function get (pids, options, callback) { if (err) throw new Error(err) }) } catch (err) { + console.log('get err - ' + platform); fn = require('./gwmi') } } diff --git a/test/integration.js b/test/integration.js index 79180f3..cebab56 100644 --- a/test/integration.js +++ b/test/integration.js @@ -8,8 +8,6 @@ const m = require('..') test('should work with a single pid', async t => { const pid = process.pid - t.log('pid') - t.log(pid) const result = await m(pid) t.log(result) From 95e0e7084e5fd4489c0f90ac1f64abe506186222 Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 13:22:25 +0800 Subject: [PATCH 07/11] ci: test 3 --- index.js | 1 - lib/stats.js | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 8a6099c..3115bbe 100644 --- a/index.js +++ b/index.js @@ -32,7 +32,6 @@ function pidusage (pids, options, callback) { } return new Promise(function (resolve, reject) { - console.log('pidusage promise - ' + pids) stats(pids, options, function (err, data) { if (err) return reject(err) resolve(data) diff --git a/lib/stats.js b/lib/stats.js index d377ff4..92c3701 100644 --- a/lib/stats.js +++ b/lib/stats.js @@ -18,6 +18,7 @@ const platformToMethod = { } const ps = require('./ps') +const gwmi = require('./gwmi') let platform = os.platform() if (fs.existsSync('/etc/alpine-release')) { @@ -47,8 +48,6 @@ try { * @param {pidCallback} callback Called when the statistics are ready. */ function get (pids, options, callback) { - console.log('get pids - ' + pids) - console.log('get platform - ' + platform) let fn = stat if (platform !== 'win' && options.usePs === true) { fn = ps @@ -59,8 +58,7 @@ function get (pids, options, callback) { if (err) throw new Error(err) }) } catch (err) { - console.log('get err - ' + platform); - fn = require('./gwmi') + fn = gwmi } } From d215cf8e0224826651ed200790f73dcb4e7e204e Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 13:26:02 +0800 Subject: [PATCH 08/11] ci: test 4 --- lib/gwmi.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/gwmi.js b/lib/gwmi.js index eb66026..1ffe13a 100644 --- a/lib/gwmi.js +++ b/lib/gwmi.js @@ -66,7 +66,9 @@ function gwmi (pids, options, done) { // CreationDate KernelModeTime ParentProcessId ProcessId UserModeTime WorkingSetSize // 20150329221650.080654+060 153750000 0 777 8556250000 110821376 + console.log(stdout) stdout = stdout.split(os.EOL).slice(3) + console.log(stdout); if (!stdout.length) { const error = new Error('No matching pid found') From 0e045b39f6cc0626ab5685f890dc6251f295af6c Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 13:30:40 +0800 Subject: [PATCH 09/11] ci: test 5 --- lib/gwmi.js | 6 +- test/integration.js | 226 ++++++++++++++++++++++---------------------- 2 files changed, 118 insertions(+), 114 deletions(-) diff --git a/lib/gwmi.js b/lib/gwmi.js index 1ffe13a..862905b 100644 --- a/lib/gwmi.js +++ b/lib/gwmi.js @@ -66,9 +66,13 @@ function gwmi (pids, options, done) { // CreationDate KernelModeTime ParentProcessId ProcessId UserModeTime WorkingSetSize // 20150329221650.080654+060 153750000 0 777 8556250000 110821376 - console.log(stdout) + console.log('stdout') + console.log(stdout); + console.log(); stdout = stdout.split(os.EOL).slice(3) + console.log('stdout after split') console.log(stdout); + console.log(); if (!stdout.length) { const error = new Error('No matching pid found') diff --git a/test/integration.js b/test/integration.js index cebab56..71c0963 100644 --- a/test/integration.js +++ b/test/integration.js @@ -32,119 +32,119 @@ test('should work with a single pid', async t => { t.false(isNaN(result.timestamp), 'timestamp') }) -test('should work with an array of pids', async t => { - const child = spawn( - 'node', - ['-e', 'console.log(`123`); setInterval(() => {}, 1000)'], - { windowsHide: true } - ) - const ppid = process.pid - const pid = child.pid - - await t.notThrowsAsync( - new Promise((resolve, reject) => { - child.stdout.on('data', d => resolve(d.toString())) - child.stderr.on('data', d => reject(d.toString())) - child.on('error', reject) - child.on('exit', code => reject(new Error('script exited with code ' + code))) - }), - 'script not executed' - ) - - const pids = [ppid, pid] - let result - try { - result = await m(pids) - child.kill() - } catch (err) { - child.kill() - t.notThrows(() => { throw err }) - } - - t.log(result) - - t.is(typeof result, 'object') - t.deepEqual(Object.keys(result).sort(), pids.map(pid => pid.toString()).sort()) - - pids.forEach(pid => { - t.is(typeof result[pid], 'object', 'result') - t.is(typeof result[pid].cpu, 'number', 'cpu') - t.false(isNaN(result[pid].cpu), 'cpu') - t.is(typeof result[pid].memory, 'number', 'memory') - // z/OS does not report memory - if (process.platform !== 'os390') { - t.false(isNaN(result[pid].memory), 'memory') - } - - t.is(typeof result[pid].ppid, 'number', 'ppid') - t.false(isNaN(result[pid].ppid), 'ppid') - t.is(typeof result[pid].pid, 'number', 'pid') - t.false(isNaN(result[pid].pid), 'pid') - t.is(typeof result[pid].elapsed, 'number', 'elapsed') - t.false(isNaN(result[pid].elapsed), 'elapsed') - t.is(typeof result[pid].timestamp, 'number', 'timestamp') - t.false(isNaN(result[pid].timestamp), 'timestamp') - }) - - m.clear() -}) - -test('should throw an error if no pid is provided', async t => { - const err = await t.throwsAsync(() => m([])) - t.is(err.message, 'You must provide at least one pid') -}) - -test('should throw an error if one of the pid is invalid', async t => { - let err = await t.throwsAsync(() => m(null)) - t.is(err.message, 'One of the pids provided is invalid') - err = await t.throwsAsync(() => m([null])) - t.is(err.message, 'One of the pids provided is invalid') - err = await t.throwsAsync(() => m(['invalid'])) - t.is(err.message, 'One of the pids provided is invalid') - err = await t.throwsAsync(() => m(-1)) - t.is(err.message, 'One of the pids provided is invalid') - err = await t.throwsAsync(() => m([-1])) - t.is(err.message, 'One of the pids provided is invalid') -}) - -test('should not throw an error if one of the pids does not exists', async t => { - await t.notThrows(() => m([process.pid, 65535])) - await t.notThrows(() => m([65535, process.pid])) -}) - -test('should throw an error if the pid does not exists', async t => { - const err = await t.throwsAsync(() => m([65535])) - t.is(err.message, 'No matching pid found') - t.is(err.code, 'ENOENT') -}) - -test('should throw an error if the pid is too large', async t => { - await t.throwsAsync(async () => m(99999999)) -}) - -test.cb('should exit right away because we cleaned up the event loop', t => { - if (os.platform().match(/^win/)) return t.end() - - process.clear_pidusage = '1' - require(path.join(__dirname, '/fixtures/_eventloop')) - process.nextTick(() => { - t.end() - }) -}) - -test.cb('should exit right away because the event loop ignores history', t => { - if (os.platform().match(/^win/)) return t.end() - - process.clear_pidusage = '0' - require(path.join(__dirname, '/fixtures/_eventloop')) - process.nextTick(() => { - t.end() - }) -}) - -test.cb("should use the callback if it's provided", t => { - m(process.pid, () => t.end()) -}) +// test('should work with an array of pids', async t => { +// const child = spawn( +// 'node', +// ['-e', 'console.log(`123`); setInterval(() => {}, 1000)'], +// { windowsHide: true } +// ) +// const ppid = process.pid +// const pid = child.pid + +// await t.notThrowsAsync( +// new Promise((resolve, reject) => { +// child.stdout.on('data', d => resolve(d.toString())) +// child.stderr.on('data', d => reject(d.toString())) +// child.on('error', reject) +// child.on('exit', code => reject(new Error('script exited with code ' + code))) +// }), +// 'script not executed' +// ) + +// const pids = [ppid, pid] +// let result +// try { +// result = await m(pids) +// child.kill() +// } catch (err) { +// child.kill() +// t.notThrows(() => { throw err }) +// } + +// t.log(result) + +// t.is(typeof result, 'object') +// t.deepEqual(Object.keys(result).sort(), pids.map(pid => pid.toString()).sort()) + +// pids.forEach(pid => { +// t.is(typeof result[pid], 'object', 'result') +// t.is(typeof result[pid].cpu, 'number', 'cpu') +// t.false(isNaN(result[pid].cpu), 'cpu') +// t.is(typeof result[pid].memory, 'number', 'memory') +// // z/OS does not report memory +// if (process.platform !== 'os390') { +// t.false(isNaN(result[pid].memory), 'memory') +// } + +// t.is(typeof result[pid].ppid, 'number', 'ppid') +// t.false(isNaN(result[pid].ppid), 'ppid') +// t.is(typeof result[pid].pid, 'number', 'pid') +// t.false(isNaN(result[pid].pid), 'pid') +// t.is(typeof result[pid].elapsed, 'number', 'elapsed') +// t.false(isNaN(result[pid].elapsed), 'elapsed') +// t.is(typeof result[pid].timestamp, 'number', 'timestamp') +// t.false(isNaN(result[pid].timestamp), 'timestamp') +// }) + +// m.clear() +// }) + +// test('should throw an error if no pid is provided', async t => { +// const err = await t.throwsAsync(() => m([])) +// t.is(err.message, 'You must provide at least one pid') +// }) + +// test('should throw an error if one of the pid is invalid', async t => { +// let err = await t.throwsAsync(() => m(null)) +// t.is(err.message, 'One of the pids provided is invalid') +// err = await t.throwsAsync(() => m([null])) +// t.is(err.message, 'One of the pids provided is invalid') +// err = await t.throwsAsync(() => m(['invalid'])) +// t.is(err.message, 'One of the pids provided is invalid') +// err = await t.throwsAsync(() => m(-1)) +// t.is(err.message, 'One of the pids provided is invalid') +// err = await t.throwsAsync(() => m([-1])) +// t.is(err.message, 'One of the pids provided is invalid') +// }) + +// test('should not throw an error if one of the pids does not exists', async t => { +// await t.notThrows(() => m([process.pid, 65535])) +// await t.notThrows(() => m([65535, process.pid])) +// }) + +// test('should throw an error if the pid does not exists', async t => { +// const err = await t.throwsAsync(() => m([65535])) +// t.is(err.message, 'No matching pid found') +// t.is(err.code, 'ENOENT') +// }) + +// test('should throw an error if the pid is too large', async t => { +// await t.throwsAsync(async () => m(99999999)) +// }) + +// test.cb('should exit right away because we cleaned up the event loop', t => { +// if (os.platform().match(/^win/)) return t.end() + +// process.clear_pidusage = '1' +// require(path.join(__dirname, '/fixtures/_eventloop')) +// process.nextTick(() => { +// t.end() +// }) +// }) + +// test.cb('should exit right away because the event loop ignores history', t => { +// if (os.platform().match(/^win/)) return t.end() + +// process.clear_pidusage = '0' +// require(path.join(__dirname, '/fixtures/_eventloop')) +// process.nextTick(() => { +// t.end() +// }) +// }) + +// test.cb("should use the callback if it's provided", t => { +// m(process.pid, () => t.end()) +// }) process.on('unhandledException', (e) => { console.error(e) From 5cae75f828c2e6600d04ad0d0ca3c961dd3d2a49 Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 14:04:39 +0800 Subject: [PATCH 10/11] ci: test 6 --- lib/gwmi.js | 12 +-- test/gwmi.js | 1 + test/integration.js | 226 ++++++++++++++++++++++---------------------- 3 files changed, 118 insertions(+), 121 deletions(-) diff --git a/lib/gwmi.js b/lib/gwmi.js index 862905b..b43db28 100644 --- a/lib/gwmi.js +++ b/lib/gwmi.js @@ -66,13 +66,9 @@ function gwmi (pids, options, done) { // CreationDate KernelModeTime ParentProcessId ProcessId UserModeTime WorkingSetSize // 20150329221650.080654+060 153750000 0 777 8556250000 110821376 - console.log('stdout') - console.log(stdout); - console.log(); - stdout = stdout.split(os.EOL).slice(3) - console.log('stdout after split') - console.log(stdout); - console.log(); + stdout = stdout.split(os.EOL).slice(1) + const index = stdout.findIndex(v => !!v) + stdout = stdout.slice(index + 2) if (!stdout.length) { const error = new Error('No matching pid found') @@ -82,7 +78,7 @@ function gwmi (pids, options, done) { let again = false const statistics = {} - for (let i = 1; i < stdout.length; i++) { + for (let i = 0; i < stdout.length; i++) { const line = stdout[i].trim().split(/\s+/) if (!line || line.length === 1) { diff --git a/test/gwmi.js b/test/gwmi.js index 500acf1..ff87b94 100644 --- a/test/gwmi.js +++ b/test/gwmi.js @@ -32,6 +32,7 @@ test('should parse gwmi output on Windows', async t => { '' + os.EOL + '' + os.EOL + 'CreationDate KernelModeTime ParentProcessId ProcessId UserModeTime WorkingSetSize' + os.EOL + + '------------ ----------- ----------- ------------ -------- --------' + os.EOL + '20150329221650.080654+060 153750000 0 777 8556250000 110821376' let calls = 0 diff --git a/test/integration.js b/test/integration.js index 71c0963..cebab56 100644 --- a/test/integration.js +++ b/test/integration.js @@ -32,119 +32,119 @@ test('should work with a single pid', async t => { t.false(isNaN(result.timestamp), 'timestamp') }) -// test('should work with an array of pids', async t => { -// const child = spawn( -// 'node', -// ['-e', 'console.log(`123`); setInterval(() => {}, 1000)'], -// { windowsHide: true } -// ) -// const ppid = process.pid -// const pid = child.pid - -// await t.notThrowsAsync( -// new Promise((resolve, reject) => { -// child.stdout.on('data', d => resolve(d.toString())) -// child.stderr.on('data', d => reject(d.toString())) -// child.on('error', reject) -// child.on('exit', code => reject(new Error('script exited with code ' + code))) -// }), -// 'script not executed' -// ) - -// const pids = [ppid, pid] -// let result -// try { -// result = await m(pids) -// child.kill() -// } catch (err) { -// child.kill() -// t.notThrows(() => { throw err }) -// } - -// t.log(result) - -// t.is(typeof result, 'object') -// t.deepEqual(Object.keys(result).sort(), pids.map(pid => pid.toString()).sort()) - -// pids.forEach(pid => { -// t.is(typeof result[pid], 'object', 'result') -// t.is(typeof result[pid].cpu, 'number', 'cpu') -// t.false(isNaN(result[pid].cpu), 'cpu') -// t.is(typeof result[pid].memory, 'number', 'memory') -// // z/OS does not report memory -// if (process.platform !== 'os390') { -// t.false(isNaN(result[pid].memory), 'memory') -// } - -// t.is(typeof result[pid].ppid, 'number', 'ppid') -// t.false(isNaN(result[pid].ppid), 'ppid') -// t.is(typeof result[pid].pid, 'number', 'pid') -// t.false(isNaN(result[pid].pid), 'pid') -// t.is(typeof result[pid].elapsed, 'number', 'elapsed') -// t.false(isNaN(result[pid].elapsed), 'elapsed') -// t.is(typeof result[pid].timestamp, 'number', 'timestamp') -// t.false(isNaN(result[pid].timestamp), 'timestamp') -// }) - -// m.clear() -// }) - -// test('should throw an error if no pid is provided', async t => { -// const err = await t.throwsAsync(() => m([])) -// t.is(err.message, 'You must provide at least one pid') -// }) - -// test('should throw an error if one of the pid is invalid', async t => { -// let err = await t.throwsAsync(() => m(null)) -// t.is(err.message, 'One of the pids provided is invalid') -// err = await t.throwsAsync(() => m([null])) -// t.is(err.message, 'One of the pids provided is invalid') -// err = await t.throwsAsync(() => m(['invalid'])) -// t.is(err.message, 'One of the pids provided is invalid') -// err = await t.throwsAsync(() => m(-1)) -// t.is(err.message, 'One of the pids provided is invalid') -// err = await t.throwsAsync(() => m([-1])) -// t.is(err.message, 'One of the pids provided is invalid') -// }) - -// test('should not throw an error if one of the pids does not exists', async t => { -// await t.notThrows(() => m([process.pid, 65535])) -// await t.notThrows(() => m([65535, process.pid])) -// }) - -// test('should throw an error if the pid does not exists', async t => { -// const err = await t.throwsAsync(() => m([65535])) -// t.is(err.message, 'No matching pid found') -// t.is(err.code, 'ENOENT') -// }) - -// test('should throw an error if the pid is too large', async t => { -// await t.throwsAsync(async () => m(99999999)) -// }) - -// test.cb('should exit right away because we cleaned up the event loop', t => { -// if (os.platform().match(/^win/)) return t.end() - -// process.clear_pidusage = '1' -// require(path.join(__dirname, '/fixtures/_eventloop')) -// process.nextTick(() => { -// t.end() -// }) -// }) - -// test.cb('should exit right away because the event loop ignores history', t => { -// if (os.platform().match(/^win/)) return t.end() - -// process.clear_pidusage = '0' -// require(path.join(__dirname, '/fixtures/_eventloop')) -// process.nextTick(() => { -// t.end() -// }) -// }) - -// test.cb("should use the callback if it's provided", t => { -// m(process.pid, () => t.end()) -// }) +test('should work with an array of pids', async t => { + const child = spawn( + 'node', + ['-e', 'console.log(`123`); setInterval(() => {}, 1000)'], + { windowsHide: true } + ) + const ppid = process.pid + const pid = child.pid + + await t.notThrowsAsync( + new Promise((resolve, reject) => { + child.stdout.on('data', d => resolve(d.toString())) + child.stderr.on('data', d => reject(d.toString())) + child.on('error', reject) + child.on('exit', code => reject(new Error('script exited with code ' + code))) + }), + 'script not executed' + ) + + const pids = [ppid, pid] + let result + try { + result = await m(pids) + child.kill() + } catch (err) { + child.kill() + t.notThrows(() => { throw err }) + } + + t.log(result) + + t.is(typeof result, 'object') + t.deepEqual(Object.keys(result).sort(), pids.map(pid => pid.toString()).sort()) + + pids.forEach(pid => { + t.is(typeof result[pid], 'object', 'result') + t.is(typeof result[pid].cpu, 'number', 'cpu') + t.false(isNaN(result[pid].cpu), 'cpu') + t.is(typeof result[pid].memory, 'number', 'memory') + // z/OS does not report memory + if (process.platform !== 'os390') { + t.false(isNaN(result[pid].memory), 'memory') + } + + t.is(typeof result[pid].ppid, 'number', 'ppid') + t.false(isNaN(result[pid].ppid), 'ppid') + t.is(typeof result[pid].pid, 'number', 'pid') + t.false(isNaN(result[pid].pid), 'pid') + t.is(typeof result[pid].elapsed, 'number', 'elapsed') + t.false(isNaN(result[pid].elapsed), 'elapsed') + t.is(typeof result[pid].timestamp, 'number', 'timestamp') + t.false(isNaN(result[pid].timestamp), 'timestamp') + }) + + m.clear() +}) + +test('should throw an error if no pid is provided', async t => { + const err = await t.throwsAsync(() => m([])) + t.is(err.message, 'You must provide at least one pid') +}) + +test('should throw an error if one of the pid is invalid', async t => { + let err = await t.throwsAsync(() => m(null)) + t.is(err.message, 'One of the pids provided is invalid') + err = await t.throwsAsync(() => m([null])) + t.is(err.message, 'One of the pids provided is invalid') + err = await t.throwsAsync(() => m(['invalid'])) + t.is(err.message, 'One of the pids provided is invalid') + err = await t.throwsAsync(() => m(-1)) + t.is(err.message, 'One of the pids provided is invalid') + err = await t.throwsAsync(() => m([-1])) + t.is(err.message, 'One of the pids provided is invalid') +}) + +test('should not throw an error if one of the pids does not exists', async t => { + await t.notThrows(() => m([process.pid, 65535])) + await t.notThrows(() => m([65535, process.pid])) +}) + +test('should throw an error if the pid does not exists', async t => { + const err = await t.throwsAsync(() => m([65535])) + t.is(err.message, 'No matching pid found') + t.is(err.code, 'ENOENT') +}) + +test('should throw an error if the pid is too large', async t => { + await t.throwsAsync(async () => m(99999999)) +}) + +test.cb('should exit right away because we cleaned up the event loop', t => { + if (os.platform().match(/^win/)) return t.end() + + process.clear_pidusage = '1' + require(path.join(__dirname, '/fixtures/_eventloop')) + process.nextTick(() => { + t.end() + }) +}) + +test.cb('should exit right away because the event loop ignores history', t => { + if (os.platform().match(/^win/)) return t.end() + + process.clear_pidusage = '0' + require(path.join(__dirname, '/fixtures/_eventloop')) + process.nextTick(() => { + t.end() + }) +}) + +test.cb("should use the callback if it's provided", t => { + m(process.pid, () => t.end()) +}) process.on('unhandledException', (e) => { console.error(e) From d6b77d816719060a8ee598f467f9373da0285578 Mon Sep 17 00:00:00 2001 From: linka Date: Tue, 22 Feb 2022 14:29:45 +0800 Subject: [PATCH 11/11] fix(wmic): add gwmi command to be compatible with deleted wmic --- CHANGELOG.md | 4 ++++ lib/gwmi.js | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53f0b0e..55cce04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 3.0.1 + +- fix wmic removed on Windows 11 and add gwmi support + ### 3.0 - removes node 8 support diff --git a/lib/gwmi.js b/lib/gwmi.js index b43db28..d33a670 100644 --- a/lib/gwmi.js +++ b/lib/gwmi.js @@ -63,8 +63,13 @@ function gwmi (pids, options, done) { // Refs: https://msdn.microsoft.com/en-us/library/aa394372(v=vs.85).aspx // NB: The columns are returned in lexicographical order // - // CreationDate KernelModeTime ParentProcessId ProcessId UserModeTime WorkingSetSize - // 20150329221650.080654+060 153750000 0 777 8556250000 110821376 + // Stdout Format + // + // Active code page: 936 + // + // CreationDate KernelModeTime ParentProcessId ProcessId UserModeTime WorkingSetSize + // ------------ -------------- --------------- --------- ------------ -------------- + // 20220220185531.619182+480 981406250 18940 2804 572656250 61841408 stdout = stdout.split(os.EOL).slice(1) const index = stdout.findIndex(v => !!v)