Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/perf_hooks.md114
-rw-r--r--doc/api/worker_threads.md12
-rw-r--r--lib/internal/histogram.js145
-rw-r--r--lib/perf_hooks.js29
-rw-r--r--src/env.h3
-rw-r--r--src/histogram-inl.h43
-rw-r--r--src/histogram.cc315
-rw-r--r--src/histogram.h149
-rw-r--r--src/node.cc1
-rw-r--r--src/node_http2.cc1
-rw-r--r--src/node_perf.cc184
-rw-r--r--src/node_perf.h37
-rw-r--r--src/node_worker.cc1
-rw-r--r--test/parallel/test-perf-hooks-histogram.js69
-rw-r--r--tools/doc/type-parser.js4
15 files changed, 784 insertions, 323 deletions
diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md
index bbd8a3c1a99..9354c40e68f 100644
--- a/doc/api/perf_hooks.md
+++ b/doc/api/perf_hooks.md
@@ -653,6 +653,22 @@ performance.mark('test');
performance.mark('meow');
```
+## `perf_hooks.createHistogram([options])`
+<!-- YAML
+added: REPLACEME
+-->
+
+* `options` {Object}
+ * `min` {number|bigint} The minimum recordable value. Must be an integer
+ value greater than 0. **Defaults**: `1`.
+ * `max` {number|bigint} The maximum recordable value. Must be an integer
+ value greater than `min`. **Defaults**: `Number.MAX_SAFE_INTEGER`.
+ * `figures` {number} The number of accuracy digits. Must be a number between
+ `1` and `5`. **Defaults**: `3`.
+* Returns {RecordableHistogram}
+
+Returns a {RecordableHistogram}.
+
## `perf_hooks.monitorEventLoopDelay([options])`
<!-- YAML
added: v11.10.0
@@ -661,12 +677,12 @@ added: v11.10.0
* `options` {Object}
* `resolution` {number} The sampling rate in milliseconds. Must be greater
than zero. **Default:** `10`.
-* Returns: {Histogram}
+* Returns: {IntervalHistogram}
_This property is an extension by Node.js. It is not available in Web browsers._
-Creates a `Histogram` object that samples and reports the event loop delay
-over time. The delays will be reported in nanoseconds.
+Creates an `IntervalHistogram` object that samples and reports the event loop
+delay over time. The delays will be reported in nanoseconds.
Using a timer to detect approximate event loop delay works because the
execution of timers is tied specifically to the lifecycle of the libuv
@@ -689,36 +705,12 @@ console.log(h.percentile(50));
console.log(h.percentile(99));
```
-### Class: `Histogram`
-<!-- YAML
-added: v11.10.0
--->
-Tracks the event loop delay at a given sampling rate. The constructor of
-this class not exposed to users.
-
-_This property is an extension by Node.js. It is not available in Web browsers._
-
-#### `histogram.disable()`
-<!-- YAML
-added: v11.10.0
--->
-
-* Returns: {boolean}
-
-Disables the event loop delay sample timer. Returns `true` if the timer was
-stopped, `false` if it was already stopped.
-
-#### `histogram.enable()`
+## Class: `Histogram`
<!-- YAML
added: v11.10.0
-->
-* Returns: {boolean}
-
-Enables the event loop delay sample timer. Returns `true` if the timer was
-started, `false` if it was already started.
-
-#### `histogram.exceeds`
+### `histogram.exceeds`
<!-- YAML
added: v11.10.0
-->
@@ -728,7 +720,7 @@ added: v11.10.0
The number of times the event loop delay exceeded the maximum 1 hour event
loop delay threshold.
-#### `histogram.max`
+### `histogram.max`
<!-- YAML
added: v11.10.0
-->
@@ -737,7 +729,7 @@ added: v11.10.0
The maximum recorded event loop delay.
-#### `histogram.mean`
+### `histogram.mean`
<!-- YAML
added: v11.10.0
-->
@@ -746,7 +738,7 @@ added: v11.10.0
The mean of the recorded event loop delays.
-#### `histogram.min`
+### `histogram.min`
<!-- YAML
added: v11.10.0
-->
@@ -755,7 +747,7 @@ added: v11.10.0
The minimum recorded event loop delay.
-#### `histogram.percentile(percentile)`
+### `histogram.percentile(percentile)`
<!-- YAML
added: v11.10.0
-->
@@ -765,7 +757,7 @@ added: v11.10.0
Returns the value at the given percentile.
-#### `histogram.percentiles`
+### `histogram.percentiles`
<!-- YAML
added: v11.10.0
-->
@@ -774,14 +766,14 @@ added: v11.10.0
Returns a `Map` object detailing the accumulated percentile distribution.
-#### `histogram.reset()`
+### `histogram.reset()`
<!-- YAML
added: v11.10.0
-->
Resets the collected histogram data.
-#### `histogram.stddev`
+### `histogram.stddev`
<!-- YAML
added: v11.10.0
-->
@@ -790,6 +782,56 @@ added: v11.10.0
The standard deviation of the recorded event loop delays.
+## Class: `IntervalHistogram extends Histogram`
+
+A `Histogram` that is periodically updated on a given interval.
+
+### `histogram.disable()`
+<!-- YAML
+added: v11.10.0
+-->
+
+* Returns: {boolean}
+
+Disables the update interval timer. Returns `true` if the timer was
+stopped, `false` if it was already stopped.
+
+### `histogram.enable()`
+<!-- YAML
+added: v11.10.0
+-->
+
+* Returns: {boolean}
+
+Enables the update interval timer. Returns `true` if the timer was
+started, `false` if it was already started.
+
+### Cloning an `IntervalHistogram`
+
+{IntervalHistogram} instances can be cloned via {MessagePort}. On the receiving
+end, the histogram is cloned as a plain {Histogram} object that does not
+implement the `enable()` and `disable()` methods.
+
+## Class: `RecordableHistogram extends Histogram`
+<!-- YAML
+added: REPLACEME
+-->
+
+### `histogram.record(val)`
+<!-- YAML
+added: REPLACEME
+-->
+
+* `val` {number|bigint} The amount to record in the histogram.
+
+### `histogram.recordDelta()`
+<!-- YAML
+added: REPLACEME
+-->
+
+Calculates the amount of time (in nanoseconds) that has passed since the
+previous call to `recordDelta()` and records that amount in the histogram.
+
## Examples
### Measuring the duration of async operations
diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md
index 4c6e590c2a7..95167da0a91 100644
--- a/doc/api/worker_threads.md
+++ b/doc/api/worker_threads.md
@@ -474,6 +474,9 @@ are part of the channel.
<!-- YAML
added: v10.5.0
changes:
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/37155
+ description: Add 'Histogram' types to the list of cloneable types.
- version: v15.6.0
pr-url: https://github.com/nodejs/node/pull/36804
description: Added `X509Certificate` to the list of cloneable types.
@@ -507,8 +510,13 @@ In particular, the significant differences to `JSON` are:
* `value` may contain typed arrays, both using `ArrayBuffer`s
and `SharedArrayBuffer`s.
* `value` may contain [`WebAssembly.Module`][] instances.
-* `value` may not contain native (C++-backed) objects other than {MessagePort}s,
- {FileHandle}s, {KeyObject}s, {CryptoKey}s, and {X509Certificate}s.
+* `value` may not contain native (C++-backed) objects other than:
+ * {CryptoKey}s,
+ * {FileHandle}s,
+ * {Histogram}s,
+ * {KeyObject}s,
+ * {MessagePort}s,
+ * {X509Certificate}s.
```js
const { MessageChannel } = require('worker_threads');
diff --git a/lib/internal/histogram.js b/lib/internal/histogram.js
index c9c72dc8b3b..7a8d79ce4a6 100644
--- a/lib/internal/histogram.js
+++ b/lib/internal/histogram.js
@@ -1,38 +1,71 @@
'use strict';
const {
+ NumberIsNaN,
+ NumberIsInteger,
+ NumberMAX_SAFE_INTEGER,
+ ObjectSetPrototypeOf,
+ SafeMap,
+ Symbol,
+ TypeError,
+} = primordials;
+
+const {
+ Histogram: _Histogram
+} = internalBinding('performance');
+
+const {
customInspectSymbol: kInspect,
} = require('internal/util');
-const { format } = require('util');
-const { NumberIsNaN, SafeMap, Symbol } = primordials;
+const { inspect } = require('util');
+
+const {
+ codes: {
+ ERR_INVALID_ARG_VALUE,
+ ERR_INVALID_ARG_TYPE,
+ ERR_OUT_OF_RANGE,
+ },
+} = require('internal/errors');
-const { ERR_INVALID_ARG_VALUE } = require('internal/errors').codes;
-const { validateNumber } = require('internal/validators');
+const {
+ validateNumber,
+} = require('internal/validators');
const kDestroy = Symbol('kDestroy');
const kHandle = Symbol('kHandle');
+const kMap = Symbol('kMap');
-// Histograms are created internally by Node.js and used to
-// record various metrics. This Histogram class provides a
-// generally read-only view of the internal histogram.
-class Histogram {
- #map = new SafeMap();
+const {
+ kClone,
+ kDeserialize,
+ JSTransferable,
+} = require('internal/worker/js_transferable');
+class Histogram extends JSTransferable {
constructor(internal) {
+ super();
this[kHandle] = internal;
+ this[kMap] = new SafeMap();
}
- [kInspect]() {
- const obj = {
+ [kInspect](depth, options) {
+ if (depth < 0)
+ return this;
+
+ const opts = {
+ ...options,
+ depth: options.depth == null ? null : options.depth - 1
+ };
+
+ return `Histogram ${inspect({
min: this.min,
max: this.max,
mean: this.mean,
exceeds: this.exceeds,
stddev: this.stddev,
percentiles: this.percentiles,
- };
- return `Histogram ${format(obj)}`;
+ }, opts)}`;
}
get min() {
@@ -65,9 +98,9 @@ class Histogram {
}
get percentiles() {
- this.#map.clear();
- this[kHandle]?.percentiles(this.#map);
- return this.#map;
+ this[kMap].clear();
+ this[kHandle]?.percentiles(this[kMap]);
+ return this[kMap];
}
reset() {
@@ -77,10 +110,90 @@ class Histogram {
[kDestroy]() {
this[kHandle] = undefined;
}
+
+ [kClone]() {
+ const handle = this[kHandle];
+ return {
+ data: { handle },
+ deserializeInfo: 'internal/histogram:InternalHistogram'
+ };
+ }
+
+ [kDeserialize]({ handle }) {
+ this[kHandle] = handle;
+ }
+}
+
+class RecordableHistogram extends Histogram {
+ constructor() {
+ // eslint-disable-next-line no-restricted-syntax
+ throw new TypeError('illegal constructor');
+ }
+
+ record(val) {
+ if (typeof val === 'bigint') {
+ this[kHandle]?.record(val);
+ return;
+ }
+
+ if (!NumberIsInteger(val))
+ throw new ERR_INVALID_ARG_TYPE('val', ['integer', 'bigint'], val);
+
+ if (val < 1 || val > NumberMAX_SAFE_INTEGER)
+ throw new ERR_OUT_OF_RANGE('val', 'a safe integer greater than 0', val);
+
+ this[kHandle]?.record(val);
+ }
+
+ recordDelta() {
+ this[kHandle]?.recordDelta();
+ }
+
+ [kClone]() {
+ const handle = this[kHandle];
+ return {
+ data: { handle },
+ deserializeInfo: 'internal/histogram:InternalRecordableHistogram'
+ };
+ }
+}
+
+class InternalHistogram extends JSTransferable {
+ constructor(handle) {
+ super();
+ this[kHandle] = handle;
+ this[kMap] = new SafeMap();
+ }
+}
+
+class InternalRecordableHistogram extends JSTransferable {
+ constructor(handle) {
+ super();
+ this[kHandle] = handle;
+ this[kMap] = new SafeMap();
+ }
+}
+
+InternalHistogram.prototype.constructor = Histogram;
+ObjectSetPrototypeOf(
+ InternalHistogram.prototype,
+ Histogram.prototype);
+
+InternalRecordableHistogram.prototype.constructor = RecordableHistogram;
+ObjectSetPrototypeOf(
+ InternalRecordableHistogram.prototype,
+ RecordableHistogram.prototype);
+
+function createHistogram() {
+ return new InternalRecordableHistogram(new _Histogram());
}
module.exports = {
Histogram,
+ RecordableHistogram,
+ InternalHistogram,
+ InternalRecordableHistogram,
kDestroy,
kHandle,
+ createHistogram,
};
diff --git a/lib/perf_hooks.js b/lib/perf_hooks.js
index 4572ec88446..5cacbea43b9 100644
--- a/lib/perf_hooks.js
+++ b/lib/perf_hooks.js
@@ -16,6 +16,7 @@ const {
ObjectKeys,
SafeSet,
Symbol,
+ TypeError,
} = primordials;
const {
@@ -65,6 +66,7 @@ const {
const {
Histogram,
+ createHistogram,
kHandle,
} = require('internal/histogram');
@@ -86,6 +88,7 @@ const kInsertEntry = Symbol('insert-entry');
const kGetEntries = Symbol('get-entries');
const kIndex = Symbol('index');
const kMarks = Symbol('marks');
+const kEnabled = Symbol('kEnabled');
const observers = {};
const observerableTypes = [
@@ -630,9 +633,26 @@ function sortedInsert(list, entry) {
}
class ELDHistogram extends Histogram {
- constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
- enable() { return this[kHandle].enable(); }
- disable() { return this[kHandle].disable(); }
+ constructor(i) {
+ if (!(i instanceof _ELDHistogram)) {
+ // eslint-disable-next-line no-restricted-syntax
+ throw new TypeError('illegal constructor');
+ }
+ super(i);
+ this[kEnabled] = false;
+ }
+ enable() {
+ if (this[kEnabled]) return false;
+ this[kEnabled] = true;
+ this[kHandle].start();
+ return true;
+ }
+ disable() {
+ if (!this[kEnabled]) return false;
+ this[kEnabled] = false;
+ this[kHandle].stop();
+ return true;
+ }
}
function monitorEventLoopDelay(options = {}) {
@@ -651,7 +671,8 @@ function monitorEventLoopDelay(options = {}) {
module.exports = {
performance,
PerformanceObserver,
- monitorEventLoopDelay
+ monitorEventLoopDelay,
+ createHistogram,
};
ObjectDefineProperty(module.exports, 'constants', {
diff --git a/src/env.h b/src/env.h
index 9e4e11e1056..f3bb8f51dda 100644
--- a/src/env.h
+++ b/src/env.h
@@ -449,11 +449,12 @@ constexpr size_t kFsStatsBufferLength =
V(filehandlereadwrap_template, v8::ObjectTemplate) \
V(fsreqpromise_constructor_template, v8::ObjectTemplate) \
V(handle_wrap_ctor_template, v8::FunctionTemplate) \
- V(histogram_instance_template, v8::ObjectTemplate) \
+ V(histogram_ctor_template, v8::FunctionTemplate) \
V(http2settings_constructor_template, v8::ObjectTemplate) \
V(http2stream_constructor_template, v8::ObjectTemplate) \
V(http2ping_constructor_template, v8::ObjectTemplate) \
V(i18n_converter_template, v8::ObjectTemplate) \
+ V(intervalhistogram_constructor_template, v8::FunctionTemplate) \
V(libuv_stream_wrap_ctor_template, v8::FunctionTemplate) \
V(message_port_constructor_template, v8::FunctionTemplate) \
V(microtask_queue_ctor_template, v8::FunctionTemplate) \
diff --git a/src/histogram-inl.h b/src/histogram-inl.h
index 58911dae8f2..18a1668512e 100644
--- a/src/histogram-inl.h
+++ b/src/histogram-inl.h
@@ -10,30 +10,34 @@
namespace node {
void Histogram::Reset() {
+ Mutex::ScopedLock lock(mutex_);
hdr_reset(histogram_.get());
-}
-
-bool Histogram::Record(int64_t value) {
- return hdr_record_value(histogram_.get(), value);
+ exceeds_ = 0;
+ prev_ = 0;
}
int64_t Histogram::Min() {
+ Mutex::ScopedLock lock(mutex_);
return hdr_min(histogram_.get());
}
int64_t Histogram::Max() {
+ Mutex::ScopedLock lock(mutex_);
return hdr_max(histogram_.get());
}
double Histogram::Mean() {
+ Mutex::ScopedLock lock(mutex_);
return hdr_mean(histogram_.get());
}
double Histogram::Stddev() {
+ Mutex::ScopedLock lock(mutex_);
return hdr_stddev(histogram_.get());
}
double Histogram::Percentile(double percentile) {
+ Mutex::ScopedLock lock(mutex_);
CHECK_GT(percentile, 0);
CHECK_LE(percentile, 100);
return static_cast<double>(
@@ -42,6 +46,7 @@ double Histogram::Percentile(double percentile) {
template <typename Iterator>
void Histogram::Percentiles(Iterator&& fn) {
+ Mutex::ScopedLock lock(mutex_);
hdr_iter iter;
hdr_iter_percentile_init(&iter, histogram_.get(), 1);
while (hdr_iter_next(&iter)) {
@@ -51,29 +56,29 @@ void Histogram::Percentiles(Iterator&& fn) {
}
}
-bool HistogramBase::RecordDelta() {
+bool Histogram::Record(int64_t value) {
+ Mutex::ScopedLock lock(mutex_);
+ return hdr_record_value(histogram_.get(), value);
+}
+
+uint64_t Histogram::RecordDelta() {
+ Mutex::ScopedLock lock(mutex_);
uint64_t time = uv_hrtime();
- bool ret = true;
+ uint64_t delta = 0;
if (prev_ > 0) {
- int64_t delta = time - prev_;
+ delta = time - prev_;
if (delta > 0) {
- ret = Record(delta);
- TraceDelta(delta);
- if (!ret) {
- if (exceeds_ < 0xFFFFFFFF)
- exceeds_++;
- TraceExceeds(delta);
- }
+ if (!hdr_record_value(histogram_.get(), delta) && exceeds_ < 0xFFFFFFFF)
+ exceeds_++;
}
}
prev_ = time;
- return ret;
+ return delta;
}
-void HistogramBase::ResetState() {
- Reset();
- exceeds_ = 0;
- prev_ = 0;
+size_t Histogram::GetMemorySize() const {
+ Mutex::ScopedLock lock(mutex_);
+ return hdr_get_memory_size(histogram_.get());
}
} // namespace node
diff --git a/src/histogram.cc b/src/histogram.cc
index 8d1eb77b1bc..d21cf2883a0 100644
--- a/src/histogram.cc
+++ b/src/histogram.cc
@@ -1,15 +1,17 @@
#include "histogram.h" // NOLINT(build/include_inline)
#include "histogram-inl.h"
+#include "base_object-inl.h"
#include "memory_tracker-inl.h"
-
+#include "node_errors.h"
namespace node {
+using v8::BigInt;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Local;
using v8::Map;
using v8::Number;
-using v8::ObjectTemplate;
+using v8::Object;
using v8::String;
using v8::Value;
@@ -19,71 +21,88 @@ Histogram::Histogram(int64_t lowest, int64_t highest, int figures) {
histogram_.reset(histogram);
}
+void Histogram::MemoryInfo(MemoryTracker* tracker) const {
+ tracker->TrackFieldWithSize("histogram", GetMemorySize());
+}
+
+HistogramImpl::HistogramImpl(int64_t lowest, int64_t highest, int figures)
+ : histogram_(new Histogram(lowest, highest, figures)) {}
+
+HistogramImpl::HistogramImpl(std::shared_ptr<Histogram> histogram)
+ : histogram_(std::move(histogram)) {}
+
HistogramBase::HistogramBase(
Environment* env,
- v8::Local<v8::Object> wrap,
+ Local<Object> wrap,
int64_t lowest,
int64_t highest,
int figures)
: BaseObject(env, wrap),
- Histogram(lowest, highest, figures) {
+ HistogramImpl(lowest, highest, figures) {
+ MakeWeak();
+}
+
+HistogramBase::HistogramBase(
+ Environment* env,
+ Local<Object> wrap,
+ std::shared_ptr<Histogram> histogram)
+ : BaseObject(env, wrap),
+ HistogramImpl(std::move(histogram)) {
MakeWeak();
}
void HistogramBase::MemoryInfo(MemoryTracker* tracker) const {
- tracker->TrackFieldWithSize("histogram", GetMemorySize());
+ tracker->TrackField("histogram", histogram());
}
void HistogramBase::GetMin(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- double value = static_cast<double>(histogram->Min());
+ double value = static_cast<double>((*histogram)->Min());
args.GetReturnValue().Set(value);
}
void HistogramBase::GetMax(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- double value = static_cast<double>(histogram->Max());
+ double value = static_cast<double>((*histogram)->Max());
args.GetReturnValue().Set(value);
}
void HistogramBase::GetMean(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- args.GetReturnValue().Set(histogram->Mean());
+ args.GetReturnValue().Set((*histogram)->Mean());
}
void HistogramBase::GetExceeds(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- double value = static_cast<double>(histogram->Exceeds());
+ double value = static_cast<double>((*histogram)->Exceeds());
args.GetReturnValue().Set(value);
}
void HistogramBase::GetStddev(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- args.GetReturnValue().Set(histogram->Stddev());
+ args.GetReturnValue().Set((*histogram)->Stddev());
}
-void HistogramBase::GetPercentile(
- const FunctionCallbackInfo<Value>& args) {
+void HistogramBase::GetPercentile(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
CHECK(args[0]->IsNumber());
double percentile = args[0].As<Number>()->Value();
- args.GetReturnValue().Set(histogram->Percentile(percentile));
+ args.GetReturnValue().Set((*histogram)->Percentile(percentile));
}
-void HistogramBase::GetPercentiles(
- const FunctionCallbackInfo<Value>& args) {
+void HistogramBase::GetPercentiles(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
CHECK(args[0]->IsMap());
Local<Map> map = args[0].As<Map>();
- histogram->Percentiles([map, env](double key, double value) {
+ (*histogram)->Percentiles([map, env](double key, double value) {
map->Set(
env->context(),
Number::New(env->isolate(), key),
@@ -94,48 +113,254 @@ void HistogramBase::GetPercentiles(
void HistogramBase::DoReset(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- histogram->ResetState();
+ (*histogram)->Reset();
+}
+
+void HistogramBase::RecordDelta(const FunctionCallbackInfo<Value>& args) {
+ HistogramBase* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ (*histogram)->RecordDelta();
}
-BaseObjectPtr<HistogramBase> HistogramBase::New(
+void HistogramBase::Record(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ CHECK_IMPLIES(!args[0]->IsNumber(), args[0]->IsBigInt());
+ bool lossless = true;
+ int64_t value = args[0]->IsBigInt()
+ ? args[0].As<BigInt>()->Int64Value(&lossless)
+ : static_cast<int64_t>(args[0].As<Number>()->Value());
+ if (!lossless || value < 1)
+ return THROW_ERR_OUT_OF_RANGE(env, "value is out of range");
+ HistogramBase* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ (*histogram)->Record(value);
+}
+
+BaseObjectPtr<HistogramBase> HistogramBase::Create(
Environment* env,
int64_t lowest,
int64_t highest,
int figures) {
- CHECK_LE(lowest, highest);
- CHECK_GT(figures, 0);
- v8::Local<v8::Object> obj;
- auto tmpl = env->histogram_instance_template();
- if (!tmpl->NewInstance(env->context()).ToLocal(&obj))
- return {};
-
- return MakeDetachedBaseObject<HistogramBase>(
+ Local<Object> obj;
+ if (!GetConstructorTemplate(env)
+ ->InstanceTemplate()
+ ->NewInstance(env->context()).ToLocal(&obj)) {
+ return BaseObjectPtr<HistogramBase>();
+ }
+
+ return MakeBaseObject<HistogramBase>(
env, obj, lowest, highest, figures);
}
-void HistogramBase::Initialize(Environment* env) {
- // Guard against multiple initializations
- if (!env->histogram_instance_template().IsEmpty())
- return;
+BaseObjectPtr<HistogramBase> HistogramBase::Create(
+ Environment* env,
+ std::shared_ptr<Histogram> histogram) {
+ Local<Object> obj;
+ if (!GetConstructorTemplate(env)
+ ->InstanceTemplate()
+ ->NewInstance(env->context()).ToLocal(&obj)) {
+ return BaseObjectPtr<HistogramBase>();
+ }
+ return MakeBaseObject<HistogramBase>(env, obj, std::move(histogram));
+}
+
+void HistogramBase::New(const FunctionCallbackInfo<Value>& args) {
+ CHECK(args.IsConstructCall());
+ Environment* env = Environment::GetCurrent(args);
+ new HistogramBase(env, args.This());
+}
+
+Local<FunctionTemplate> HistogramBase::GetConstructorTemplate(
+ Environment* env) {
+ Local<FunctionTemplate> tmpl = env->histogram_ctor_template();
+ if (tmpl.IsEmpty()) {
+ tmpl = env->NewFunctionTemplate(New);
+ Local<String> classname =
+ FIXED_ONE_BYTE_STRING(env->isolate(), "Histogram");
+ tmpl->SetClassName(classname);
+ tmpl->Inherit(BaseObject::GetConstructorTemplate(env));
+
+ tmpl->InstanceTemplate()->SetInternalFieldCount(
+ HistogramBase::kInternalFieldCount);
+ env->SetProtoMethodNoSideEffect(tmpl, "exceeds", GetExceeds);
+ env->SetProtoMethodNoSideEffect(tmpl, "min", GetMin);
+ env->SetProtoMethodNoSideEffect(tmpl, "max", GetMax);
+ env->SetProtoMethodNoSideEffect(tmpl, "mean", GetMean);
+ env->SetProtoMethodNoSideEffect(tmpl, "stddev", GetStddev);
+ env->SetProtoMethodNoSideEffect(tmpl, "percentile", GetPercentile);
+ env->SetProtoMethodNoSideEffect(tmpl, "percentiles", GetPercentiles);
+ env->SetProtoMethod(tmpl, "reset", DoReset);
+ env->SetProtoMethod(tmpl, "record", Record);
+ env->SetProtoMethod(tmpl, "recordDelta", RecordDelta);
+ env->set_histogram_ctor_template(tmpl);
+ }
+ return tmpl;
+}
+
+void HistogramBase::Initialize(Environment* env, Local<Object> target) {
+ env->SetConstructorFunction(target, "Histogram", GetConstructorTemplate(env));
+}
+
+BaseObjectPtr<BaseObject> HistogramBase::HistogramTransferData::Deserialize(
+ Environment* env,
+ v8::Local<v8::Context> context,
+ std::unique_ptr<worker::TransferData> self) {
+ return Create(env, std::move(histogram_));
+}
+
+std::unique_ptr<worker::TransferData> HistogramBase::CloneForMessaging() const {
+ return std::make_unique<HistogramTransferData>(this);
+}
+
+void HistogramBase::HistogramTransferData::MemoryInfo(
+ MemoryTracker* tracker) const {
+ tracker->TrackField("histogram", histogram_);
+}
+
+Local<FunctionTemplate> IntervalHistogram::GetConstructorTemplate(
+ Environment* env) {
+ Local<FunctionTemplate> tmpl = env->intervalhistogram_constructor_template();
+ if (tmpl.IsEmpty()) {
+ tmpl = FunctionTemplate::New(env->isolate());
+ tmpl->Inherit(HandleWrap::GetConstructorTemplate(env));
+ tmpl->InstanceTemplate()->SetInternalFieldCount(
+ HistogramBase::kInternalFieldCount);
+ env->SetProtoMethodNoSideEffect(tmpl, "exceeds", GetExceeds);
+ env->SetProtoMethodNoSideEffect(tmpl, "min", GetMin);
+ env->SetProtoMethodNoSideEffect(tmpl, "max", GetMax);
+ env->SetProtoMethodNoSideEffect(tmpl, "mean", GetMean);
+ env->SetProtoMethodNoSideEffect(tmpl, "stddev", GetStddev);
+ env->SetProtoMethodNoSideEffect(tmpl, "percentile", GetPercentile);
+ env->SetProtoMethodNoSideEffect(tmpl, "percentiles", GetPercentiles);
+ env->SetProtoMethod(tmpl, "reset", DoReset);
+ env->SetProtoMethod(tmpl, "start", Start);
+ env->SetProtoMethod(tmpl, "stop", Stop);
+ env->set_intervalhistogram_constructor_template(tmpl);
+ }
+ return tmpl;
+}
+
+IntervalHistogram::IntervalHistogram(
+ Environment* env,
+ Local<Object> wrap,
+ AsyncWrap::ProviderType type,
+ int32_t interval,
+ int64_t lowest,
+ int64_t highest,
+ int figures)
+ : HandleWrap(
+ env,
+ wrap,
+ reinterpret_cast<uv_handle_t*>(&timer_),
+ type),
+ HistogramImpl(lowest, highest, figures),
+ interval_(interval) {
+ MakeWeak();
+ uv_timer_init(env->event_loop(), &timer_);
+}
+
+void IntervalHistogram::TimerCB(uv_timer_t* handle) {
+ IntervalHistogram* histogram =
+ ContainerOf(&IntervalHistogram::timer_, handle);
+ histogram->OnInterval();
+}
+
+void IntervalHistogram::MemoryInfo(MemoryTracker* tracker) const {
+ tracker->TrackField("histogram", histogram());
+}
+
+void IntervalHistogram::OnStart(StartFlags flags) {
+ if (enabled_ || IsHandleClosing()) return;
+ enabled_ = true;
+ if (flags == StartFlags::RESET)
+ histogram()->Reset();
+ uv_timer_start(&timer_, TimerCB, interval_, interval_);
+ uv_unref(reinterpret_cast<uv_handle_t*>(&timer_));
+}
+
+void IntervalHistogram::OnStop() {
+ if (!enabled_ || IsHandleClosing()) return;
+ enabled_ = false;
+ uv_timer_stop(&timer_);
+}
+
+void IntervalHistogram::Start(const FunctionCallbackInfo<Value>& args) {
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ histogram->OnStart(args[0]->IsTrue() ? StartFlags::RESET : StartFlags::NONE);
+}
+
+void IntervalHistogram::Stop(const FunctionCallbackInfo<Value>& args) {
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ histogram->OnStop();
+}
+
+void IntervalHistogram::GetMin(const FunctionCallbackInfo<Value>& args) {
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ double value = static_cast<double>((*histogram)->Min());
+ args.GetReturnValue().Set(value);
+}
+
+void IntervalHistogram::GetMax(const FunctionCallbackInfo<Value>& args) {
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ double value = static_cast<double>((*histogram)->Max());
+ args.GetReturnValue().Set(value);
+}
+
+void IntervalHistogram::GetMean(const FunctionCallbackInfo<Value>& args) {
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ args.GetReturnValue().Set((*histogram)->Mean());
+}
+
+void IntervalHistogram::GetExceeds(const FunctionCallbackInfo<Value>& args) {
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ double value = static_cast<double>((*histogram)->Exceeds());
+ args.GetReturnValue().Set(value);
+}
- Local<FunctionTemplate> histogram = FunctionTemplate::New(env->isolate());
- Local<String> classname = FIXED_ONE_BYTE_STRING(env->isolate(), "Histogram");
- histogram->SetClassName(classname);
+void IntervalHistogram::GetStddev(const FunctionCallbackInfo<Value>& args) {
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ args.GetReturnValue().Set((*histogram)->Stddev());
+}
- Local<ObjectTemplate> histogramt =
- histogram->InstanceTemplate();
+void IntervalHistogram::GetPercentile(const FunctionCallbackInfo<Value>& args) {
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ CHECK(args[0]->IsNumber());
+ double percentile = args[0].As<Number>()->Value();
+ args.GetReturnValue().Set((*histogram)->Percentile(percentile));
+}
- histogramt->SetInternalFieldCount(1);
- env->SetProtoMethod(histogram, "exceeds", HistogramBase::GetExceeds);
- env->SetProtoMethod(histogram, "min", HistogramBase::GetMin);
- env->SetProtoMethod(histogram, "max", HistogramBase::GetMax);
- env->SetProtoMethod(histogram, "mean", HistogramBase::GetMean);
- env->SetProtoMethod(histogram, "stddev", HistogramBase::GetStddev);
- env->SetProtoMethod(histogram, "percentile", HistogramBase::GetPercentile);
- env->SetProtoMethod(histogram, "percentiles", HistogramBase::GetPercentiles);
- env->SetProtoMethod(histogram, "reset", HistogramBase::DoReset);
+void IntervalHistogram::GetPercentiles(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ CHECK(args[0]->IsMap());
+ Local<Map> map = args[0].As<Map>();
+ (*histogram)->Percentiles([map, env](double key, double value) {
+ map->Set(
+ env->context(),
+ Number::New(env->isolate(), key),
+ Number::New(env->isolate(), value)).IsEmpty();
+ });
+}
+
+void IntervalHistogram::DoReset(const FunctionCallbackInfo<Value>& args) {
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ (*histogram)->Reset();
+}
- env->set_histogram_instance_template(histogramt);
+std::unique_ptr<worker::TransferData>
+IntervalHistogram::CloneForMessaging() const {
+ return std::make_unique<HistogramBase::HistogramTransferData>(histogram());
}
} // namespace node
diff --git a/src/histogram.h b/src/histogram.h
index e92c31c4724..8c164f54cfd 100644
--- a/src/histogram.h
+++ b/src/histogram.h
@@ -5,20 +5,25 @@
#include "hdr_histogram.h"
#include "base_object.h"
+#include "memory_tracker.h"
+#include "node_messaging.h"
#include "util.h"
+#include "v8.h"
+#include "uv.h"
#include <functional>
#include <limits>
#include <map>
+#include <string>
namespace node {
constexpr int kDefaultHistogramFigures = 3;
-class Histogram {
+class Histogram : public MemoryRetainer {
public:
Histogram(
- int64_t lowest = std::numeric_limits<int64_t>::min(),
+ int64_t lowest = 1,
int64_t highest = std::numeric_limits<int64_t>::max(),
int figures = kDefaultHistogramFigures);
virtual ~Histogram() = default;
@@ -30,32 +35,61 @@ class Histogram {
inline double Mean();
inline double Stddev();
inline double Percentile(double percentile);
+ inline int64_t Exceeds() const { return exceeds_; }
+
+ inline uint64_t RecordDelta();
// Iterator is a function type that takes two doubles as argument, one for
// percentile and one for the value at that percentile.
template <typename Iterator>
inline void Percentiles(Iterator&& fn);
- size_t GetMemorySize() const {
- return hdr_get_memory_size(histogram_.get());
- }
+ inline size_t GetMemorySize() const;
+
+ void MemoryInfo(MemoryTracker* tracker) const override;
+ SET_MEMORY_INFO_NAME(Histogram)
+ SET_SELF_SIZE(Histogram)
private:
using HistogramPointer = DeleteFnPtr<hdr_histogram, hdr_close>;
HistogramPointer histogram_;
+ int64_t exceeds_ = 0;
+ uint64_t prev_ = 0;
+
+ Mutex mutex_;
};
-class HistogramBase : public BaseObject, public Histogram {
+class HistogramImpl {
public:
- virtual ~HistogramBase() = default;
+ HistogramImpl(int64_t lowest, int64_t highest, int figures);
+ explicit HistogramImpl(std::shared_ptr<Histogram> histogram);
- virtual void TraceDelta(int64_t delta) {}
- virtual void TraceExceeds(int64_t delta) {}
+ Histogram* operator->() { return histogram_.get(); }
- inline bool RecordDelta();
- inline void ResetState();
+ protected:
+ const std::shared_ptr<Histogram>& histogram() const { return histogram_; }
- int64_t Exceeds() const { return exceeds_; }
+ private:
+ std::shared_ptr<Histogram> histogram_;
+};
+
+class HistogramBase : public BaseObject, public HistogramImpl {
+ public:
+ static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
+ Environment* env);
+ static void Initialize(Environment* env, v8::Local<v8::Object> target);
+
+ static BaseObjectPtr<HistogramBase> Create(
+ Environment* env,
+ int64_t lowest = 1,
+ int64_t highest = std::numeric_limits<int64_t>::max(),
+ int figures = kDefaultHistogramFigures);
+
+ static BaseObjectPtr<HistogramBase> Create(
+ Environment* env,
+ std::shared_ptr<Histogram> histogram);
+
+ static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(HistogramBase)
@@ -71,24 +105,103 @@ class HistogramBase : public BaseObject, public Histogram {
static void GetPercentiles(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void DoReset(const v8::FunctionCallbackInfo<v8::Value>& args);
- static void Initialize(Environment* env);
+ static void Record(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void RecordDelta(const v8::FunctionCallbackInfo<v8::Value>& args);
- static BaseObjectPtr<HistogramBase> New(
+ HistogramBase(
Environment* env,
- int64_t lowest = std::numeric_limits<int64_t>::min(),
+ v8::Local<v8::Object> wrap,
+ int64_t lowest = 1,
int64_t highest = std::numeric_limits<int64_t>::max(),
int figures = kDefaultHistogramFigures);
HistogramBase(
Environment* env,
v8::Local<v8::Object> wrap,
- int64_t lowest = std::numeric_limits<int64_t>::min(),
+ std::shared_ptr<Histogram> histogram);
+
+ TransferMode GetTransferMode() const override {
+ return TransferMode::kCloneable;
+ }
+ std::unique_ptr<worker::TransferData> CloneForMessaging() const override;
+
+ class HistogramTransferData : public worker::TransferData {
+ public:
+ explicit HistogramTransferData(const HistogramBase* histogram)
+ : histogram_(histogram->histogram()) {}
+
+ explicit HistogramTransferData(std::shared_ptr<Histogram> histogram)
+ : histogram_(std::move(histogram)) {}
+
+ BaseObjectPtr<BaseObject> Deserialize(
+ Environment* env,
+ v8::Local<v8::Context> context,
+ std::unique_ptr<worker::TransferData> self) override;
+
+ void MemoryInfo(MemoryTracker* tracker) const override;
+ SET_MEMORY_INFO_NAME(HistogramTransferData)
+ SET_SELF_SIZE(HistogramTransferData)
+
+ private:
+ std::shared_ptr<Histogram> histogram_;
+ };
+};
+
+class IntervalHistogram : public HandleWrap, public HistogramImpl {
+ public:
+ enum class StartFlags {
+ NONE,
+ RESET
+ };
+
+ static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
+ Environment* env);
+
+ static BaseObjectPtr<IntervalHistogram> Create(
+ Environment* env,
+ int64_t lowest = 1,
+ int64_t highest = std::numeric_limits<int64_t>::max(),
+ int figures = kDefaultHistogramFigures);
+
+ virtual void OnInterval() = 0;
+
+ void MemoryInfo(MemoryTracker* tracker) const override;
+
+ IntervalHistogram(
+ Environment* env,
+ v8::Local<v8::Object> wrap,
+ AsyncWrap::ProviderType type,
+ int32_t interval,
+ int64_t lowest = 1,
int64_t highest = std::numeric_limits<int64_t>::max(),
int figures = kDefaultHistogramFigures);
+ static void GetMin(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetMax(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetMean(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetExceeds(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetStddev(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetPercentile(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetPercentiles(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void DoReset(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Stop(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ TransferMode GetTransferMode() const override {
+ return TransferMode::kCloneable;
+ }
+ std::unique_ptr<worker::TransferData> CloneForMessaging() const override;
+
private:
- int64_t exceeds_ = 0;
- uint64_t prev_ = 0;
+ static void TimerCB(uv_timer_t* handle);
+ void OnStart(StartFlags flags = StartFlags::RESET);
+ void OnStop();
+
+ bool enabled_ = false;
+ int32_t interval_ = 0;
+ uv_timer_t timer_;
};
} // namespace node
diff --git a/src/node.cc b/src/node.cc
index c2ef9235d50..5d8ed679484 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -26,6 +26,7 @@
#include "debug_utils-inl.h"
#include "env-inl.h"
#include "memory_tracker-inl.h"
+#include "histogram-inl.h"
#include "node_binding.h"
#include "node_errors.h"
#include "node_internals.h"
diff --git a/src/node_http2.cc b/src/node_http2.cc
index a4b8e4d2ac7..ccff6dc4ca6 100644
--- a/src/node_http2.cc
+++ b/src/node_http2.cc
@@ -2,6 +2,7 @@
#include "allocated_buffer-inl.h"
#include "aliased_struct-inl.h"
#include "debug_utils-inl.h"
+#include "histogram-inl.h"
#include "memory_tracker-inl.h"
#include "node.h"
#include "node_buffer.h"
diff --git a/src/node_perf.cc b/src/node_perf.cc
index e7ffd31445f..73fc31394bd 100644
--- a/src/node_perf.cc
+++ b/src/node_perf.cc
@@ -1,4 +1,5 @@
#include "aliased_buffer.h"
+#include "histogram-inl.h"
#include "memory_tracker-inl.h"
#include "node_internals.h"
#include "node_perf.h"
@@ -19,10 +20,10 @@ using v8::FunctionTemplate;
using v8::GCCallbackFlags;
using v8::GCType;
using v8::HandleScope;
+using v8::Int32;
using v8::Integer;
using v8::Isolate;
using v8::Local;
-using v8::Map;
using v8::MaybeLocal;
using v8::Number;
using v8::Object;
@@ -446,156 +447,45 @@ void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(1.0 * idle_time / 1e6);
}
-
// Event Loop Timing Histogram
-namespace {
-static void ELDHistogramMin(const FunctionCallbackInfo<Value>& args) {
- ELDHistogram* histogram;
- ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- double value = static_cast<double>(histogram->Min());
- args.GetReturnValue().Set(value);
-}
-
-static void ELDHistogramMax(const FunctionCallbackInfo<Value>& args) {
- ELDHistogram* histogram;
- ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- double value = static_cast<double>(histogram->Max());
- args.GetReturnValue().Set(value);
-}
-
-static void ELDHistogramMean(const FunctionCallbackInfo<Value>& args) {
- ELDHistogram* histogram;
- ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- args.GetReturnValue().Set(histogram->Mean());
-}
-
-static void ELDHistogramExceeds(const FunctionCallbackInfo<Value>& args) {
- ELDHistogram* histogram;
- ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- double value = static_cast<double>(histogram->Exceeds());
- args.GetReturnValue().Set(value);
-}
-
-static void ELDHistogramStddev(const FunctionCallbackInfo<Value>& args) {
- ELDHistogram* histogram;
- ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- args.GetReturnValue().Set(histogram->Stddev());
-}
-
-static void ELDHistogramPercentile(const FunctionCallbackInfo<Value>& args) {
- ELDHistogram* histogram;
- ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- CHECK(args[0]->IsNumber());
- double percentile = args[0].As<Number>()->Value();
- args.GetReturnValue().Set(histogram->Percentile(percentile));
-}
-
-static void ELDHistogramPercentiles(const FunctionCallbackInfo<Value>& args) {
- Environment* env = Environment::GetCurrent(args);
- ELDHistogram* histogram;
- ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- CHECK(args[0]->IsMap());
- Local<Map> map = args[0].As<Map>();
- histogram->Percentiles([&](double key, double value) {
- map->Set(env->context(),
- Number::New(env->isolate(), key),
- Number::New(env->isolate(), value)).IsEmpty();
- });
-}
-
-static void ELDHistogramEnable(const FunctionCallbackInfo<Value>& args) {
- ELDHistogram* histogram;
- ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- args.GetReturnValue().Set(histogram->Enable());
-}
-
-static void ELDHistogramDisable(const FunctionCallbackInfo<Value>& args) {
- ELDHistogram* histogram;
- ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- args.GetReturnValue().Set(histogram->Disable());
-}
-
-static void ELDHistogramReset(const FunctionCallbackInfo<Value>& args) {
- ELDHistogram* histogram;
- ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
- histogram->ResetState();
-}
-
-static void ELDHistogramNew(const FunctionCallbackInfo<Value>& args) {
+void ELDHistogram::New(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK(args.IsConstructCall());
- int32_t resolution = args[0]->IntegerValue(env->context()).FromJust();
+ int32_t resolution = args[0].As<Int32>()->Value();
CHECK_GT(resolution, 0);
new ELDHistogram(env, args.This(), resolution);
}
-} // namespace
+
+void ELDHistogram::Initialize(Environment* env, Local<Object> target) {
+ Local<FunctionTemplate> tmpl = env->NewFunctionTemplate(New);
+ tmpl->Inherit(IntervalHistogram::GetConstructorTemplate(env));
+ tmpl->InstanceTemplate()->SetInternalFieldCount(
+ ELDHistogram::kInternalFieldCount);
+ env->SetConstructorFunction(target, "ELDHistogram", tmpl);
+}
ELDHistogram::ELDHistogram(
Environment* env,
Local<Object> wrap,
- int32_t resolution) : HandleWrap(env,
- wrap,
- reinterpret_cast<uv_handle_t*>(&timer_),
- AsyncWrap::PROVIDER_ELDHISTOGRAM),
- Histogram(1, 3.6e12),
- resolution_(resolution) {
- MakeWeak();
- uv_timer_init(env->event_loop(), &timer_);
-}
-
-void ELDHistogram::DelayIntervalCallback(uv_timer_t* req) {
- ELDHistogram* histogram = ContainerOf(&ELDHistogram::timer_, req);
- histogram->RecordDelta();
+ int32_t interval)
+ : IntervalHistogram(
+ env,
+ wrap,
+ AsyncWrap::PROVIDER_ELDHISTOGRAM,
+ interval, 1, 3.6e12, 3) {}
+
+void ELDHistogram::OnInterval() {
+ uint64_t delta = histogram()->RecordDelta();
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
- "min", histogram->Min());
+ "delay", delta);
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
- "max", histogram->Max());
+ "min", histogram()->Min());
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
- "mean", histogram->Mean());
+ "max", histogram()->Max());
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
- "stddev", histogram->Stddev());
-}
-
-bool ELDHistogram::RecordDelta() {
- uint64_t time = uv_hrtime();
- bool ret = true;
- if (prev_ > 0) {
- int64_t delta = time - prev_;
- if (delta > 0) {
- ret = Record(delta);
- TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
- "delay", delta);
- if (!ret) {
- if (exceeds_ < 0xFFFFFFFF)
- exceeds_++;
- ProcessEmitWarning(
- env(),
- "Event loop delay exceeded 1 hour: %" PRId64 " nanoseconds",
- delta);
- }
- }
- }
- prev_ = time;
- return ret;
-}
-
-bool ELDHistogram::Enable() {
- if (enabled_ || IsHandleClosing()) return false;
- enabled_ = true;
- prev_ = 0;
- uv_timer_start(&timer_,
- DelayIntervalCallback,
- resolution_,
- resolution_);
- uv_unref(reinterpret_cast<uv_handle_t*>(&timer_));
- return true;
-}
-
-bool ELDHistogram::Disable() {
- if (!enabled_ || IsHandleClosing()) return false;
- enabled_ = false;
- uv_timer_stop(&timer_);
- return true;
+ "mean", histogram()->Mean());
+ TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+ "stddev", histogram()->Stddev());
}
void Initialize(Local<Object> target,
@@ -688,24 +578,8 @@ void Initialize(Local<Object> target,
constants,
attr).ToChecked();
- Local<String> eldh_classname = FIXED_ONE_BYTE_STRING(isolate, "ELDHistogram");
- Local<FunctionTemplate> eldh =
- env->NewFunctionTemplate(ELDHistogramNew);
- eldh->SetClassName(eldh_classname);
- eldh->InstanceTemplate()->SetInternalFieldCount(
- ELDHistogram::kInternalFieldCount);
- eldh->Inherit(BaseObject::GetConstructorTemplate(env));
- env->SetProtoMethod(eldh, "exceeds", ELDHistogramExceeds);
- env->SetProtoMethod(eldh, "min", ELDHistogramMin);
- env->SetProtoMethod(eldh, "max", ELDHistogramMax);
- env->SetProtoMethod(eldh, "mean", ELDHistogramMean);
- env->SetProtoMethod(eldh, "stddev", ELDHistogramStddev);
- env->SetProtoMethod(eldh, "percentile", ELDHistogramPercentile);
- env->SetProtoMethod(eldh, "percentiles", ELDHistogramPercentiles);
- env->SetProtoMethod(eldh, "enable", ELDHistogramEnable);
- env->SetProtoMethod(eldh, "disable", ELDHistogramDisable);
- env->SetProtoMethod(eldh, "reset", ELDHistogramReset);
- env->SetConstructorFunction(target, eldh_classname, eldh);
+ HistogramBase::Initialize(env, target);
+ ELDHistogram::Initialize(env, target);
}
} // namespace performance
diff --git a/src/node_perf.h b/src/node_perf.h
index a8a913bddea..ddd99b33040 100644
--- a/src/node_perf.h
+++ b/src/node_perf.h
@@ -6,7 +6,7 @@
#include "node.h"
#include "node_perf_common.h"
#include "base_object-inl.h"
-#include "histogram-inl.h"
+#include "histogram.h"
#include "v8.h"
#include "uv.h"
@@ -140,37 +140,20 @@ class GCPerformanceEntry : public PerformanceEntry {
PerformanceGCFlags gcflags_;
};
-class ELDHistogram : public HandleWrap, public Histogram {
+class ELDHistogram : public IntervalHistogram {
public:
- ELDHistogram(Environment* env,
- v8::Local<v8::Object> wrap,
- int32_t resolution);
-
- bool RecordDelta();
- bool Enable();
- bool Disable();
- void ResetState() {
- Reset();
- exceeds_ = 0;
- prev_ = 0;
- }
- int64_t Exceeds() const { return exceeds_; }
+ static void Initialize(Environment* env, v8::Local<v8::Object> target);
+ static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
- void MemoryInfo(MemoryTracker* tracker) const override {
- tracker->TrackFieldWithSize("histogram", GetMemorySize());
- }
+ ELDHistogram(
+ Environment* env,
+ v8::Local<v8::Object> wrap,
+ int32_t interval);
+
+ void OnInterval() override;
SET_MEMORY_INFO_NAME(ELDHistogram)
SET_SELF_SIZE(ELDHistogram)
-
- private:
- static void DelayIntervalCallback(uv_timer_t* req);
-
- bool enabled_ = false;
- int32_t resolution_ = 0;
- int64_t exceeds_ = 0;
- uint64_t prev_ = 0;
- uv_timer_t timer_;
};
} // namespace performance
diff --git a/src/node_worker.cc b/src/node_worker.cc
index 6f7ccc33ba5..c1a0c6668bf 100644
--- a/src/node_worker.cc
+++ b/src/node_worker.cc
@@ -1,5 +1,6 @@
#include "node_worker.h"
#include "debug_utils-inl.h"
+#include "histogram-inl.h"
#include "memory_tracker-inl.h"
#include "node_errors.h"
#include "node_external_reference.h"
diff --git a/test/parallel/test-perf-hooks-histogram.js b/test/parallel/test-perf-hooks-histogram.js
new file mode 100644
index 00000000000..f8c373bbd4c
--- /dev/null
+++ b/test/parallel/test-perf-hooks-histogram.js
@@ -0,0 +1,69 @@
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+const {
+ createHistogram,
+ monitorEventLoopDelay,
+} = require('perf_hooks');
+
+{
+ const h = createHistogram();
+
+ assert.strictEqual(h.min, 9223372036854776000);
+ assert.strictEqual(h.max, 0);
+ assert.strictEqual(h.exceeds, 0);
+ assert(Number.isNaN(h.mean));
+ assert(Number.isNaN(h.stddev));
+
+ h.record(1);
+
+ [false, '', {}, undefined, null].forEach((i) => {
+ assert.throws(() => h.record(i), {
+ code: 'ERR_INVALID_ARG_TYPE'
+ });
+ });
+ assert.throws(() => h.record(0, Number.MAX_SAFE_INTEGER + 1), {
+ code: 'ERR_OUT_OF_RANGE'
+ });
+
+ assert.strictEqual(h.min, 1);
+ assert.strictEqual(h.max, 1);
+ assert.strictEqual(h.exceeds, 0);
+ assert.strictEqual(h.mean, 1);
+ assert.strictEqual(h.stddev, 0);
+
+ assert.strictEqual(h.percentile(1), 1);
+ assert.strictEqual(h.percentile(100), 1);
+
+ const mc = new MessageChannel();
+ mc.port1.onmessage = common.mustCall(({ data }) => {
+ assert.strictEqual(h.min, 1);
+ assert.strictEqual(h.max, 1);
+ assert.strictEqual(h.exceeds, 0);
+ assert.strictEqual(h.mean, 1);
+ assert.strictEqual(h.stddev, 0);
+
+ data.record(2n);
+ data.recordDelta();
+
+ assert.strictEqual(h.max, 2);
+
+ mc.port1.close();
+ });
+ mc.port2.postMessage(h);
+}
+
+{
+ const e = monitorEventLoopDelay();
+ e.enable();
+ const mc = new MessageChannel();
+ mc.port1.onmessage = common.mustCall(({ data }) => {
+ assert(typeof data.min, 'number');
+ assert(data.min > 0);
+ assert.strictEqual(data.disable, undefined);
+ assert.strictEqual(data.enable, undefined);
+ mc.port1.close();
+ });
+ setTimeout(() => mc.port2.postMessage(e), 100);
+}
diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js
index 70a3fede3d5..c5362867959 100644
--- a/tools/doc/type-parser.js
+++ b/tools/doc/type-parser.js
@@ -182,6 +182,10 @@ const customTypesMap = {
'os.constants.dlopen': 'os.html#os_dlopen_constants',
'Histogram': 'perf_hooks.html#perf_hooks_class_histogram',
+ 'IntervalHistogram':
+ 'perf_hooks.html#perf_hooks_class_intervalhistogram_extends_histogram',
+ 'RecordableHistogram':
+ 'perf_hooks.html#perf_hooks_class_recordablehistogram_extends_histogram',
'PerformanceEntry': 'perf_hooks.html#perf_hooks_class_performanceentry',
'PerformanceNodeTiming':
'perf_hooks.html#perf_hooks_class_performancenodetiming',