perf_hooks: implement histogram based api

Add a sampling-based event loop delay monitor.

```js
const { monitorEventLoopDelay } = require('perf_hooks');

const h = monitorEventLoopDelay();

h.enable();

h.disable();

console.log(h.percentiles);
console.log(h.min);
console.log(h.max);
console.log(h.mean);
console.log(h.stddev);
console.log(h.percentile(50));
```

PR-URL: https://github.com/nodejs/node/pull/25378
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
James M Snell 2019-01-07 11:36:35 -08:00 committed by jasnell
parent 679c23f2ae
commit bcdd228f90
No known key found for this signature in database
GPG key ID: 7341B15C070877AC
18 changed files with 2207 additions and 3 deletions

View file

@ -1,6 +1,7 @@
'use strict';
const {
ELDHistogram: _ELDHistogram,
PerformanceEntry,
mark: _mark,
clearMark: _clearMark,
@ -35,6 +36,8 @@ const { AsyncResource } = require('async_hooks');
const L = require('internal/linkedlist');
const kInspect = require('internal/util').customInspectSymbol;
const kHandle = Symbol('handle');
const kMap = Symbol('map');
const kCallback = Symbol('callback');
const kTypes = Symbol('types');
const kEntries = Symbol('entries');
@ -545,9 +548,73 @@ function sortedInsert(list, entry) {
list.splice(location, 0, entry);
}
class ELDHistogram {
constructor(handle) {
this[kHandle] = handle;
this[kMap] = new Map();
}
reset() { this[kHandle].reset(); }
enable() { return this[kHandle].enable(); }
disable() { return this[kHandle].disable(); }
get exceeds() { return this[kHandle].exceeds(); }
get min() { return this[kHandle].min(); }
get max() { return this[kHandle].max(); }
get mean() { return this[kHandle].mean(); }
get stddev() { return this[kHandle].stddev(); }
percentile(percentile) {
if (typeof percentile !== 'number') {
const errors = lazyErrors();
throw new errors.ERR_INVALID_ARG_TYPE('percentile', 'number', percentile);
}
if (percentile <= 0 || percentile > 100) {
const errors = lazyErrors();
throw new errors.ERR_INVALID_ARG_VALUE.RangeError('percentile',
percentile);
}
return this[kHandle].percentile(percentile);
}
get percentiles() {
this[kMap].clear();
this[kHandle].percentiles(this[kMap]);
return this[kMap];
}
[kInspect]() {
return {
min: this.min,
max: this.max,
mean: this.mean,
stddev: this.stddev,
percentiles: this.percentiles,
exceeds: this.exceeds
};
}
}
function monitorEventLoopDelay(options = {}) {
if (typeof options !== 'object' || options === null) {
const errors = lazyErrors();
throw new errors.ERR_INVALID_ARG_TYPE('options', 'Object', options);
}
const { resolution = 10 } = options;
if (typeof resolution !== 'number') {
const errors = lazyErrors();
throw new errors.ERR_INVALID_ARG_TYPE('options.resolution',
'number', resolution);
}
if (resolution <= 0 || !Number.isSafeInteger(resolution)) {
const errors = lazyErrors();
throw new errors.ERR_INVALID_OPT_VALUE.RangeError('resolution', resolution);
}
return new ELDHistogram(new _ELDHistogram(resolution));
}
module.exports = {
performance,
PerformanceObserver
PerformanceObserver,
monitorEventLoopDelay
};
Object.defineProperty(module.exports, 'constants', {