Files
node/src/node_perf.cc
T
Trevor Norris 589b2a1244 perf_hooks: add idleTime and event loop util
Use uv_metrics_idle_time() to return a high resolution millisecond timer
of the amount of time the event loop has been idle since it was
initialized.

Include performance.eventLoopUtilization() API to handle the math of
calculating the idle and active times. This has been added to prevent
accidental miscalculations of the event loop utilization. Such as not
taking into consideration offsetting nodeTiming.loopStart or timing
differences when being called from a Worker thread.

PR-URL: https://github.com/nodejs/node/pull/34938
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Adrian Estrada <edsadr@gmail.com>
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
2020-08-29 07:02:31 -07:00

717 lines
25 KiB
C++

#include "aliased_buffer.h"
#include "memory_tracker-inl.h"
#include "node_internals.h"
#include "node_perf.h"
#include "node_buffer.h"
#include "node_process.h"
#include "util-inl.h"
#include <cinttypes>
namespace node {
namespace performance {
using v8::Context;
using v8::DontDelete;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::GCCallbackFlags;
using v8::GCType;
using v8::HandleScope;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Map;
using v8::MaybeLocal;
using v8::Number;
using v8::Object;
using v8::PropertyAttribute;
using v8::ReadOnly;
using v8::String;
using v8::Value;
// Microseconds in a millisecond, as a float.
#define MICROS_PER_MILLIS 1e3
// https://w3c.github.io/hr-time/#dfn-time-origin
const uint64_t timeOrigin = PERFORMANCE_NOW();
// https://w3c.github.io/hr-time/#dfn-time-origin-timestamp
const double timeOriginTimestamp = GetCurrentTimeInMicroseconds();
uint64_t performance_v8_start;
PerformanceState::PerformanceState(Isolate* isolate,
const PerformanceState::SerializeInfo* info)
: root(isolate,
sizeof(performance_state_internal),
MAYBE_FIELD_PTR(info, root)),
milestones(isolate,
offsetof(performance_state_internal, milestones),
NODE_PERFORMANCE_MILESTONE_INVALID,
root,
MAYBE_FIELD_PTR(info, milestones)),
observers(isolate,
offsetof(performance_state_internal, observers),
NODE_PERFORMANCE_ENTRY_TYPE_INVALID,
root,
MAYBE_FIELD_PTR(info, observers)) {
if (info == nullptr) {
for (size_t i = 0; i < milestones.Length(); i++) milestones[i] = -1.;
}
}
PerformanceState::SerializeInfo PerformanceState::Serialize(
v8::Local<v8::Context> context, v8::SnapshotCreator* creator) {
SerializeInfo info{root.Serialize(context, creator),
milestones.Serialize(context, creator),
observers.Serialize(context, creator)};
return info;
}
void PerformanceState::Deserialize(v8::Local<v8::Context> context) {
root.Deserialize(context);
// This is just done to set up the pointers, we will actually reset
// all the milestones after deserialization.
milestones.Deserialize(context);
observers.Deserialize(context);
}
std::ostream& operator<<(std::ostream& o,
const PerformanceState::SerializeInfo& i) {
o << "{\n"
<< " " << i.root << ", // root\n"
<< " " << i.milestones << ", // milestones\n"
<< " " << i.observers << ", // observers\n"
<< "}";
return o;
}
void PerformanceState::Mark(enum PerformanceMilestone milestone,
uint64_t ts) {
this->milestones[milestone] = ts;
TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
TRACING_CATEGORY_NODE1(bootstrap),
GetPerformanceMilestoneName(milestone),
TRACE_EVENT_SCOPE_THREAD, ts / 1000);
}
// Initialize the performance entry object properties
inline void InitObject(const PerformanceEntry& entry, Local<Object> obj) {
Environment* env = entry.env();
Isolate* isolate = env->isolate();
Local<Context> context = env->context();
PropertyAttribute attr =
static_cast<PropertyAttribute>(ReadOnly | DontDelete);
obj->DefineOwnProperty(context,
env->name_string(),
String::NewFromUtf8(isolate,
entry.name().c_str())
.ToLocalChecked(),
attr)
.Check();
obj->DefineOwnProperty(context,
env->entry_type_string(),
String::NewFromUtf8(isolate,
entry.type().c_str())
.ToLocalChecked(),
attr)
.Check();
obj->DefineOwnProperty(context,
env->start_time_string(),
Number::New(isolate, entry.startTime()),
attr).Check();
obj->DefineOwnProperty(context,
env->duration_string(),
Number::New(isolate, entry.duration()),
attr).Check();
}
// Create a new PerformanceEntry object
MaybeLocal<Object> PerformanceEntry::ToObject() const {
Local<Object> obj;
if (!env_->performance_entry_template()
->NewInstance(env_->context())
.ToLocal(&obj)) {
return MaybeLocal<Object>();
}
InitObject(*this, obj);
return obj;
}
// Allow creating a PerformanceEntry object from JavaScript
void PerformanceEntry::New(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
Utf8Value name(isolate, args[0]);
Utf8Value type(isolate, args[1]);
uint64_t now = PERFORMANCE_NOW();
PerformanceEntry entry(env, *name, *type, now, now);
Local<Object> obj = args.This();
InitObject(entry, obj);
PerformanceEntry::Notify(env, entry.kind(), obj);
}
// Pass the PerformanceEntry object to the PerformanceObservers
void PerformanceEntry::Notify(Environment* env,
PerformanceEntryType type,
Local<Value> object) {
Context::Scope scope(env->context());
AliasedUint32Array& observers = env->performance_state()->observers;
if (!env->performance_entry_callback().IsEmpty() &&
type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID && observers[type]) {
node::MakeCallback(env->isolate(),
object.As<Object>(),
env->performance_entry_callback(),
1, &object,
node::async_context{0, 0});
}
}
// Create a User Timing Mark
void Mark(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
HandleScope scope(env->isolate());
Utf8Value name(env->isolate(), args[0]);
uint64_t now = PERFORMANCE_NOW();
auto marks = env->performance_marks();
(*marks)[*name] = now;
TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP(
TRACING_CATEGORY_NODE2(perf, usertiming),
*name, now / 1000);
PerformanceEntry entry(env, *name, "mark", now, now);
Local<Object> obj;
if (!entry.ToObject().ToLocal(&obj)) return;
PerformanceEntry::Notify(env, entry.kind(), obj);
args.GetReturnValue().Set(obj);
}
void ClearMark(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
auto marks = env->performance_marks();
if (args.Length() == 0) {
marks->clear();
} else {
Utf8Value name(env->isolate(), args[0]);
marks->erase(*name);
}
}
inline uint64_t GetPerformanceMark(Environment* env, const std::string& name) {
auto marks = env->performance_marks();
auto res = marks->find(name);
return res != marks->end() ? res->second : 0;
}
// Create a User Timing Measure. A Measure is a PerformanceEntry that
// measures the duration between two distinct user timing marks
void Measure(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
HandleScope scope(env->isolate());
Utf8Value name(env->isolate(), args[0]);
Utf8Value startMark(env->isolate(), args[1]);
AliasedFloat64Array& milestones = env->performance_state()->milestones;
uint64_t startTimestamp = timeOrigin;
uint64_t start = GetPerformanceMark(env, *startMark);
if (start != 0) {
startTimestamp = start;
} else {
PerformanceMilestone milestone = ToPerformanceMilestoneEnum(*startMark);
if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
startTimestamp = milestones[milestone];
}
uint64_t endTimestamp = 0;
if (args[2]->IsUndefined()) {
endTimestamp = PERFORMANCE_NOW();
} else {
Utf8Value endMark(env->isolate(), args[2]);
endTimestamp = GetPerformanceMark(env, *endMark);
if (endTimestamp == 0) {
PerformanceMilestone milestone = ToPerformanceMilestoneEnum(*endMark);
if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
endTimestamp = milestones[milestone];
}
}
if (endTimestamp < startTimestamp)
endTimestamp = startTimestamp;
TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
TRACING_CATEGORY_NODE2(perf, usertiming),
*name, *name, startTimestamp / 1000);
TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
TRACING_CATEGORY_NODE2(perf, usertiming),
*name, *name, endTimestamp / 1000);
PerformanceEntry entry(env, *name, "measure", startTimestamp, endTimestamp);
Local<Object> obj;
if (!entry.ToObject().ToLocal(&obj)) return;
PerformanceEntry::Notify(env, entry.kind(), obj);
args.GetReturnValue().Set(obj);
}
// Allows specific Node.js lifecycle milestones to be set from JavaScript
void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Context> context = env->context();
PerformanceMilestone milestone =
static_cast<PerformanceMilestone>(
args[0]->Int32Value(context).ToChecked());
if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
env->performance_state()->Mark(milestone);
}
void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK(args[0]->IsFunction());
env->set_performance_entry_callback(args[0].As<Function>());
}
// Creates a GC Performance Entry and passes it to observers
void PerformanceGCCallback(Environment* env,
std::unique_ptr<GCPerformanceEntry> entry) {
HandleScope scope(env->isolate());
Local<Context> context = env->context();
AliasedUint32Array& observers = env->performance_state()->observers;
if (observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) {
Local<Object> obj;
if (!entry->ToObject().ToLocal(&obj)) return;
PropertyAttribute attr =
static_cast<PropertyAttribute>(ReadOnly | DontDelete);
obj->DefineOwnProperty(context,
env->kind_string(),
Integer::New(env->isolate(), entry->gckind()),
attr).Check();
obj->DefineOwnProperty(context,
env->flags_string(),
Integer::New(env->isolate(), entry->gcflags()),
attr).Check();
PerformanceEntry::Notify(env, entry->kind(), obj);
}
}
// Marks the start of a GC cycle
void MarkGarbageCollectionStart(Isolate* isolate,
GCType type,
GCCallbackFlags flags,
void* data) {
Environment* env = static_cast<Environment*>(data);
env->performance_state()->performance_last_gc_start_mark = PERFORMANCE_NOW();
}
// Marks the end of a GC cycle
void MarkGarbageCollectionEnd(Isolate* isolate,
GCType type,
GCCallbackFlags flags,
void* data) {
Environment* env = static_cast<Environment*>(data);
PerformanceState* state = env->performance_state();
// If no one is listening to gc performance entries, do not create them.
if (!state->observers[NODE_PERFORMANCE_ENTRY_TYPE_GC])
return;
auto entry = std::make_unique<GCPerformanceEntry>(
env,
static_cast<PerformanceGCKind>(type),
static_cast<PerformanceGCFlags>(flags),
state->performance_last_gc_start_mark,
PERFORMANCE_NOW());
env->SetImmediate([entry = std::move(entry)](Environment* env) mutable {
PerformanceGCCallback(env, std::move(entry));
}, CallbackFlags::kUnrefed);
}
void GarbageCollectionCleanupHook(void* data) {
Environment* env = static_cast<Environment*>(data);
env->isolate()->RemoveGCPrologueCallback(MarkGarbageCollectionStart, data);
env->isolate()->RemoveGCEpilogueCallback(MarkGarbageCollectionEnd, data);
}
static void InstallGarbageCollectionTracking(
const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart,
static_cast<void*>(env));
env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd,
static_cast<void*>(env));
env->AddCleanupHook(GarbageCollectionCleanupHook, env);
}
static void RemoveGarbageCollectionTracking(
const FunctionCallbackInfo<Value> &args) {
Environment* env = Environment::GetCurrent(args);
env->RemoveCleanupHook(GarbageCollectionCleanupHook, env);
GarbageCollectionCleanupHook(env);
}
// Gets the name of a function
inline Local<Value> GetName(Local<Function> fn) {
Local<Value> val = fn->GetDebugName();
if (val.IsEmpty() || val->IsUndefined()) {
Local<Value> boundFunction = fn->GetBoundFunction();
if (!boundFunction.IsEmpty() && !boundFunction->IsUndefined()) {
val = GetName(boundFunction.As<Function>());
}
}
return val;
}
// Executes a wrapped Function and captures timing information, causing a
// Function PerformanceEntry to be emitted to PerformanceObservers after
// execution.
void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Environment* env = Environment::GetCurrent(context);
CHECK_NOT_NULL(env);
Local<Function> fn = args.Data().As<Function>();
size_t count = args.Length();
size_t idx;
SlicedArguments call_args(args);
Utf8Value name(isolate, GetName(fn));
bool is_construct_call = args.IsConstructCall();
uint64_t start = PERFORMANCE_NOW();
TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
TRACING_CATEGORY_NODE2(perf, timerify),
*name, *name, start / 1000);
v8::MaybeLocal<Value> ret;
if (is_construct_call) {
ret = fn->NewInstance(context, call_args.length(), call_args.out())
.FromMaybe(Local<Object>());
} else {
ret = fn->Call(context, args.This(), call_args.length(), call_args.out());
}
uint64_t end = PERFORMANCE_NOW();
TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
TRACING_CATEGORY_NODE2(perf, timerify),
*name, *name, end / 1000);
if (ret.IsEmpty())
return;
args.GetReturnValue().Set(ret.ToLocalChecked());
AliasedUint32Array& observers = env->performance_state()->observers;
if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION])
return;
PerformanceEntry entry(env, *name, "function", start, end);
Local<Object> obj;
if (!entry.ToObject().ToLocal(&obj)) return;
for (idx = 0; idx < count; idx++)
obj->Set(context, idx, args[idx]).Check();
PerformanceEntry::Notify(env, entry.kind(), obj);
}
// Wraps a Function in a TimerFunctionCall
void Timerify(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Context> context = env->context();
CHECK(args[0]->IsFunction());
CHECK(args[1]->IsNumber());
Local<Function> fn = args[0].As<Function>();
int length = args[1]->IntegerValue(context).ToChecked();
Local<Function> wrap =
Function::New(context, TimerFunctionCall, fn, length).ToLocalChecked();
args.GetReturnValue().Set(wrap);
}
// Notify a custom PerformanceEntry to observers
void Notify(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Utf8Value type(env->isolate(), args[0]);
Local<Value> entry = args[1];
PerformanceEntryType entry_type = ToPerformanceEntryTypeEnum(*type);
AliasedUint32Array& observers = env->performance_state()->observers;
if (entry_type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
observers[entry_type]) {
USE(env->performance_entry_callback()->
Call(env->context(), Undefined(env->isolate()), 1, &entry));
}
}
// Return idle time of the event loop
void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
uint64_t idle_time = uv_metrics_idle_time(env->event_loop());
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) {
Environment* env = Environment::GetCurrent(args);
CHECK(args.IsConstructCall());
int32_t resolution = args[0]->IntegerValue(env->context()).FromJust();
CHECK_GT(resolution, 0);
new ELDHistogram(env, args.This(), resolution);
}
} // namespace
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();
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
"min", histogram->Min());
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
"max", histogram->Max());
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
"mean", histogram->Mean());
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;
}
void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Environment* env = Environment::GetCurrent(context);
Isolate* isolate = env->isolate();
PerformanceState* state = env->performance_state();
target->Set(context,
FIXED_ONE_BYTE_STRING(isolate, "observerCounts"),
state->observers.GetJSArray()).Check();
target->Set(context,
FIXED_ONE_BYTE_STRING(isolate, "milestones"),
state->milestones.GetJSArray()).Check();
Local<String> performanceEntryString =
FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry");
Local<FunctionTemplate> pe = FunctionTemplate::New(isolate);
pe->SetClassName(performanceEntryString);
Local<Function> fn = pe->GetFunction(context).ToLocalChecked();
target->Set(context, performanceEntryString, fn).Check();
env->set_performance_entry_template(fn);
env->SetMethod(target, "clearMark", ClearMark);
env->SetMethod(target, "mark", Mark);
env->SetMethod(target, "measure", Measure);
env->SetMethod(target, "markMilestone", MarkMilestone);
env->SetMethod(target, "setupObservers", SetupPerformanceObservers);
env->SetMethod(target, "timerify", Timerify);
env->SetMethod(target,
"installGarbageCollectionTracking",
InstallGarbageCollectionTracking);
env->SetMethod(target,
"removeGarbageCollectionTracking",
RemoveGarbageCollectionTracking);
env->SetMethod(target, "notify", Notify);
env->SetMethod(target, "loopIdleTime", LoopIdleTime);
Local<Object> constants = Object::New(isolate);
NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MAJOR);
NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MINOR);
NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_INCREMENTAL);
NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_WEAKCB);
NODE_DEFINE_CONSTANT(
constants, NODE_PERFORMANCE_GC_FLAGS_NO);
NODE_DEFINE_CONSTANT(
constants, NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED);
NODE_DEFINE_CONSTANT(
constants, NODE_PERFORMANCE_GC_FLAGS_FORCED);
NODE_DEFINE_CONSTANT(
constants, NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING);
NODE_DEFINE_CONSTANT(
constants, NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE);
NODE_DEFINE_CONSTANT(
constants, NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY);
NODE_DEFINE_CONSTANT(
constants, NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE);
#define V(name, _) \
NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_ENTRY_TYPE_##name);
NODE_PERFORMANCE_ENTRY_TYPES(V)
#undef V
#define V(name, _) \
NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_MILESTONE_##name);
NODE_PERFORMANCE_MILESTONES(V)
#undef V
PropertyAttribute attr =
static_cast<PropertyAttribute>(ReadOnly | DontDelete);
target->DefineOwnProperty(context,
FIXED_ONE_BYTE_STRING(isolate, "timeOrigin"),
Number::New(isolate, timeOrigin / 1e6),
attr).ToChecked();
target->DefineOwnProperty(
context,
FIXED_ONE_BYTE_STRING(isolate, "timeOriginTimestamp"),
Number::New(isolate, timeOriginTimestamp / MICROS_PER_MILLIS),
attr).ToChecked();
target->DefineOwnProperty(context,
env->constants_string(),
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);
target->Set(context, eldh_classname,
eldh->GetFunction(env->context()).ToLocalChecked()).Check();
}
} // namespace performance
} // namespace node
NODE_MODULE_CONTEXT_AWARE_INTERNAL(performance, node::performance::Initialize)