perf_hooks: refactor internals

Refactor and simplify the perf_hooks native internals.

PR-URL: https://github.com/nodejs/node/pull/17822
Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
James M Snell 2017-12-21 14:29:05 -08:00 committed by Anna Henningsen
parent 6c0da34905
commit 9e5ccf0313
No known key found for this signature in database
GPG Key ID: 9C63F3A6CD2AD8F9
2 changed files with 154 additions and 247 deletions

View File

@ -17,9 +17,8 @@ using v8::Integer;
using v8::Isolate; using v8::Isolate;
using v8::Local; using v8::Local;
using v8::Name; using v8::Name;
using v8::Number;
using v8::Object; using v8::Object;
using v8::ObjectTemplate;
using v8::Signature;
using v8::String; using v8::String;
using v8::Value; using v8::Value;
@ -30,37 +29,78 @@ uint64_t performance_v8_start;
uint64_t performance_last_gc_start_mark_ = 0; uint64_t performance_last_gc_start_mark_ = 0;
v8::GCType performance_last_gc_type_ = v8::GCType::kGCTypeAll; v8::GCType performance_last_gc_type_ = v8::GCType::kGCTypeAll;
// 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();
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
const Local<Object> PerformanceEntry::ToObject() const {
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) { void PerformanceEntry::New(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate(); Isolate* isolate = env->isolate();
Utf8Value name(isolate, args[0]); Utf8Value name(isolate, args[0]);
Utf8Value type(isolate, args[1]); Utf8Value type(isolate, args[1]);
uint64_t now = PERFORMANCE_NOW(); 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, // Pass the PerformanceEntry object to the PerformanceObservers
PerformanceEntry* entry) { inline void PerformanceEntry::Notify(Environment* env,
PerformanceEntryType type,
Local<Value> object) {
Context::Scope scope(env->context());
uint32_t* observers = env->performance_state()->observers; uint32_t* observers = env->performance_state()->observers;
PerformanceEntryType type = ToPerformanceEntryTypeEnum(entry->type().c_str()); if (observers != nullptr &&
if (observers == nullptr || type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
type == NODE_PERFORMANCE_ENTRY_TYPE_INVALID || observers[type]) {
!observers[type]) { node::MakeCallback(env->isolate(),
return; 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) { void Mark(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
Local<Context> context = env->context(); HandleScope scope(env->isolate());
Isolate* isolate = env->isolate(); Utf8Value name(env->isolate(), args[0]);
Utf8Value name(isolate, args[0]);
uint64_t now = PERFORMANCE_NOW(); uint64_t now = PERFORMANCE_NOW();
auto marks = env->performance_marks(); auto marks = env->performance_marks();
(*marks)[*name] = now; (*marks)[*name] = now;
@ -68,25 +108,27 @@ void Mark(const FunctionCallbackInfo<Value>& args) {
// TODO(jasnell): Once Tracing API is fully implemented, this should // TODO(jasnell): Once Tracing API is fully implemented, this should
// record a trace event also. // record a trace event also.
Local<Function> fn = env->performance_entry_template(); PerformanceEntry entry(env, *name, "mark", now, now);
Local<Object> obj = fn->NewInstance(context).ToLocalChecked(); Local<Object> obj = entry.ToObject();
new PerformanceEntry(env, obj, *name, "mark", now, now); PerformanceEntry::Notify(env, entry.kind(), obj);
args.GetReturnValue().Set(obj); args.GetReturnValue().Set(obj);
} }
inline uint64_t GetPerformanceMark(Environment* env, std::string name) { inline uint64_t GetPerformanceMark(Environment* env, std::string name) {
auto marks = env->performance_marks(); auto marks = env->performance_marks();
auto res = marks->find(name); auto res = marks->find(name);
return res != marks->end() ? res->second : 0; 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) { void Measure(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
Local<Context> context = env->context(); HandleScope scope(env->isolate());
Isolate* isolate = env->isolate(); Utf8Value name(env->isolate(), args[0]);
Utf8Value name(isolate, args[0]); Utf8Value startMark(env->isolate(), args[1]);
Utf8Value startMark(isolate, args[1]); Utf8Value endMark(env->isolate(), args[2]);
Utf8Value endMark(isolate, args[2]);
double* milestones = env->performance_state()->milestones; double* milestones = env->performance_state()->milestones;
@ -113,41 +155,13 @@ void Measure(const FunctionCallbackInfo<Value>& args) {
// TODO(jasnell): Once Tracing API is fully implemented, this should // TODO(jasnell): Once Tracing API is fully implemented, this should
// record a trace event also. // record a trace event also.
Local<Function> fn = env->performance_entry_template(); PerformanceEntry entry(env, *name, "measure", startTimestamp, endTimestamp);
Local<Object> obj = fn->NewInstance(context).ToLocalChecked(); Local<Object> obj = entry.ToObject();
new PerformanceEntry(env, obj, *name, "measure", PerformanceEntry::Notify(env, entry.kind(), obj);
startTimestamp, endTimestamp);
args.GetReturnValue().Set(obj); args.GetReturnValue().Set(obj);
} }
void GetPerformanceEntryName(const FunctionCallbackInfo<Value>& info) { // Allows specific Node.js lifecycle milestones to be set from JavaScript
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());
}
void MarkMilestone(const FunctionCallbackInfo<Value>& args) { void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
Local<Context> context = env->context(); Local<Context> context = env->context();
@ -160,44 +174,35 @@ void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
} }
} }
void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) { void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
CHECK(args[0]->IsFunction()); CHECK(args[0]->IsFunction());
env->set_performance_entry_callback(args[0].As<Function>()); env->set_performance_entry_callback(args[0].As<Function>());
} }
void PerformanceGCCallback(uv_async_t* handle) { // Creates a GC Performance Entry and passes it to observers
PerformanceEntry::Data* data = void PerformanceGCCallback(Environment* env, void* ptr) {
static_cast<PerformanceEntry::Data*>(handle->data); GCPerformanceEntry* entry = static_cast<GCPerformanceEntry*>(ptr);
Environment* env = data->env(); HandleScope scope(env->isolate());
Isolate* isolate = env->isolate();
HandleScope scope(isolate);
Local<Context> context = env->context(); 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; uint32_t* observers = env->performance_state()->observers;
if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) { if (observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) {
goto cleanup; 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(); delete entry;
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);
} }
// Marks the start of a GC cycle
void MarkGarbageCollectionStart(Isolate* isolate, void MarkGarbageCollectionStart(Isolate* isolate,
v8::GCType type, v8::GCType type,
v8::GCCallbackFlags flags) { v8::GCCallbackFlags flags) {
@ -205,28 +210,27 @@ void MarkGarbageCollectionStart(Isolate* isolate,
performance_last_gc_type_ = type; performance_last_gc_type_ = type;
} }
// Marks the end of a GC cycle
void MarkGarbageCollectionEnd(Isolate* isolate, void MarkGarbageCollectionEnd(Isolate* isolate,
v8::GCType type, v8::GCType type,
v8::GCCallbackFlags flags, v8::GCCallbackFlags flags,
void* data) { void* data) {
Environment* env = static_cast<Environment*>(data); Environment* env = static_cast<Environment*>(data);
uv_async_t* async = new uv_async_t(); // coverity[leaked_storage] env->SetImmediate(PerformanceGCCallback,
if (uv_async_init(env->event_loop(), async, PerformanceGCCallback)) new GCPerformanceEntry(env,
return delete async; static_cast<PerformanceGCKind>(type),
uv_unref(reinterpret_cast<uv_handle_t*>(async)); performance_last_gc_start_mark_,
async->data = PERFORMANCE_NOW()));
new PerformanceEntry::Data(env, "gc", "gc",
performance_last_gc_start_mark_,
PERFORMANCE_NOW(), type);
CHECK_EQ(0, uv_async_send(async));
} }
inline void SetupGarbageCollectionTracking(Environment* env) { inline void SetupGarbageCollectionTracking(Environment* env) {
env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart); env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart);
env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd, env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd,
static_cast<void*>(env)); static_cast<void*>(env));
} }
// Gets the name of a function
inline Local<Value> GetName(Local<Function> fn) { inline Local<Value> GetName(Local<Function> fn) {
Local<Value> val = fn->GetDebugName(); Local<Value> val = fn->GetDebugName();
if (val.IsEmpty() || val->IsUndefined()) { if (val.IsEmpty() || val->IsUndefined()) {
@ -238,6 +242,9 @@ inline Local<Value> GetName(Local<Function> fn) {
return val; 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) { void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate); HandleScope scope(isolate);
@ -247,9 +254,8 @@ void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
size_t count = args.Length(); size_t count = args.Length();
size_t idx; size_t idx;
std::vector<Local<Value>> call_args; 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]); call_args.push_back(args[i]);
}
Utf8Value name(isolate, GetName(fn)); Utf8Value name(isolate, GetName(fn));
@ -286,15 +292,14 @@ void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION]) if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION])
return; return;
Local<Function> ctor = env->performance_entry_template(); PerformanceEntry entry(env, *name, "function", start, end);
v8::MaybeLocal<Object> instance = ctor->NewInstance(context); Local<Object> obj = entry.ToObject();
Local<Object> obj = instance.ToLocalChecked(); for (idx = 0; idx < count; idx++)
for (idx = 0; idx < count; idx++) { obj->Set(context, idx, args[idx]).FromJust();
obj->Set(context, idx, args[idx]).ToChecked(); PerformanceEntry::Notify(env, entry.kind(), obj);
}
new PerformanceEntry(env, obj, *name, "function", start, end);
} }
// Wraps a Function in a TimerFunctionCall
void Timerify(const FunctionCallbackInfo<Value>& args) { void Timerify(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
Local<Context> context = env->context(); Local<Context> context = env->context();
@ -307,6 +312,7 @@ void Timerify(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(wrap); args.GetReturnValue().Set(wrap);
} }
void Init(Local<Object> target, void Init(Local<Object> target,
Local<Value> unused, Local<Value> unused,
Local<Context> context) { Local<Context> context) {
@ -329,55 +335,10 @@ void Init(Local<Object> target,
Local<String> performanceEntryString = Local<String> performanceEntryString =
FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry"); FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry");
Local<FunctionTemplate> pe = env->NewFunctionTemplate(PerformanceEntry::New); Local<FunctionTemplate> pe = FunctionTemplate::New(isolate);
pe->InstanceTemplate()->SetInternalFieldCount(1);
pe->SetClassName(performanceEntryString); 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(); Local<Function> fn = pe->GetFunction();
target->Set(performanceEntryString, fn); target->Set(context, performanceEntryString, fn).FromJust();
env->set_performance_entry_template(fn); env->set_performance_entry_template(fn);
env->SetMethod(target, "mark", Mark); env->SetMethod(target, "mark", Mark);

View File

@ -42,120 +42,51 @@ static inline PerformanceEntryType ToPerformanceEntryTypeEnum(
NODE_EXTERN inline void MarkPerformanceMilestone( NODE_EXTERN inline void MarkPerformanceMilestone(
Environment* env, Environment* env,
PerformanceMilestone milestone) { PerformanceMilestone milestone) {
env->performance_state()->milestones[milestone] = PERFORMANCE_NOW(); env->performance_state()->milestones[milestone] = PERFORMANCE_NOW();
} }
class PerformanceEntry : public BaseObject { class PerformanceEntry {
public: public:
// Used for temporary storage of performance entry details when the static inline void Notify(Environment* env,
// object cannot be created immediately. PerformanceEntryType type,
class Data { Local<Value> object);
public:
Data(
Environment* env,
const char* name,
const char* type,
uint64_t startTime,
uint64_t endTime,
int data = 0) :
env_(env),
name_(name),
type_(type),
startTime_(startTime),
endTime_(endTime),
data_(data) {}
Environment* env() const {
return env_;
}
const std::string& name() const {
return name_;
}
const std::string& type() const {
return type_;
}
uint64_t startTime() const {
return startTime_;
}
uint64_t endTime() const {
return endTime_;
}
int data() const {
return data_;
}
private:
Environment* const env_;
const std::string name_;
const std::string type_;
const uint64_t startTime_;
const uint64_t endTime_;
const int data_;
};
static void NotifyObservers(Environment* env, PerformanceEntry* entry);
static void New(const FunctionCallbackInfo<Value>& args); static void New(const FunctionCallbackInfo<Value>& args);
PerformanceEntry(Environment* env, PerformanceEntry(Environment* env,
Local<Object> wrap,
const char* name, const char* name,
const char* type, const char* type,
uint64_t startTime, uint64_t startTime,
uint64_t endTime) : uint64_t endTime) : env_(env),
BaseObject(env, wrap), name_(name),
name_(name), type_(type),
type_(type), startTime_(startTime),
startTime_(startTime), endTime_(endTime) { }
endTime_(endTime) {
MakeWeak<PerformanceEntry>(this); virtual ~PerformanceEntry() { }
NotifyObservers(env, this);
virtual const Local<Object> ToObject() const;
Environment* env() const { return env_; }
const std::string& name() const { return name_; }
const std::string& type() const { return type_; }
PerformanceEntryType kind() {
return ToPerformanceEntryTypeEnum(type().c_str());
} }
PerformanceEntry(Environment* env, double startTime() const { return startTime_ / 1e6; }
Local<Object> wrap,
Data* data) :
BaseObject(env, wrap),
name_(data->name()),
type_(data->type()),
startTime_(data->startTime()),
endTime_(data->endTime()) {
MakeWeak<PerformanceEntry>(this);
NotifyObservers(env, this);
}
~PerformanceEntry() {} double duration() const { return durationNano() / 1e6; }
const std::string& name() const { uint64_t startTimeNano() const { return startTime_; }
return name_;
}
const std::string& type() const { uint64_t durationNano() const { return endTime_ - startTime_; }
return type_;
}
double startTime() const {
return startTime_ / 1e6;
}
double duration() const {
return durationNano() / 1e6;
}
uint64_t startTimeNano() const {
return startTime_;
}
uint64_t durationNano() const {
return endTime_ - startTime_;
}
private: private:
Environment* env_;
const std::string name_; const std::string name_;
const std::string type_; const std::string type_;
const uint64_t startTime_; const uint64_t startTime_;
@ -169,6 +100,21 @@ enum PerformanceGCKind {
NODE_PERFORMANCE_GC_WEAKCB = GCType::kGCTypeProcessWeakCallbacks NODE_PERFORMANCE_GC_WEAKCB = GCType::kGCTypeProcessWeakCallbacks
}; };
class GCPerformanceEntry : public PerformanceEntry {
public:
GCPerformanceEntry(Environment* env,
PerformanceGCKind gckind,
uint64_t startTime,
uint64_t endTime) :
PerformanceEntry(env, "gc", "gc", startTime, endTime),
gckind_(gckind) { }
PerformanceGCKind gckind() const { return gckind_; }
private:
PerformanceGCKind gckind_;
};
} // namespace performance } // namespace performance
} // namespace node } // namespace node