Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf_hooks: refactor internals #17822

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 110 additions & 149 deletions src/node_perf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Name;
using v8::Number;
using v8::Object;
using v8::ObjectTemplate;
using v8::Signature;
using v8::String;
using v8::Value;

Expand All @@ -30,63 +29,106 @@ uint64_t performance_v8_start;
uint64_t performance_last_gc_start_mark_ = 0;
v8::GCType performance_last_gc_type_ = v8::GCType::kGCTypeAll;

// Initialize the performance entry object properties
inline void InitObject(PerformanceEntry* entry, Local<Object> obj) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const PerformanceEntry& entry? Makes it a bit more obvious which parameter is the source/target

Environment* env = entry->env();
Isolate* isolate = env->isolate();
Local<Context> context = env->context();
v8::PropertyAttribute attr =
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
obj->DefineOwnProperty(context,
env->name_string(),
String::NewFromUtf8(isolate,
entry->name().c_str(),
String::kNormalString),
attr).FromJust();
obj->DefineOwnProperty(context,
FIXED_ONE_BYTE_STRING(isolate, "entryType"),
String::NewFromUtf8(isolate,
entry->type().c_str(),
String::kNormalString),
attr).FromJust();
obj->DefineOwnProperty(context,
FIXED_ONE_BYTE_STRING(isolate, "startTime"),
Number::New(isolate, entry->startTime()),
attr).FromJust();
obj->DefineOwnProperty(context,
FIXED_ONE_BYTE_STRING(isolate, "duration"),
Number::New(isolate, entry->duration()),
attr).FromJust();
}

// Create a new PerformanceEntry object
Local<Object> PerformanceEntry::ToObject() {
Local<Object> obj =
env()->performance_entry_template()
->NewInstance(env()->context()).ToLocalChecked();
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();
new PerformanceEntry(env, args.This(), *name, *type, now, now);
PerformanceEntry entry(env, *name, *type, now, now);
Local<Object> obj = args.This();
InitObject(&entry, obj);
PerformanceEntry::Notify(env, entry.kind(), obj);
}

void PerformanceEntry::NotifyObservers(Environment* env,
PerformanceEntry* entry) {
// Pass the PerformanceEntry object to the PerformanceObservers
inline void PerformanceEntry::Notify(Environment* env,
PerformanceEntryType type,
Local<Value> object) {
Context::Scope scope(env->context());
uint32_t* observers = env->performance_state()->observers;
PerformanceEntryType type = ToPerformanceEntryTypeEnum(entry->type().c_str());
if (observers == nullptr ||
type == NODE_PERFORMANCE_ENTRY_TYPE_INVALID ||
!observers[type]) {
return;
if (observers != nullptr &&
type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
observers[type]) {
node::MakeCallback(env->isolate(),
env->process_object(),
env->performance_entry_callback(),
1, &object);
}
Local<Context> context = env->context();
Isolate* isolate = env->isolate();
Local<Value> argv = entry->object();
env->performance_entry_callback()->Call(context,
v8::Undefined(isolate),
1, &argv).ToLocalChecked();
}

// Create a User Timing Mark
void Mark(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Context> context = env->context();
Isolate* isolate = env->isolate();
Utf8Value name(isolate, args[0]);
HandleScope scope(env->isolate());
Utf8Value name(env->isolate(), args[0]);
uint64_t now = PERFORMANCE_NOW();
auto marks = env->performance_marks();
(*marks)[*name] = now;

// TODO(jasnell): Once Tracing API is fully implemented, this should
// record a trace event also.

Local<Function> fn = env->performance_entry_template();
Local<Object> obj = fn->NewInstance(context).ToLocalChecked();
new PerformanceEntry(env, obj, *name, "mark", now, now);
PerformanceEntry entry(env, *name, "mark", now, now);
Local<Object> obj = entry.ToObject();
PerformanceEntry::Notify(env, entry.kind(), obj);
args.GetReturnValue().Set(obj);
}


inline uint64_t GetPerformanceMark(Environment* env, 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);
Local<Context> context = env->context();
Isolate* isolate = env->isolate();
Utf8Value name(isolate, args[0]);
Utf8Value startMark(isolate, args[1]);
Utf8Value endMark(isolate, args[2]);
HandleScope scope(env->isolate());
Utf8Value name(env->isolate(), args[0]);
Utf8Value startMark(env->isolate(), args[1]);
Utf8Value endMark(env->isolate(), args[2]);

double* milestones = env->performance_state()->milestones;

Expand All @@ -113,41 +155,13 @@ void Measure(const FunctionCallbackInfo<Value>& args) {
// TODO(jasnell): Once Tracing API is fully implemented, this should
// record a trace event also.

Local<Function> fn = env->performance_entry_template();
Local<Object> obj = fn->NewInstance(context).ToLocalChecked();
new PerformanceEntry(env, obj, *name, "measure",
startTimestamp, endTimestamp);
PerformanceEntry entry(env, *name, "measure", startTimestamp, endTimestamp);
Local<Object> obj = entry.ToObject();
PerformanceEntry::Notify(env, entry.kind(), obj);
args.GetReturnValue().Set(obj);
}

void GetPerformanceEntryName(const FunctionCallbackInfo<Value>& info) {
Isolate* isolate = info.GetIsolate();
PerformanceEntry* entry;
ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder());
info.GetReturnValue().Set(
String::NewFromUtf8(isolate, entry->name().c_str(), String::kNormalString));
}

void GetPerformanceEntryType(const FunctionCallbackInfo<Value>& info) {
Isolate* isolate = info.GetIsolate();
PerformanceEntry* entry;
ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder());
info.GetReturnValue().Set(
String::NewFromUtf8(isolate, entry->type().c_str(), String::kNormalString));
}

void GetPerformanceEntryStartTime(const FunctionCallbackInfo<Value>& info) {
PerformanceEntry* entry;
ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder());
info.GetReturnValue().Set(entry->startTime());
}

void GetPerformanceEntryDuration(const FunctionCallbackInfo<Value>& info) {
PerformanceEntry* entry;
ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder());
info.GetReturnValue().Set(entry->duration());
}

// 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();
Expand All @@ -160,73 +174,63 @@ void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
}
}


void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK(args[0]->IsFunction());
env->set_performance_entry_callback(args[0].As<Function>());
}

void PerformanceGCCallback(uv_async_t* handle) {
PerformanceEntry::Data* data =
static_cast<PerformanceEntry::Data*>(handle->data);
Environment* env = data->env();
Isolate* isolate = env->isolate();
HandleScope scope(isolate);
// Creates a GC Performance Entry and passes it to observers
void PerformanceGCCallback(Environment* env, void* ptr) {
GCPerformanceEntry* entry = static_cast<GCPerformanceEntry*>(ptr);
HandleScope scope(env->isolate());
Local<Context> context = env->context();
Context::Scope context_scope(context);
Local<Function> fn;
Local<Object> obj;
PerformanceGCKind kind = static_cast<PerformanceGCKind>(data->data());

uint32_t* observers = env->performance_state()->observers;
if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) {
goto cleanup;
if (observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) {
Local<Object> obj = entry->ToObject();
v8::PropertyAttribute attr =
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
obj->DefineOwnProperty(context,
FIXED_ONE_BYTE_STRING(env->isolate(), "kind"),
Integer::New(env->isolate(), entry->gckind()),
attr).FromJust();
PerformanceEntry::Notify(env, entry->kind(), obj);
}

fn = env->performance_entry_template();
obj = fn->NewInstance(context).ToLocalChecked();
obj->Set(context,
FIXED_ONE_BYTE_STRING(isolate, "kind"),
Integer::New(isolate, kind)).FromJust();
new PerformanceEntry(env, obj, data);

cleanup:
delete data;
auto closeCB = [](uv_handle_t* handle) {
delete reinterpret_cast<uv_async_t*>(handle);
};
uv_close(reinterpret_cast<uv_handle_t*>(handle), closeCB);
delete entry;
}

// Marks the start of a GC cycle
void MarkGarbageCollectionStart(Isolate* isolate,
v8::GCType type,
v8::GCCallbackFlags flags) {
performance_last_gc_start_mark_ = PERFORMANCE_NOW();
performance_last_gc_type_ = type;
}

// Marks the end of a GC cycle
void MarkGarbageCollectionEnd(Isolate* isolate,
v8::GCType type,
v8::GCCallbackFlags flags,
void* data) {
Environment* env = static_cast<Environment*>(data);
uv_async_t* async = new uv_async_t(); // coverity[leaked_storage]
if (uv_async_init(env->event_loop(), async, PerformanceGCCallback))
return delete async;
uv_unref(reinterpret_cast<uv_handle_t*>(async));
async->data =
new PerformanceEntry::Data(env, "gc", "gc",
performance_last_gc_start_mark_,
PERFORMANCE_NOW(), type);
CHECK_EQ(0, uv_async_send(async));
env->SetImmediate(PerformanceGCCallback,
new GCPerformanceEntry(env,
static_cast<PerformanceGCKind>(type),
performance_last_gc_start_mark_,
PERFORMANCE_NOW()));
}


inline void SetupGarbageCollectionTracking(Environment* env) {
env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart);
env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd,
static_cast<void*>(env));
}

// Gets the name of a function
inline Local<Value> GetName(Local<Function> fn) {
Local<Value> val = fn->GetDebugName();
if (val.IsEmpty() || val->IsUndefined()) {
Expand All @@ -238,6 +242,9 @@ inline Local<Value> GetName(Local<Function> fn) {
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();
HandleScope scope(isolate);
Expand All @@ -247,9 +254,8 @@ void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
size_t count = args.Length();
size_t idx;
std::vector<Local<Value>> call_args;
for (size_t i = 0; i < count; ++i) {
for (size_t i = 0; i < count; ++i)
call_args.push_back(args[i]);
}

Utf8Value name(isolate, GetName(fn));

Expand Down Expand Up @@ -286,15 +292,14 @@ void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION])
return;

Local<Function> ctor = env->performance_entry_template();
v8::MaybeLocal<Object> instance = ctor->NewInstance(context);
Local<Object> obj = instance.ToLocalChecked();
for (idx = 0; idx < count; idx++) {
obj->Set(context, idx, args[idx]).ToChecked();
}
new PerformanceEntry(env, obj, *name, "function", start, end);
PerformanceEntry entry(env, *name, "function", start, end);
Local<Object> obj = entry.ToObject();
for (idx = 0; idx < count; idx++)
obj->Set(context, idx, args[idx]).FromJust();
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();
Expand All @@ -307,6 +312,7 @@ void Timerify(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(wrap);
}


void Init(Local<Object> target,
Local<Value> unused,
Local<Context> context) {
Expand All @@ -329,55 +335,10 @@ void Init(Local<Object> target,
Local<String> performanceEntryString =
FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry");

Local<FunctionTemplate> pe = env->NewFunctionTemplate(PerformanceEntry::New);
pe->InstanceTemplate()->SetInternalFieldCount(1);
Local<FunctionTemplate> pe = FunctionTemplate::New(isolate);
pe->SetClassName(performanceEntryString);

Local<Signature> signature = Signature::New(env->isolate(), pe);

Local<FunctionTemplate> get_performance_entry_name_templ =
FunctionTemplate::New(env->isolate(),
GetPerformanceEntryName,
env->as_external(),
signature);

Local<FunctionTemplate> get_performance_entry_type_templ =
FunctionTemplate::New(env->isolate(),
GetPerformanceEntryType,
env->as_external(),
signature);

Local<FunctionTemplate> get_performance_entry_start_time_templ =
FunctionTemplate::New(env->isolate(),
GetPerformanceEntryStartTime,
env->as_external(),
signature);

Local<FunctionTemplate> get_performance_entry_duration_templ =
FunctionTemplate::New(env->isolate(),
GetPerformanceEntryDuration,
env->as_external(),
signature);

Local<ObjectTemplate> ot = pe->InstanceTemplate();
ot->SetAccessorProperty(env->name_string(),
get_performance_entry_name_templ,
Local<FunctionTemplate>());

ot->SetAccessorProperty(FIXED_ONE_BYTE_STRING(isolate, "entryType"),
get_performance_entry_type_templ,
Local<FunctionTemplate>());

ot->SetAccessorProperty(FIXED_ONE_BYTE_STRING(isolate, "startTime"),
get_performance_entry_start_time_templ,
Local<FunctionTemplate>());

ot->SetAccessorProperty(FIXED_ONE_BYTE_STRING(isolate, "duration"),
get_performance_entry_duration_templ,
Local<FunctionTemplate>());

Local<Function> fn = pe->GetFunction();
target->Set(performanceEntryString, fn);
target->Set(context, performanceEntryString, fn).FromJust();
env->set_performance_entry_template(fn);

env->SetMethod(target, "mark", Mark);
Expand Down
Loading