Skip to content

Commit 1b9fdba

Browse files
Patrick MuellerFishrock123
authored andcommitted
process: add process.cpuUsage() - implementation, doc, tests
Add process.cpuUsage() method that returns the user and system CPU time usage of the current process PR-URL: #6157 Reviewed-By: Robert Lindstaedt <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Trevor Norris <[email protected]> Reviewed-By: Santiago Gimeno <[email protected]>
1 parent 2c92a1f commit 1b9fdba

File tree

6 files changed

+204
-0
lines changed

6 files changed

+204
-0
lines changed

‎doc/api/process.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,29 @@ the value of `process.config`.*
504504

505505
If `process.connected` is `false`, it is no longer possible to send messages.
506506

507+
## process.cpuUsage([previousValue])
508+
509+
Returns the user and system CPU time usage of the current process, in an object
510+
with properties `user` and `system`, whose values are microsecond values
511+
(millionth of a second). These values measure time spent in user and
512+
system code respectively, and may end up being greater than actual elapsed time
513+
if multiple CPU cores are performing work for this process.
514+
515+
The result of a previous call to `process.cpuUsage()` can be passed as the
516+
argument to the function, to get a diff reading.
517+
518+
```js
519+
conststartUsage=process.cpuUsage();
520+
//{user: 38579, system: 6986 }
521+
522+
// spin the CPU for 500 milliseconds
523+
constnow=Date.now();
524+
while (Date.now() - now <500);
525+
526+
console.log(process.cpuUsage(startUsage));
527+
//{user: 514883, system: 11226 }
528+
```
529+
507530
## process.cwd()
508531

509532
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: 52 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,57 @@ const assert = process.assert = function(x, msg){
2223
};
2324

2425

26+
// Set up the process.cpuUsage() function.
27+
functionsetup_cpuUsage(){
28+
// Get the native function, which will be replaced with a JS version.
29+
const_cpuUsage=process.cpuUsage;
30+
31+
// Create the argument array that will be passed to the native function.
32+
constcpuValues=newFloat64Array(2);
33+
34+
// Replace the native function with the JS version that calls the native
35+
// function.
36+
process.cpuUsage=functioncpuUsage(prevValue){
37+
// If a previous value was passed in, ensure it has the correct shape.
38+
if(prevValue){
39+
if(!previousValueIsValid(prevValue.user)){
40+
thrownewTypeError('value of user property of argument is invalid');
41+
}
42+
43+
if(!previousValueIsValid(prevValue.system)){
44+
thrownewTypeError('value of system property of argument is invalid');
45+
}
46+
}
47+
48+
// Call the native function to get the current values.
49+
consterrmsg=_cpuUsage(cpuValues);
50+
if(errmsg){
51+
thrownewError('unable to obtain CPU usage: '+errmsg);
52+
}
53+
54+
// If a previous value was passed in, return diff of current from previous.
55+
if(prevValue)return{
56+
user: cpuValues[0]-prevValue.user,
57+
system: cpuValues[1]-prevValue.system
58+
};
59+
60+
// If no previous value passed in, return current value.
61+
return{
62+
user: cpuValues[0],
63+
system: cpuValues[1]
64+
};
65+
66+
// Ensure that a previously passed in value is valid. Currently, the native
67+
// implementation always returns numbers <= Number.MAX_SAFE_INTEGER.
68+
functionpreviousValueIsValid(num){
69+
returnNumber.isFinite(num)&&
70+
num<=Number.MAX_SAFE_INTEGER&&
71+
num>=0;
72+
}
73+
};
74+
}
75+
76+
2577
functionsetup_hrtime(){
2678
const_hrtime=process.hrtime;
2779
consthrValues=newUint32Array(3);

‎src/node.cc‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ using v8::Boolean;
106106
using v8::Context;
107107
using v8::EscapableHandleScope;
108108
using v8::Exception;
109+
using v8::Float64Array;
109110
using v8::Function;
110111
using v8::FunctionCallbackInfo;
111112
using v8::FunctionTemplate;
@@ -2220,6 +2221,38 @@ void Hrtime(const FunctionCallbackInfo<Value>& args){
22202221
fields[2] = t % NANOS_PER_SEC;
22212222
}
22222223

2224+
// Microseconds in a second, as a float, used in CPUUsage() below
2225+
#defineMICROS_PER_SEC1e6
2226+
2227+
// CPUUsage use libuv's uv_getrusage() this-process resource usage accessor,
2228+
// to access ru_utime (user CPU time used) and ru_stime (system CPU time used),
2229+
// which are uv_timeval_t structs (long tv_sec, long tv_usec).
2230+
// Returns those values as Float64 microseconds in the elements of the array
2231+
// passed to the function.
2232+
voidCPUUsage(const FunctionCallbackInfo<Value>& args){
2233+
uv_rusage_t rusage;
2234+
2235+
// Call libuv to get the values we'll return.
2236+
int err = uv_getrusage(&rusage);
2237+
if (err){
2238+
// On error, return the strerror version of the error code.
2239+
Local<String> errmsg = OneByteString(args.GetIsolate(), uv_strerror(err));
2240+
args.GetReturnValue().Set(errmsg);
2241+
return;
2242+
}
2243+
2244+
// Get the double array pointer from the Float64Array argument.
2245+
CHECK(args[0]->IsFloat64Array());
2246+
Local<Float64Array> array = args[0].As<Float64Array>();
2247+
CHECK_EQ(array->Length(), 2);
2248+
Local<ArrayBuffer> ab = array->Buffer();
2249+
double* fields = static_cast<double*>(ab->GetContents().Data());
2250+
2251+
// Set the Float64Array elements to be user / system values in microseconds.
2252+
fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec;
2253+
fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec;
2254+
}
2255+
22232256
extern"C"voidnode_module_register(void* m){
22242257
structnode_module* mp = reinterpret_cast<structnode_module*>(m);
22252258

@@ -3212,6 +3245,8 @@ void SetupProcessObject(Environment* env,
32123245

32133246
env->SetMethod(process, "hrtime", Hrtime);
32143247

3248+
env->SetMethod(process, "cpuUsage", CPUUsage);
3249+
32153250
env->SetMethod(process, "dlopen", DLOpen);
32163251

32173252
env->SetMethod(process, "uptime", Uptime);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use strict';
2+
require('../common');
3+
constassert=require('assert');
4+
5+
constresult=process.cpuUsage();
6+
7+
// Validate the result of calling with no previous value argument.
8+
validateResult(result);
9+
10+
// Validate the result of calling with a previous value argument.
11+
validateResult(process.cpuUsage(result));
12+
13+
// Ensure the results are >= the previous.
14+
letthisUsage;
15+
letlastUsage=process.cpuUsage();
16+
for(leti=0;i<10;i++){
17+
thisUsage=process.cpuUsage();
18+
validateResult(thisUsage);
19+
assert(thisUsage.user>=lastUsage.user);
20+
assert(thisUsage.system>=lastUsage.system);
21+
lastUsage=thisUsage;
22+
}
23+
24+
// Ensure that the diffs are >= 0.
25+
letstartUsage;
26+
letdiffUsage;
27+
for(leti=0;i<10;i++){
28+
startUsage=process.cpuUsage();
29+
diffUsage=process.cpuUsage(startUsage);
30+
validateResult(startUsage);
31+
validateResult(diffUsage);
32+
assert(diffUsage.user>=0);
33+
assert(diffUsage.system>=0);
34+
}
35+
36+
// Ensure that an invalid shape for the previous value argument throws an error.
37+
assert.throws(function(){process.cpuUsage(1);});
38+
assert.throws(function(){process.cpuUsage({});});
39+
assert.throws(function(){process.cpuUsage({user: 'a'});});
40+
assert.throws(function(){process.cpuUsage({system: 'b'});});
41+
assert.throws(function(){process.cpuUsage({user: null,system: 'c'});});
42+
assert.throws(function(){process.cpuUsage({user: 'd',system: null});});
43+
assert.throws(function(){process.cpuUsage({user: -1,system: 2});});
44+
assert.throws(function(){process.cpuUsage({user: 3,system: -2});});
45+
assert.throws(function(){process.cpuUsage({
46+
user: Number.POSITIVE_INFINITY,
47+
system: 4
48+
});});
49+
assert.throws(function(){process.cpuUsage({
50+
user: 5,
51+
system: Number.NEGATIVE_INFINITY
52+
});});
53+
54+
// Ensure that the return value is the expected shape.
55+
functionvalidateResult(result){
56+
assert.notEqual(result,null);
57+
58+
assert(Number.isFinite(result.user));
59+
assert(Number.isFinite(result.system));
60+
61+
assert(result.user>=0);
62+
assert(result.system>=0);
63+
}
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+
constassert=require('assert');
4+
5+
conststart=process.cpuUsage();
6+
7+
// Run a busy-loop for specified # of milliseconds.
8+
constRUN_FOR_MS=500;
9+
10+
// Define slop factor for checking maximum expected diff values.
11+
constSLOP_FACTOR=2;
12+
13+
// Run a busy loop.
14+
constnow=Date.now();
15+
while(Date.now()-now<RUN_FOR_MS);
16+
17+
// Get a diff reading from when we started.
18+
constdiff=process.cpuUsage(start);
19+
20+
constMICROSECONDS_PER_SECOND=1000*1000;
21+
22+
// Diff usages should be >= 0, <= ~RUN_FOR_MS millis.
23+
// Let's be generous with the slop factor, defined above, in case other things
24+
// are happening on this CPU. The <= check may be invalid if the node process
25+
// is making use of multiple CPUs, in which case, just remove it.
26+
assert(diff.user>=0);
27+
assert(diff.user<=SLOP_FACTOR*RUN_FOR_MS*MICROSECONDS_PER_SECOND);
28+
29+
assert(diff.system>=0);
30+
assert(diff.system<=SLOP_FACTOR*RUN_FOR_MS*MICROSECONDS_PER_SECOND);

0 commit comments

Comments
(0)