From a329cf62abcdf11671d558f7783c96426c454275 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 21 Dec 2017 14:29:05 -0800 Subject: [PATCH] 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 --- src/node_perf.cc | 259 ++++++++++++++++++++--------------------------- src/node_perf.h | 134 ++++++++---------------- 2 files changed, 150 insertions(+), 243 deletions(-) diff --git a/src/node_perf.cc b/src/node_perf.cc index 37256a943cb014..fcf7cd8b5af4e0 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -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; @@ -30,37 +29,78 @@ 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(const PerformanceEntry& entry, Local obj) { + Environment* env = entry.env(); + Isolate* isolate = env->isolate(); + Local context = env->context(); + v8::PropertyAttribute attr = + static_cast(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 PerformanceEntry::ToObject() const { + Local 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& 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 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 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 = env->context(); - Isolate* isolate = env->isolate(); - Local argv = entry->object(); - env->performance_entry_callback()->Call(context, - v8::Undefined(isolate), - 1, &argv).ToLocalChecked(); } +// Create a User Timing Mark void Mark(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - Local 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; @@ -68,25 +108,27 @@ void Mark(const FunctionCallbackInfo& args) { // TODO(jasnell): Once Tracing API is fully implemented, this should // record a trace event also. - Local fn = env->performance_entry_template(); - Local obj = fn->NewInstance(context).ToLocalChecked(); - new PerformanceEntry(env, obj, *name, "mark", now, now); + PerformanceEntry entry(env, *name, "mark", now, now); + Local 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& args) { Environment* env = Environment::GetCurrent(args); - Local 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; @@ -113,41 +155,13 @@ void Measure(const FunctionCallbackInfo& args) { // TODO(jasnell): Once Tracing API is fully implemented, this should // record a trace event also. - Local fn = env->performance_entry_template(); - Local obj = fn->NewInstance(context).ToLocalChecked(); - new PerformanceEntry(env, obj, *name, "measure", - startTimestamp, endTimestamp); + PerformanceEntry entry(env, *name, "measure", startTimestamp, endTimestamp); + Local obj = entry.ToObject(); + PerformanceEntry::Notify(env, entry.kind(), obj); args.GetReturnValue().Set(obj); } -void GetPerformanceEntryName(const FunctionCallbackInfo& 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& 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& info) { - PerformanceEntry* entry; - ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder()); - info.GetReturnValue().Set(entry->startTime()); -} - -void GetPerformanceEntryDuration(const FunctionCallbackInfo& 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& args) { Environment* env = Environment::GetCurrent(args); Local context = env->context(); @@ -160,44 +174,35 @@ void MarkMilestone(const FunctionCallbackInfo& args) { } } + void SetupPerformanceObservers(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsFunction()); env->set_performance_entry_callback(args[0].As()); } -void PerformanceGCCallback(uv_async_t* handle) { - PerformanceEntry::Data* data = - static_cast(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(ptr); + HandleScope scope(env->isolate()); Local context = env->context(); - Context::Scope context_scope(context); - Local fn; - Local obj; - PerformanceGCKind kind = static_cast(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 obj = entry->ToObject(); + v8::PropertyAttribute attr = + static_cast(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(handle); - }; - uv_close(reinterpret_cast(handle), closeCB); + delete entry; } +// Marks the start of a GC cycle void MarkGarbageCollectionStart(Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags) { @@ -205,28 +210,27 @@ void MarkGarbageCollectionStart(Isolate* isolate, 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(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(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(type), + performance_last_gc_start_mark_, + PERFORMANCE_NOW())); } + inline void SetupGarbageCollectionTracking(Environment* env) { env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart); env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd, static_cast(env)); } +// Gets the name of a function inline Local GetName(Local fn) { Local val = fn->GetDebugName(); if (val.IsEmpty() || val->IsUndefined()) { @@ -238,6 +242,9 @@ inline Local GetName(Local 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& args) { Isolate* isolate = args.GetIsolate(); HandleScope scope(isolate); @@ -247,9 +254,8 @@ void TimerFunctionCall(const FunctionCallbackInfo& args) { size_t count = args.Length(); size_t idx; std::vector> 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)); @@ -286,15 +292,14 @@ void TimerFunctionCall(const FunctionCallbackInfo& args) { if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION]) return; - Local ctor = env->performance_entry_template(); - v8::MaybeLocal instance = ctor->NewInstance(context); - Local 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 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& args) { Environment* env = Environment::GetCurrent(args); Local context = env->context(); @@ -307,6 +312,7 @@ void Timerify(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(wrap); } + void Init(Local target, Local unused, Local context) { @@ -329,55 +335,10 @@ void Init(Local target, Local performanceEntryString = FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry"); - Local pe = env->NewFunctionTemplate(PerformanceEntry::New); - pe->InstanceTemplate()->SetInternalFieldCount(1); + Local pe = FunctionTemplate::New(isolate); pe->SetClassName(performanceEntryString); - - Local signature = Signature::New(env->isolate(), pe); - - Local get_performance_entry_name_templ = - FunctionTemplate::New(env->isolate(), - GetPerformanceEntryName, - env->as_external(), - signature); - - Local get_performance_entry_type_templ = - FunctionTemplate::New(env->isolate(), - GetPerformanceEntryType, - env->as_external(), - signature); - - Local get_performance_entry_start_time_templ = - FunctionTemplate::New(env->isolate(), - GetPerformanceEntryStartTime, - env->as_external(), - signature); - - Local get_performance_entry_duration_templ = - FunctionTemplate::New(env->isolate(), - GetPerformanceEntryDuration, - env->as_external(), - signature); - - Local ot = pe->InstanceTemplate(); - ot->SetAccessorProperty(env->name_string(), - get_performance_entry_name_templ, - Local()); - - ot->SetAccessorProperty(FIXED_ONE_BYTE_STRING(isolate, "entryType"), - get_performance_entry_type_templ, - Local()); - - ot->SetAccessorProperty(FIXED_ONE_BYTE_STRING(isolate, "startTime"), - get_performance_entry_start_time_templ, - Local()); - - ot->SetAccessorProperty(FIXED_ONE_BYTE_STRING(isolate, "duration"), - get_performance_entry_duration_templ, - Local()); - Local fn = pe->GetFunction(); - target->Set(performanceEntryString, fn); + target->Set(context, performanceEntryString, fn).FromJust(); env->set_performance_entry_template(fn); env->SetMethod(target, "mark", Mark); diff --git a/src/node_perf.h b/src/node_perf.h index 690f70830961bb..e5655f54fed07e 100644 --- a/src/node_perf.h +++ b/src/node_perf.h @@ -42,120 +42,51 @@ static inline PerformanceEntryType ToPerformanceEntryTypeEnum( NODE_EXTERN inline void MarkPerformanceMilestone( Environment* env, PerformanceMilestone milestone) { - env->performance_state()->milestones[milestone] = PERFORMANCE_NOW(); - } + env->performance_state()->milestones[milestone] = PERFORMANCE_NOW(); +} -class PerformanceEntry : public BaseObject { +class PerformanceEntry { public: - // Used for temporary storage of performance entry details when the - // object cannot be created immediately. - class Data { - 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 inline void Notify(Environment* env, + PerformanceEntryType type, + Local object); static void New(const FunctionCallbackInfo& args); PerformanceEntry(Environment* env, - Local wrap, const char* name, const char* type, uint64_t startTime, - uint64_t endTime) : - BaseObject(env, wrap), - name_(name), - type_(type), - startTime_(startTime), - endTime_(endTime) { - MakeWeak(this); - NotifyObservers(env, this); - } + uint64_t endTime) : env_(env), + name_(name), + type_(type), + startTime_(startTime), + endTime_(endTime) { } - PerformanceEntry(Environment* env, - Local wrap, - Data* data) : - BaseObject(env, wrap), - name_(data->name()), - type_(data->type()), - startTime_(data->startTime()), - endTime_(data->endTime()) { - MakeWeak(this); - NotifyObservers(env, this); - } + virtual ~PerformanceEntry() { } - ~PerformanceEntry() {} + virtual const Local ToObject() const; - const std::string& name() const { - return name_; - } + Environment* env() const { return env_; } - const std::string& type() const { - return type_; - } + const std::string& name() const { return name_; } - double startTime() const { - return startTime_ / 1e6; - } + const std::string& type() const { return type_; } - double duration() const { - return durationNano() / 1e6; + PerformanceEntryType kind() { + return ToPerformanceEntryTypeEnum(type().c_str()); } - uint64_t startTimeNano() const { - return startTime_; - } + double startTime() const { return startTime_ / 1e6; } - uint64_t durationNano() const { - return endTime_ - startTime_; - } + double duration() const { return durationNano() / 1e6; } + + uint64_t startTimeNano() const { return startTime_; } + + uint64_t durationNano() const { return endTime_ - startTime_; } private: + Environment* env_; const std::string name_; const std::string type_; const uint64_t startTime_; @@ -169,6 +100,21 @@ enum PerformanceGCKind { 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 node