Skip to content

Commit

Permalink
Merge pull request #59 from soyuka/cpu
Browse files Browse the repository at this point in the history
Fix cpu percentage computation
  • Loading branch information
soyuka authored Jun 4, 2018
2 parents 9bbc258 + aa013f6 commit 931259e
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 34 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ setInterval(function () {
pidusage(process.pid, function (err, stats) {
console.log(stats)
// => {
// cpu: 10.0, // percentage (it may happen to be greater than 100%)
// cpu: 10.0, // percentage (0-100%)
// memory: 357306368, // bytes
// ppid: 312, // PPID
// pid: 727, // PID
Expand All @@ -41,7 +41,7 @@ pidusage([727, 1234], function (err, stats) {
console.log(stats)
// => {
// 727: {
// cpu: 10.0, // percentage
// cpu: 10.0, // percentage (0-100%)
// memory: 357306368, // bytes
// ppid: 312, // PPID
// pid: 727, // PID
Expand All @@ -50,7 +50,7 @@ pidusage([727, 1234], function (err, stats) {
// timestamp: 864000000 // ms since epoch
// },
// 1234: {
// cpu: 0.1, // percentage
// cpu: 0.1, // percentage (0-100%)
// memory: 3846144, // bytes
// ppid: 727, // PPID
// pid: 1234, // PID
Expand All @@ -65,7 +65,7 @@ pidusage([727, 1234], function (err, stats) {
const stats = await pidusage(process.pid)
console.log(stats)
// => {
// cpu: 10.0, // percentage (it may happen to be greater than 100%)
// cpu: 10.0, // percentage (0-100%)
// memory: 357306368, // bytes
// ppid: 312, // PPID
// pid: 727, // PID
Expand Down Expand Up @@ -135,4 +135,3 @@ This project is licensed under the MIT License - see the [LICENSE][license] file

[node:cpuUsage]: https://nodejs.org/api/process.html#process_process_cpuusage_previousvalue
[node:memUsage]: https://nodejs.org/api/process.html#process_process_memoryusage

29 changes: 14 additions & 15 deletions lib/procfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ var path = require('path')
var updateCpu = require('./helpers/cpu')
var parallel = require('./helpers/parallel')
var history = require('./history')
var cpu = null
var cpuInfo = null

function readProcFile (pid, options, done) {
var hst = history.get(pid, options.maxage)
Expand All @@ -15,11 +15,10 @@ function readProcFile (pid, options, done) {
if (err.code === 'ENOENT') {
err.message = 'No maching pid found'
}

return done(err, null)
}

var date = Date.now()

// https://github.com/arunoda/node-usage/commit/a6ca74ecb8dd452c3c00ed2bde93294d7bb75aa8
// preventing process space in name by removing values before last ) (pid (name) ...)
var index = infos.lastIndexOf(')')
Expand All @@ -33,27 +32,27 @@ function readProcFile (pid, options, done) {
stime: parseFloat(infos[12]),
cutime: parseFloat(infos[13]),
cstime: parseFloat(infos[14]),
start: parseFloat(infos[19]) / cpu.clockTick,
start: parseFloat(infos[19]) / cpuInfo.clockTick,
rss: parseFloat(infos[21]),
uptime: cpu.uptime
uptime: cpuInfo.uptime
}

// http://stackoverflow.com/questions/16726779/total-cpu-usage-of-an-application-from-proc-pid-stat/16736599#16736599
var memory = stat.rss * cpuInfo.pageSize

// https://stackoverflow.com/a/16736599/3921589
var childrens = options.childrens ? stat.cutime + stat.cstime : 0
var total = (stat.stime - (hst.stime || 0) + stat.utime - (hst.utime || 0) + childrens) / cpu.clockTick
// process usage since last call in seconds
var total = (stat.stime - (hst.stime || 0) + stat.utime - (hst.utime || 0) + childrens) / cpuInfo.clockTick
// time elapsed between calls in seconds
var seconds = Math.abs(hst.uptime !== undefined ? stat.uptime - hst.uptime : stat.start - stat.uptime)
if (seconds === 0) seconds = 1 // we sure can't divide through 0
var cpu = seconds > 0 ? (total / seconds) * 100 : 0

history.set(stat, options.maxage)

var cpuPercent = Math.min(Math.round((total / seconds) * 100000) / 1000, 100.0)
var memory = stat.rss * cpu.pageSize

return done(null, {
cpu: cpuPercent,
cpu: Math.min(cpu, 100.0),
memory: memory,
ctime: (stat.utime + stat.stime) / cpu.clockTick,
ctime: (stat.utime + stat.stime) / cpuInfo.clockTick,
elapsed: date - (stat.start * 1000),
timestamp: stat.start * 1000, // start date
pid: pid,
Expand All @@ -63,10 +62,10 @@ function readProcFile (pid, options, done) {
}

function procfile (pid, options, done) {
updateCpu(cpu, function (err, result) {
updateCpu(cpuInfo, function (err, result) {
if (err) return done(err)

cpu = result
cpuInfo = result
var fns = {}

pid.forEach(function (id, i) {
Expand Down
7 changes: 5 additions & 2 deletions lib/ps.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var os = require('os')
var bin = require('./bin')

var PLATFORM = os.platform()
var cpus = os.cpus().length

function parseTime (timestr, fraction) {
var time = 0
Expand Down Expand Up @@ -56,6 +57,7 @@ function ps (pids, options, done) {
// ELAPSED: format is [[dd-]hh:]mm:ss
// RSS: is counted as blocks of 1024 bytes
// TIME: format is [[dd-]hh:]mm:ss
// %CPU: goes from 0 to vcore * 100
//
// Refs: http://www.manpages.info/linux/ps.1.html
// NB: The columns are returned in the order given inside the -o option
Expand All @@ -70,6 +72,7 @@ function ps (pids, options, done) {
// ELAPSED: format is [[dd-]hh:]mm:ss
// RSS: is counted as blocks of 1024 bytes
// TIME: format is [[dd-]hh:]ss:mm.pp (pp is the percentage of a minute)
// %CPU: goes from 0 to vcore * 100
//
// Refs: https://ss64.com/osx/ps.html
// NB: The columns are returned in the order given inside the -o option
Expand All @@ -92,13 +95,13 @@ function ps (pids, options, done) {

var pid = parseInt(line[1], 10)
var ppid = parseInt(line[2], 10)
var cpu = parseFloat(line[3].replace(',', '.'), 10)
var cpu = parseFloat(line[3].replace(',', '.'), 10) / cpus
var memory = parseInt(line[4], 10) * 1024
var etime = parseTime(line[0])
var ctime = parseTime(line[5], true)

statistics[pid] = {
cpu: Math.min(Math.round(cpu * 1000) / 1000, 100.0),
cpu: Math.min(cpu, 100.0),
memory: memory,
ppid: ppid,
pid: pid,
Expand Down
8 changes: 4 additions & 4 deletions lib/wmic.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,15 @@ function wmic (pids, options, done) {
}

// process usage since last call
var total = (kerneltime + usertime - hst.ctime)
// time elapsed between calls
var total = (kerneltime + usertime - hst.ctime) / 1000
// time elapsed between calls in seconds
var seconds = uptime - hst.uptime
var cpu = seconds > 0 ? (total / (seconds * 1000)) * 100 : 0
var cpu = seconds > 0 ? (total / seconds) * 100 : 0

history.set(pid, {ctime: usertime + kerneltime, uptime: uptime}, options.maxage)

statistics[pid] = {
cpu: Math.min(Math.round(cpu * 1000) / 1000, 100.0),
cpu: Math.min(cpu, 100.0),
memory: memory,
ppid: ppid,
pid: pid,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"node": ">=4"
},
"scripts": {
"lint": "standard index.js lib/**/*.js test/**/*.js",
"test": "npm run lint && nyc ava -m \"!*benchmark*\"",
"lint": "standard",
"test": "standard &&nyc ava -m \"!*benchmark*\"",
"alpine": "docker run -v $(pwd):/var/pidusage pidusage:latest npm test",
"coverage": "codecov",
"bench": "ava -m \"*benchmark*\""
Expand Down
5 changes: 3 additions & 2 deletions test/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ test('should work with an array of pids', async t => {
child.stderr.on('data', d => reject(d.toString()))
child.on('error', reject)
child.on('exit', reject)
})
, 'script not executed')
}),
'script not executed'
)

const pids = [ppid, pid]
let result
Expand Down
16 changes: 12 additions & 4 deletions test/ps.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ test('should parse ps output on Darwin', async t => {
spawn: () => mocks.spawn(stdout, '', null, 0, null)
})
mockery.registerMock('os', {
EOL: os.EOL, platform: () => 'darwin', type: () => 'type', release: () => 'release'}
)
EOL: os.EOL,
platform: () => 'darwin',
type: () => 'type',
release: () => 'release',
cpus: () => [os.cpus()[0]]
})

const ps = require('../lib/ps')

Expand Down Expand Up @@ -99,8 +103,12 @@ test('should parse ps output on *nix', async t => {
spawn: () => mocks.spawn(stdout, '', null, 0, null)
})
mockery.registerMock('os', {
EOL: os.EOL, platform: () => 'linux', type: () => 'type', release: () => 'release'}
)
EOL: os.EOL,
platform: () => 'linux',
type: () => 'type',
release: () => 'release',
cpus: () => [os.cpus()[0]]
})

const ps = require('../lib/ps')

Expand Down

0 comments on commit 931259e

Please sign in to comment.