Skip to content

Commit

Permalink
process: expose uv_rusage on process.resourcesUsage()
Browse files Browse the repository at this point in the history
As discussed in nodejs/diagnostics#161,
the core should expose important metrics about the runtime, this PR's
goal is to let user get the number of io request made, and lower level
mertrics like the page faults and context switches.

PR-URL: #28018
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
  • Loading branch information
vmarchaud authored and targos committed Jul 2, 2019
1 parent 624fd17 commit 19f9281
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 1 deletion.
89 changes: 89 additions & 0 deletions doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -1815,6 +1815,91 @@ process.report.writeReport();

Additional documentation is available in the [report documentation][].

## process.resourceUsage()
<!-- YAML
added: REPLACEME
-->

* Returns: {Object}
* `userCPUTime` {integer}
* `systemCPUTime` {integer}
* `maxRSS` {integer}
* `sharedMemorySize` {integer}
* `unsharedDataSize` {integer}
* `unsharedStackSize` {integer}
* `minorPageFault` {integer}
* `majorPageFault` {integer}
* `swapedOut` {integer}
* `fsRead` {integer}
* `fsWrite` {integer}
* `ipcSent` {integer}
* `ipcReceived` {integer}
* `signalsCount` {integer}
* `voluntaryContextSwitches` {integer}
* `involuntaryContextSwitches` {integer}

The `process.resourceUsage()` method returns the resource usage
for the current process.
All of these values come from the `uv_getrusage` call which returns
[this struct][uv_rusage_t], here the mapping between node and libuv:
- `userCPUTime` maps to `ru_utime` computed in microseconds.
It is the values as [`process.cpuUsage().user`][process.cpuUsage]
- `systemCPUTime` maps to `ru_stime` computed in microseconds.
It is the value as [`process.cpuUsage().system`][process.cpuUsage]
- `maxRSS` maps to `ru_maxrss` which is the maximum resident set size
used (in kilobytes).
- `sharedMemorySize` maps to `ru_ixrss` but is not supported by any platform.
- `unsharedDataSize` maps to `ru_idrss` but is not supported by any platform.
- `unsharedStackSize` maps to `ru_isrss` but is not supported by any platform.
- `minorPageFault` maps to `ru_minflt` which is the number of minor page fault
for the process, see [this article for more details][wikipedia_minor_fault]
- `majorPageFault` maps to `ru_majflt` which is the number of major page fault
for the process, see [this article for more details][wikipedia_major_fault].
This field is not supported on Windows platforms.
- `swappedOut` maps to `ru_nswap` which is not supported by any platform.
- `fsRead` maps to `ru_inblock` which is the number of times the file system
had to perform input.
- `fsWrite` maps to `ru_oublock` which is the number of times the file system
had to perform output.
- `ipcSent` maps to `ru_msgsnd` but is not supported by any platform.
- `ipcReceived` maps to `ru_msgrcv` but is not supported by any platform.
- `signalsCount` maps to `ru_nsignals` but is not supported by any platform.
- `voluntaryContextSwitches` maps to `ru_nvcsw` which is the number of times
a CPU context switch resulted due to a process voluntarily giving up the
processor before its time slice was completed
(usually to await availability of a resource).
This field is not supported on Windows platforms.
- `involuntaryContextSwitches` maps to `ru_nivcsw` which is the number of times
a CPU context switch resulted due to a higher priority process becoming runnable
or because the current process exceeded its time slice.
This field is not supported on Windows platforms.


```js
console.log(process.resourceUsage());
/*
Will output:
{
userCPUTime: 82872,
systemCPUTime: 4143,
maxRSS: 33164,
sharedMemorySize: 0,
unsharedDataSize: 0,
unsharedStackSize: 0,
minorPageFault: 2469,
majorPageFault: 0,
swapedOut: 0,
fsRead: 0,
fsWrite: 8,
ipcSent: 0,
ipcReceived: 0,
signalsCount: 0,
voluntaryContextSwitches: 79,
involuntaryContextSwitches: 1
}
*/
```

## process.send(message[, sendHandle[, options]][, callback])
<!-- YAML
added: v0.5.9
Expand Down Expand Up @@ -2329,6 +2414,10 @@ cases:
[Writable]: stream.html#stream_writable_streams
[debugger]: debugger.html
[note on process I/O]: process.html#process_a_note_on_process_i_o
[process.cpuUsage]: #process_process_cpuusage_previousvalue
[process_emit_warning]: #process_process_emitwarning_warning_type_code_ctor
[process_warning]: #process_event_warning
[report documentation]: report.html
[uv_rusage_t]: http://docs.libuv.org/en/v1.x/misc.html#c.uv_rusage_t
[wikipedia_minor_fault]: https://en.wikipedia.org/wiki/Page_fault#Minor
[wikipedia_major_fault]: https://en.wikipedia.org/wiki/Page_fault#Major
1 change: 1 addition & 0 deletions lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ if (isMainThread) {
process.hrtime = wrapped.hrtime;
process.hrtime.bigint = wrapped.hrtimeBigInt;
process.cpuUsage = wrapped.cpuUsage;
process.resourceUsage = wrapped.resourceUsage;
process.memoryUsage = wrapped.memoryUsage;
process.kill = wrapped.kill;
process.exit = wrapped.exit;
Expand Down
27 changes: 26 additions & 1 deletion lib/internal/process/per_thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ function wrapProcessMethods(binding) {
hrtime: _hrtime,
hrtimeBigInt: _hrtimeBigInt,
cpuUsage: _cpuUsage,
memoryUsage: _memoryUsage
memoryUsage: _memoryUsage,
resourceUsage: _resourceUsage
} = binding;

function _rawDebug(...args) {
Expand Down Expand Up @@ -190,12 +191,36 @@ function wrapProcessMethods(binding) {
return true;
}

const resourceValues = new Float64Array(16);
function resourceUsage() {
_resourceUsage(resourceValues);
return {
userCPUTime: resourceValues[0],
systemCPUTime: resourceValues[1],
maxRSS: resourceValues[2],
sharedMemorySize: resourceValues[3],
unsharedDataSize: resourceValues[4],
unsharedStackSize: resourceValues[5],
minorPageFault: resourceValues[6],
majorPageFault: resourceValues[7],
swappedOut: resourceValues[8],
fsRead: resourceValues[9],
fsWrite: resourceValues[10],
ipcSent: resourceValues[11],
ipcReceived: resourceValues[12],
signalsCount: resourceValues[13],
voluntaryContextSwitches: resourceValues[14],
involuntaryContextSwitches: resourceValues[15]
};
}


return {
_rawDebug,
hrtime,
hrtimeBigInt,
cpuUsage,
resourceUsage,
memoryUsage,
kill,
exit
Expand Down
33 changes: 33 additions & 0 deletions src/node_process_methods.cc
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,38 @@ void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
Array::New(env->isolate(), handle_v.data(), handle_v.size()));
}

static void ResourceUsage(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

uv_rusage_t rusage;
int err = uv_getrusage(&rusage);
if (err)
return env->ThrowUVException(err, "uv_getrusage");

CHECK(args[0]->IsFloat64Array());
Local<Float64Array> array = args[0].As<Float64Array>();
CHECK_EQ(array->Length(), 16);
Local<ArrayBuffer> ab = array->Buffer();
double* fields = static_cast<double*>(ab->GetContents().Data());

fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec;
fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec;
fields[2] = rusage.ru_maxrss;
fields[3] = rusage.ru_ixrss;
fields[4] = rusage.ru_idrss;
fields[5] = rusage.ru_isrss;
fields[6] = rusage.ru_minflt;
fields[7] = rusage.ru_majflt;
fields[8] = rusage.ru_nswap;
fields[9] = rusage.ru_inblock;
fields[10] = rusage.ru_oublock;
fields[11] = rusage.ru_msgsnd;
fields[12] = rusage.ru_msgrcv;
fields[13] = rusage.ru_nsignals;
fields[14] = rusage.ru_nvcsw;
fields[15] = rusage.ru_nivcsw;
}

#ifdef __POSIX__
static void DebugProcess(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Expand Down Expand Up @@ -418,6 +450,7 @@ static void InitializeProcessMethods(Local<Object> target,
env->SetMethod(target, "cpuUsage", CPUUsage);
env->SetMethod(target, "hrtime", Hrtime);
env->SetMethod(target, "hrtimeBigInt", HrtimeBigInt);
env->SetMethod(target, "resourceUsage", ResourceUsage);

env->SetMethod(target, "_getActiveRequests", GetActiveRequests);
env->SetMethod(target, "_getActiveHandles", GetActiveHandles);
Expand Down
27 changes: 27 additions & 0 deletions test/parallel/test-resource-usage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';
require('../common');
const assert = require('assert');

const rusage = process.resourceUsage();

[
'userCPUTime',
'systemCPUTime',
'maxRSS',
'sharedMemorySize',
'unsharedDataSize',
'unsharedStackSize',
'minorPageFault',
'majorPageFault',
'swappedOut',
'fsRead',
'fsWrite',
'ipcSent',
'ipcReceived',
'signalsCount',
'voluntaryContextSwitches',
'involuntaryContextSwitches'
].forEach((n) => {
assert.strictEqual(typeof rusage[n], 'number', `${n} should be a number`);
assert(rusage[n] >= 0, `${n} should be above or equal 0`);
});

0 comments on commit 19f9281

Please sign in to comment.