Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1b9fdba

Browse filesBrowse 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 <robert.lindstaedt@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
1 parent 2c92a1f commit 1b9fdba
Copy full SHA for 1b9fdba

File tree

Expand file treeCollapse file tree

6 files changed

+204
-0
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

6 files changed

+204
-0
lines changed
Open diff view settings
Collapse file

‎doc/api/process.md‎

Copy file name to clipboardExpand all lines: doc/api/process.md
+23Lines changed: 23 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
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+
const startUsage = process.cpuUsage();
520+
// { user: 38579, system: 6986 }
521+
522+
// spin the CPU for 500 milliseconds
523+
const now = 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.
Collapse file

‎lib/internal/bootstrap_node.js‎

Copy file name to clipboardExpand all lines: lib/internal/bootstrap_node.js
+1Lines 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();
Collapse file

‎lib/internal/process.js‎

Copy file name to clipboardExpand all lines: lib/internal/process.js
+52Lines 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+
function setup_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+
const cpuValues = new Float64Array(2);
33+
34+
// Replace the native function with the JS version that calls the native
35+
// function.
36+
process.cpuUsage = function cpuUsage(prevValue) {
37+
// If a previous value was passed in, ensure it has the correct shape.
38+
if (prevValue) {
39+
if (!previousValueIsValid(prevValue.user)) {
40+
throw new TypeError('value of user property of argument is invalid');
41+
}
42+
43+
if (!previousValueIsValid(prevValue.system)) {
44+
throw new TypeError('value of system property of argument is invalid');
45+
}
46+
}
47+
48+
// Call the native function to get the current values.
49+
const errmsg = _cpuUsage(cpuValues);
50+
if (errmsg) {
51+
throw new Error('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+
function previousValueIsValid(num) {
69+
return Number.isFinite(num) &&
70+
num <= Number.MAX_SAFE_INTEGER &&
71+
num >= 0;
72+
}
73+
};
74+
}
75+
76+
2577
function setup_hrtime() {
2678
const _hrtime = process.hrtime;
2779
const hrValues = new Uint32Array(3);
Collapse file

‎src/node.cc‎

Copy file name to clipboardExpand all lines: src/node.cc
+35Lines 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+
#define MICROS_PER_SEC 1e6
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+
void CPUUsage(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" void node_module_register(void* m) {
22242257
struct node_module* mp = reinterpret_cast<struct node_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);
Collapse file
+63Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
5+
const result = 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+
let thisUsage;
15+
let lastUsage = process.cpuUsage();
16+
for (let i = 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+
let startUsage;
26+
let diffUsage;
27+
for (let i = 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+
function validateResult(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+
}
Collapse file
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
5+
const start = process.cpuUsage();
6+
7+
// Run a busy-loop for specified # of milliseconds.
8+
const RUN_FOR_MS = 500;
9+
10+
// Define slop factor for checking maximum expected diff values.
11+
const SLOP_FACTOR = 2;
12+
13+
// Run a busy loop.
14+
const now = Date.now();
15+
while (Date.now() - now < RUN_FOR_MS);
16+
17+
// Get a diff reading from when we started.
18+
const diff = process.cpuUsage(start);
19+
20+
const MICROSECONDS_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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.