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 7a9c8d8

Browse filesBrowse files
Patrick MuellerMylesBorins
authored andcommitted
process: add process.cpuUsage() - implementation, doc, tests
Backport to v4.x Original commit message: 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> PR-URL: #10796 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com> Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
1 parent 278d718 commit 7a9c8d8
Copy full SHA for 7a9c8d8

File tree

Expand file treeCollapse file tree

5 files changed

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

5 files changed

+208
-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
@@ -421,6 +421,29 @@ added: v0.7.2
421421

422422
If `process.connected` is false, it is no longer possible to send messages.
423423

424+
## process.cpuUsage([previousValue])
425+
426+
Returns the user and system CPU time usage of the current process, in an object
427+
with properties `user` and `system`, whose values are microsecond values
428+
(millionth of a second). These values measure time spent in user and
429+
system code respectively, and may end up being greater than actual elapsed time
430+
if multiple CPU cores are performing work for this process.
431+
432+
The result of a previous call to `process.cpuUsage()` can be passed as the
433+
argument to the function, to get a diff reading.
434+
435+
```js
436+
const startUsage = process.cpuUsage();
437+
// { user: 38579, system: 6986 }
438+
439+
// spin the CPU for 500 milliseconds
440+
const now = Date.now();
441+
while (Date.now() - now < 500);
442+
443+
console.log(process.cpuUsage(startUsage));
444+
// { user: 514883, system: 11226 }
445+
```
446+
424447
## process.cwd()
425448
<!-- YAML
426449
added: v0.1.8
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
@@ -102,6 +102,7 @@ using v8::Boolean;
102102
using v8::Context;
103103
using v8::EscapableHandleScope;
104104
using v8::Exception;
105+
using v8::Float64Array;
105106
using v8::Function;
106107
using v8::FunctionCallbackInfo;
107108
using v8::FunctionTemplate;
@@ -2203,6 +2204,38 @@ void Hrtime(const FunctionCallbackInfo<Value>& args) {
22032204
args.GetReturnValue().Set(tuple);
22042205
}
22052206

2207+
// Microseconds in a second, as a float, used in CPUUsage() below
2208+
#define MICROS_PER_SEC 1e6
2209+
2210+
// CPUUsage use libuv's uv_getrusage() this-process resource usage accessor,
2211+
// to access ru_utime (user CPU time used) and ru_stime (system CPU time used),
2212+
// which are uv_timeval_t structs (long tv_sec, long tv_usec).
2213+
// Returns those values as Float64 microseconds in the elements of the array
2214+
// passed to the function.
2215+
void CPUUsage(const FunctionCallbackInfo<Value>& args) {
2216+
uv_rusage_t rusage;
2217+
2218+
// Call libuv to get the values we'll return.
2219+
int err = uv_getrusage(&rusage);
2220+
if (err) {
2221+
// On error, return the strerror version of the error code.
2222+
Local<String> errmsg = OneByteString(args.GetIsolate(), uv_strerror(err));
2223+
args.GetReturnValue().Set(errmsg);
2224+
return;
2225+
}
2226+
2227+
// Get the double array pointer from the Float64Array argument.
2228+
CHECK(args[0]->IsFloat64Array());
2229+
Local<Float64Array> array = args[0].As<Float64Array>();
2230+
CHECK_EQ(array->Length(), 2);
2231+
Local<ArrayBuffer> ab = array->Buffer();
2232+
double* fields = static_cast<double*>(ab->GetContents().Data());
2233+
2234+
// Set the Float64Array elements to be user / system values in microseconds.
2235+
fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec;
2236+
fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec;
2237+
}
2238+
22062239
extern "C" void node_module_register(void* m) {
22072240
struct node_module* mp = reinterpret_cast<struct node_module*>(m);
22082241

@@ -3153,6 +3186,8 @@ void SetupProcessObject(Environment* env,
31533186

31543187
env->SetMethod(process, "hrtime", Hrtime);
31553188

3189+
env->SetMethod(process, "cpuUsage", CPUUsage);
3190+
31563191
env->SetMethod(process, "dlopen", DLOpen);
31573192

31583193
env->SetMethod(process, "uptime", Uptime);
Collapse file

‎src/node.js‎

Copy file name to clipboardExpand all lines: src/node.js
+53Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
startup.processStdio();
3939
startup.processKillAndExit();
4040
startup.processSignalHandlers();
41+
startup.processCpuUsage();
4142

4243
// Do not initialize channel in debugger agent, it deletes env variable
4344
// and the main thread won't see it.
@@ -286,6 +287,58 @@
286287
});
287288
};
288289

290+
startup.processCpuUsage = function() {
291+
// Get the native function, which will be replaced with a JS version.
292+
const _cpuUsage = process.cpuUsage;
293+
294+
// Create the argument array that will be passed to the native function.
295+
const cpuValues = new Float64Array(2);
296+
297+
// Replace the native function with the JS version that calls the native
298+
// function.
299+
process.cpuUsage = function cpuUsage(prevValue) {
300+
// If a previous value was passed in, ensure it has the correct shape.
301+
if (prevValue) {
302+
if (!previousValueIsValid(prevValue.user)) {
303+
throw new TypeError('value of user property of argument is invalid');
304+
}
305+
306+
if (!previousValueIsValid(prevValue.system)) {
307+
throw new TypeError(
308+
'value of system property of argument is invalid');
309+
}
310+
}
311+
312+
// Call the native function to get the current values.
313+
const errmsg = _cpuUsage(cpuValues);
314+
if (errmsg) {
315+
throw new Error('unable to obtain CPU usage: ' + errmsg);
316+
}
317+
318+
// If a previous value was passed in,
319+
// return diff of current from previous.
320+
if (prevValue) return {
321+
user: cpuValues[0] - prevValue.user,
322+
system: cpuValues[1] - prevValue.system
323+
};
324+
325+
// If no previous value passed in, return current value.
326+
return {
327+
user: cpuValues[0],
328+
system: cpuValues[1]
329+
};
330+
331+
// Ensure that a previously passed in value is valid. Currently, the
332+
// native implementation always returns
333+
// numbers <= Number.MAX_SAFE_INTEGER.
334+
function previousValueIsValid(num) {
335+
return Number.isFinite(num) &&
336+
num <= Number.MAX_SAFE_INTEGER &&
337+
num >= 0;
338+
}
339+
};
340+
};
341+
289342
var addPendingUnhandledRejection;
290343
var hasBeenNotifiedProperty = new WeakMap();
291344
startup.processNextTick = function() {
Collapse file
+67Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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() {
46+
process.cpuUsage({
47+
user: Number.POSITIVE_INFINITY,
48+
system: 4
49+
});
50+
});
51+
assert.throws(function() {
52+
process.cpuUsage({
53+
user: 5,
54+
system: Number.NEGATIVE_INFINITY
55+
});
56+
});
57+
58+
// Ensure that the return value is the expected shape.
59+
function validateResult(result) {
60+
assert.notEqual(result, null);
61+
62+
assert(Number.isFinite(result.user));
63+
assert(Number.isFinite(result.system));
64+
65+
assert(result.user >= 0);
66+
assert(result.system >= 0);
67+
}
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.