Skip to content

Commit 19c6a84

Browse files
author
Patrick Mueller
committed
initial stab at process.cpuUsage()
1 parent 59d23ad commit 19c6a84

File tree

6 files changed

+192
-0
lines changed

6 files changed

+192
-0
lines changed

doc/api/process.markdown

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,29 @@ An example of the possible output looks like:
495495

496496
If `process.connected` is false, it is no longer possible to send messages.
497497

498+
## process.cpuUsage()
499+
500+
Returns the user and system cpu time usage of the current process, in an object
501+
with properties `user` and `system`, whose values are arrays of integers as
502+
`[seconds, nanoseconds]` tuples. These values measure time spent in user and
503+
system code respectively, and may end up being greater than actual elapsed time
504+
if multiple cpu cores are performing work for this process.
505+
506+
You may pass in the result of a previous call to `process.cpuUsage()` to get a
507+
diff reading.
508+
509+
```js
510+
var startUsage = process.cpuUsage();
511+
// { user: [ 0, 35884000 ], system: [ 0, 7320000 ] }
512+
513+
// spin the CPU for 500 milliseconds
514+
var now = Date.now();
515+
while (Date.now() - now < 500);
516+
517+
console.log(process.cpuUsage(startUsage));
518+
// { user: [ 0, 509627000 ], system: [ 0, 9993000 ] }
519+
```
520+
498521
## process.cwd()
499522

500523
Returns the current working directory of the process.

lib/internal/bootstrap_node.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
const _process = NativeModule.require('internal/process');
4848

4949
_process.setup_hrtime();
50+
_process.setup_cpuUsage();
5051
_process.setupConfig(NativeModule._source);
5152
NativeModule.require('internal/process/warning').setup();
5253
NativeModule.require('internal/process/next_tick').setup();

lib/internal/process.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ function lazyConstants() {
99
return _lazyConstants;
1010
}
1111

12+
exports.setup_cpuUsage = setup_cpuUsage;
1213
exports.setup_hrtime = setup_hrtime;
1314
exports.setupConfig = setupConfig;
1415
exports.setupKillAndExit = setupKillAndExit;
@@ -22,6 +23,65 @@ const assert = process.assert = function(x, msg) {
2223
};
2324

2425

26+
function setup_cpuUsage() {
27+
const _cpuUsage = process.cpuUsage;
28+
const cpuValues = new Uint32Array(8);
29+
30+
process.cpuUsage = function cpuUsage(prevValue) {
31+
const err = _cpuUsage(cpuValues);
32+
if (err !== 0) throw new Error('unable to obtain cpu usage time');
33+
34+
// the second element of user/system comes back from libuv as
35+
// microseconds, but we convert to nanoseconds here (* 1000) to
36+
// be consistent with hrtime()
37+
var currValue = {
38+
user: [
39+
cpuValues[0] * 0x100000000 + cpuValues[1],
40+
(cpuValues[2] * 0x100000000 + cpuValues[3]) * 1000
41+
],
42+
system: [
43+
cpuValues[4] * 0x100000000 + cpuValues[5],
44+
(cpuValues[6] * 0x100000000 + cpuValues[7]) * 1000
45+
]
46+
};
47+
48+
if (prevValue == null) return currValue;
49+
50+
if (prevValue.user == null) {
51+
throw new Error('previous value argument missing "user" property');
52+
}
53+
if (prevValue.system == null) {
54+
throw new Error('previous value argument missing "system" property');
55+
}
56+
57+
currValue.user = diffSecUsec(currValue.user, prevValue.user);
58+
currValue.system = diffSecUsec(currValue.system, prevValue.system);
59+
60+
return currValue;
61+
};
62+
}
63+
64+
65+
// diff the value from a current and previous [sec, usec] array
66+
function diffSecUsec(curr, prev) {
67+
if (prev == null) return curr;
68+
69+
// perform the diff
70+
var result = [];
71+
72+
result[0] = curr[0] - prev[0];
73+
result[1] = curr[1] - prev[1];
74+
75+
// result[1] could be negative, on roll-overs, so ... be nice and normalize
76+
if (result[1] < 0) {
77+
result[0] -= 1; // subtract 1 sec
78+
result[1] += 1000 * 1000 * 1000; // add 1 sec's worth of usec's
79+
}
80+
81+
return result;
82+
}
83+
84+
2585
function setup_hrtime() {
2686
const _hrtime = process.hrtime;
2787
const hrValues = new Uint32Array(3);

src/node.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2216,6 +2216,35 @@ void Hrtime(const FunctionCallbackInfo<Value>& args) {
22162216
fields[2] = t % NANOS_PER_SEC;
22172217
}
22182218

2219+
// CPUUsage exposes libuv's uv_getrusage() this-process resource usage accessor,
2220+
// to access ru_utime (user CPU time used) and ru_stime (system CPU time used),
2221+
// which are uv_timeval_t structs (long tv_sec, long tv_usec) as tuples
2222+
// similar to process.hrtime() values. Only it's seconds/microseconds instead
2223+
// of hrtime()'s seconds/nanoseconds.
2224+
void CPUUsage(const FunctionCallbackInfo<Value>& args) {
2225+
uv_rusage_t rusage;
2226+
2227+
// call to libuv to get the values we'll return
2228+
int err = uv_getrusage(&rusage);
2229+
2230+
// expecting to be passed in a uint array, of length 8
2231+
Local<ArrayBuffer> ab = args[0].As<Uint32Array>()->Buffer();
2232+
uint32_t* fields = static_cast<uint32_t*>(ab->GetContents().Data());
2233+
2234+
// results passed as split values, reassembled in JS
2235+
fields[0] = (rusage.ru_utime.tv_sec) >> 32;
2236+
fields[1] = (rusage.ru_utime.tv_sec) & 0xffffffff;
2237+
fields[2] = (rusage.ru_utime.tv_usec) >> 32;
2238+
fields[3] = (rusage.ru_utime.tv_usec) & 0xffffffff;
2239+
fields[4] = (rusage.ru_stime.tv_sec) >> 32;
2240+
fields[5] = (rusage.ru_stime.tv_sec) & 0xffffffff;
2241+
fields[6] = (rusage.ru_stime.tv_usec) >> 32;
2242+
fields[7] = (rusage.ru_stime.tv_usec) & 0xffffffff;
2243+
2244+
// return value will be return code from the libuv call
2245+
args.GetReturnValue().Set(err);
2246+
}
2247+
22192248
extern "C" void node_module_register(void* m) {
22202249
struct node_module* mp = reinterpret_cast<struct node_module*>(m);
22212250

@@ -3208,6 +3237,8 @@ void SetupProcessObject(Environment* env,
32083237

32093238
env->SetMethod(process, "hrtime", Hrtime);
32103239

3240+
env->SetMethod(process, "cpuUsage", CPUUsage);
3241+
32113242
env->SetMethod(process, "dlopen", DLOpen);
32123243

32133244
env->SetMethod(process, "uptime", Uptime);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
require('../common');
3+
var assert = require('assert');
4+
5+
// the default behavior, return { user: [int, int], system: [int, int]}
6+
var result = process.cpuUsage();
7+
8+
// validate the default behavior
9+
validateResult(result);
10+
11+
// validate that passing an existing result returns another valid result
12+
validateResult(process.cpuUsage(result));
13+
14+
// test that only a previous result object may be passed to process.cpuUsage()
15+
assert.throws(function() { process.cpuUsage(1); });
16+
assert.throws(function() { process.cpuUsage({}); });
17+
assert.throws(function() { process.cpuUsage({ user: [0, 0] }); });
18+
assert.throws(function() { process.cpuUsage({ system: [0, 0] }); });
19+
assert.throws(function() { process.cpuUsage({ user: null, system: [0, 0] }); });
20+
assert.throws(function() { process.cpuUsage({ user: [0, 0], system: null }); });
21+
22+
function validateResult(result) {
23+
assert(result.user != null);
24+
assert(result.system != null);
25+
26+
assert(Array.isArray(result.user));
27+
assert(Array.isArray(result.system));
28+
29+
assert.equal(2, result.user.length);
30+
assert.equal(2, result.system.length);
31+
32+
result.user.forEach(function(v) {
33+
assert.equal('number', typeof v);
34+
assert(isFinite(v));
35+
});
36+
37+
result.system.forEach(function(v) {
38+
assert.equal('number', typeof v);
39+
assert(isFinite(v));
40+
});
41+
}
42+
43+
// ensure we don't end up sending negative values on second rollovers
44+
// https://github.com/nodejs/node/issues/4751
45+
const diff = process.cpuUsage({ user: [0, 1e9 - 1], system: [0, 1e9 - 1] });
46+
assert(diff.user[1] >= 0);
47+
assert(diff.system[1] >= 0);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
require('../common');
3+
var assert = require('assert');
4+
5+
var start = process.cpuUsage();
6+
7+
// process.cpuUsage() should return {user: [int, int], system: [int, int]}
8+
assert(start != null);
9+
assert(Array.isArray(start.user));
10+
assert(Array.isArray(start.system));
11+
12+
// busy-loop for 2 seconds
13+
var now = Date.now();
14+
while (Date.now() - now < 2000);
15+
16+
// get a diff reading
17+
var diff = process.cpuUsage(start);
18+
19+
// user should be at least 1 second, at most 2 seconds later
20+
// (the while loop will usually exit a few nanoseconds before 2)
21+
assert(diff.user[0] >= 1);
22+
assert(diff.user[0] <= 2);
23+
24+
// hard to tell what the system number should be, but at least > 0, < 2
25+
assert(diff.system[0] >= 0);
26+
assert(diff.system[0] <= 2);
27+
28+
// make sure nanosecond value is not negative
29+
assert(diff.user[1] >= 0);
30+
assert(diff.system[1] >= 0);

0 commit comments

Comments
 (0)