1#include "node_perf.h" 2#include "aliased_buffer-inl.h" 3#include "env-inl.h" 4#include "histogram-inl.h" 5#include "memory_tracker-inl.h" 6#include "node_buffer.h" 7#include "node_external_reference.h" 8#include "node_internals.h" 9#include "node_process-inl.h" 10#include "util-inl.h" 11 12#include <cinttypes> 13 14namespace node { 15namespace performance { 16 17using v8::Context; 18using v8::DontDelete; 19using v8::Function; 20using v8::FunctionCallbackInfo; 21using v8::FunctionTemplate; 22using v8::GCCallbackFlags; 23using v8::GCType; 24using v8::Int32; 25using v8::Integer; 26using v8::Isolate; 27using v8::Local; 28using v8::MaybeLocal; 29using v8::Number; 30using v8::Object; 31using v8::PropertyAttribute; 32using v8::ReadOnly; 33using v8::String; 34using v8::Value; 35 36// Microseconds in a millisecond, as a float. 37#define MICROS_PER_MILLIS 1e3 38 39// https://w3c.github.io/hr-time/#dfn-time-origin 40const uint64_t timeOrigin = PERFORMANCE_NOW(); 41// https://w3c.github.io/hr-time/#dfn-time-origin-timestamp 42const double timeOriginTimestamp = GetCurrentTimeInMicroseconds(); 43uint64_t performance_v8_start; 44 45PerformanceState::PerformanceState(Isolate* isolate, 46 const PerformanceState::SerializeInfo* info) 47 : root(isolate, 48 sizeof(performance_state_internal), 49 MAYBE_FIELD_PTR(info, root)), 50 milestones(isolate, 51 offsetof(performance_state_internal, milestones), 52 NODE_PERFORMANCE_MILESTONE_INVALID, 53 root, 54 MAYBE_FIELD_PTR(info, milestones)), 55 observers(isolate, 56 offsetof(performance_state_internal, observers), 57 NODE_PERFORMANCE_ENTRY_TYPE_INVALID, 58 root, 59 MAYBE_FIELD_PTR(info, observers)) { 60 if (info == nullptr) { 61 for (size_t i = 0; i < milestones.Length(); i++) milestones[i] = -1.; 62 } 63} 64 65PerformanceState::SerializeInfo PerformanceState::Serialize( 66 v8::Local<v8::Context> context, v8::SnapshotCreator* creator) { 67 SerializeInfo info{root.Serialize(context, creator), 68 milestones.Serialize(context, creator), 69 observers.Serialize(context, creator)}; 70 return info; 71} 72 73void PerformanceState::Deserialize(v8::Local<v8::Context> context) { 74 root.Deserialize(context); 75 // This is just done to set up the pointers, we will actually reset 76 // all the milestones after deserialization. 77 milestones.Deserialize(context); 78 observers.Deserialize(context); 79} 80 81std::ostream& operator<<(std::ostream& o, 82 const PerformanceState::SerializeInfo& i) { 83 o << "{\n" 84 << " " << i.root << ", // root\n" 85 << " " << i.milestones << ", // milestones\n" 86 << " " << i.observers << ", // observers\n" 87 << "}"; 88 return o; 89} 90 91void PerformanceState::Mark(PerformanceMilestone milestone, uint64_t ts) { 92 this->milestones[milestone] = static_cast<double>(ts); 93 TRACE_EVENT_INSTANT_WITH_TIMESTAMP0( 94 TRACING_CATEGORY_NODE1(bootstrap), 95 GetPerformanceMilestoneName(milestone), 96 TRACE_EVENT_SCOPE_THREAD, ts / 1000); 97} 98 99// Allows specific Node.js lifecycle milestones to be set from JavaScript 100void MarkMilestone(const FunctionCallbackInfo<Value>& args) { 101 Environment* env = Environment::GetCurrent(args); 102 PerformanceMilestone milestone = 103 static_cast<PerformanceMilestone>(args[0].As<Int32>()->Value()); 104 if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID) 105 env->performance_state()->Mark(milestone); 106} 107 108void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) { 109 Environment* env = Environment::GetCurrent(args); 110 CHECK(args[0]->IsFunction()); 111 env->set_performance_entry_callback(args[0].As<Function>()); 112} 113 114// Marks the start of a GC cycle 115void MarkGarbageCollectionStart( 116 Isolate* isolate, 117 GCType type, 118 GCCallbackFlags flags, 119 void* data) { 120 Environment* env = static_cast<Environment*>(data); 121 // Prevent gc callback from reentering with different type 122 // See https://github.com/nodejs/node/issues/44046 123 if (env->performance_state()->current_gc_type != 0) { 124 return; 125 } 126 env->performance_state()->performance_last_gc_start_mark = PERFORMANCE_NOW(); 127 env->performance_state()->current_gc_type = type; 128} 129 130MaybeLocal<Object> GCPerformanceEntryTraits::GetDetails( 131 Environment* env, 132 const GCPerformanceEntry& entry) { 133 Local<Object> obj = Object::New(env->isolate()); 134 135 if (!obj->Set( 136 env->context(), 137 env->kind_string(), 138 Integer::NewFromUnsigned( 139 env->isolate(), 140 entry.details.kind)).IsJust()) { 141 return MaybeLocal<Object>(); 142 } 143 144 if (!obj->Set( 145 env->context(), 146 env->flags_string(), 147 Integer::NewFromUnsigned( 148 env->isolate(), 149 entry.details.flags)).IsJust()) { 150 return MaybeLocal<Object>(); 151 } 152 153 return obj; 154} 155 156// Marks the end of a GC cycle 157void MarkGarbageCollectionEnd( 158 Isolate* isolate, 159 GCType type, 160 GCCallbackFlags flags, 161 void* data) { 162 Environment* env = static_cast<Environment*>(data); 163 PerformanceState* state = env->performance_state(); 164 if (type != state->current_gc_type) { 165 return; 166 } 167 env->performance_state()->current_gc_type = 0; 168 // If no one is listening to gc performance entries, do not create them. 169 if (LIKELY(!state->observers[NODE_PERFORMANCE_ENTRY_TYPE_GC])) 170 return; 171 172 double start_time = 173 (state->performance_last_gc_start_mark - timeOrigin) / 1e6; 174 double duration = 175 (PERFORMANCE_NOW() / 1e6) - (state->performance_last_gc_start_mark / 1e6); 176 177 std::unique_ptr<GCPerformanceEntry> entry = 178 std::make_unique<GCPerformanceEntry>( 179 "gc", 180 start_time, 181 duration, 182 GCPerformanceEntry::Details(static_cast<PerformanceGCKind>(type), 183 static_cast<PerformanceGCFlags>(flags))); 184 185 env->SetImmediate([entry = std::move(entry)](Environment* env) { 186 entry->Notify(env); 187 }, CallbackFlags::kUnrefed); 188} 189 190void GarbageCollectionCleanupHook(void* data) { 191 Environment* env = static_cast<Environment*>(data); 192 // Reset current_gc_type to 0 193 env->performance_state()->current_gc_type = 0; 194 env->isolate()->RemoveGCPrologueCallback(MarkGarbageCollectionStart, data); 195 env->isolate()->RemoveGCEpilogueCallback(MarkGarbageCollectionEnd, data); 196} 197 198static void InstallGarbageCollectionTracking( 199 const FunctionCallbackInfo<Value>& args) { 200 Environment* env = Environment::GetCurrent(args); 201 // Reset current_gc_type to 0 202 env->performance_state()->current_gc_type = 0; 203 env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart, 204 static_cast<void*>(env)); 205 env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd, 206 static_cast<void*>(env)); 207 env->AddCleanupHook(GarbageCollectionCleanupHook, env); 208} 209 210static void RemoveGarbageCollectionTracking( 211 const FunctionCallbackInfo<Value> &args) { 212 Environment* env = Environment::GetCurrent(args); 213 214 env->RemoveCleanupHook(GarbageCollectionCleanupHook, env); 215 GarbageCollectionCleanupHook(env); 216} 217 218// Notify a custom PerformanceEntry to observers 219void Notify(const FunctionCallbackInfo<Value>& args) { 220 Environment* env = Environment::GetCurrent(args); 221 Utf8Value type(env->isolate(), args[0]); 222 Local<Value> entry = args[1]; 223 PerformanceEntryType entry_type = ToPerformanceEntryTypeEnum(*type); 224 AliasedUint32Array& observers = env->performance_state()->observers; 225 if (entry_type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID && 226 observers[entry_type]) { 227 USE(env->performance_entry_callback()-> 228 Call(env->context(), Undefined(env->isolate()), 1, &entry)); 229 } 230} 231 232// Return idle time of the event loop 233void LoopIdleTime(const FunctionCallbackInfo<Value>& args) { 234 Environment* env = Environment::GetCurrent(args); 235 uint64_t idle_time = uv_metrics_idle_time(env->event_loop()); 236 args.GetReturnValue().Set(1.0 * idle_time / 1e6); 237} 238 239void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) { 240 Environment* env = Environment::GetCurrent(args); 241 int64_t interval = args[0].As<Integer>()->Value(); 242 CHECK_GT(interval, 0); 243 BaseObjectPtr<IntervalHistogram> histogram = 244 IntervalHistogram::Create(env, interval, [](Histogram& histogram) { 245 uint64_t delta = histogram.RecordDelta(); 246 TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop), 247 "delay", delta); 248 TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop), 249 "min", histogram.Min()); 250 TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop), 251 "max", histogram.Max()); 252 TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop), 253 "mean", histogram.Mean()); 254 TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop), 255 "stddev", histogram.Stddev()); 256 }, Histogram::Options { 1000 }); 257 args.GetReturnValue().Set(histogram->object()); 258} 259 260void GetTimeOrigin(const FunctionCallbackInfo<Value>& args) { 261 args.GetReturnValue().Set(Number::New(args.GetIsolate(), timeOrigin / 1e6)); 262} 263 264void GetTimeOriginTimeStamp(const FunctionCallbackInfo<Value>& args) { 265 args.GetReturnValue().Set( 266 Number::New(args.GetIsolate(), timeOriginTimestamp / MICROS_PER_MILLIS)); 267} 268 269void MarkBootstrapComplete(const FunctionCallbackInfo<Value>& args) { 270 Environment* env = Environment::GetCurrent(args); 271 env->performance_state()->Mark( 272 performance::NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); 273} 274 275void Initialize(Local<Object> target, 276 Local<Value> unused, 277 Local<Context> context, 278 void* priv) { 279 Environment* env = Environment::GetCurrent(context); 280 Isolate* isolate = env->isolate(); 281 PerformanceState* state = env->performance_state(); 282 283 target->Set(context, 284 FIXED_ONE_BYTE_STRING(isolate, "observerCounts"), 285 state->observers.GetJSArray()).Check(); 286 target->Set(context, 287 FIXED_ONE_BYTE_STRING(isolate, "milestones"), 288 state->milestones.GetJSArray()).Check(); 289 290 Local<String> performanceEntryString = 291 FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry"); 292 293 Local<FunctionTemplate> pe = FunctionTemplate::New(isolate); 294 pe->SetClassName(performanceEntryString); 295 Local<Function> fn = pe->GetFunction(context).ToLocalChecked(); 296 target->Set(context, performanceEntryString, fn).Check(); 297 env->set_performance_entry_template(fn); 298 299 SetMethod(context, target, "markMilestone", MarkMilestone); 300 SetMethod(context, target, "setupObservers", SetupPerformanceObservers); 301 SetMethod(context, 302 target, 303 "installGarbageCollectionTracking", 304 InstallGarbageCollectionTracking); 305 SetMethod(context, 306 target, 307 "removeGarbageCollectionTracking", 308 RemoveGarbageCollectionTracking); 309 SetMethod(context, target, "notify", Notify); 310 SetMethod(context, target, "loopIdleTime", LoopIdleTime); 311 SetMethod(context, target, "getTimeOrigin", GetTimeOrigin); 312 SetMethod(context, target, "getTimeOriginTimestamp", GetTimeOriginTimeStamp); 313 SetMethod(context, target, "createELDHistogram", CreateELDHistogram); 314 SetMethod(context, target, "markBootstrapComplete", MarkBootstrapComplete); 315 316 Local<Object> constants = Object::New(isolate); 317 318 NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MAJOR); 319 NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MINOR); 320 NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_INCREMENTAL); 321 NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_WEAKCB); 322 323 NODE_DEFINE_CONSTANT( 324 constants, NODE_PERFORMANCE_GC_FLAGS_NO); 325 NODE_DEFINE_CONSTANT( 326 constants, NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED); 327 NODE_DEFINE_CONSTANT( 328 constants, NODE_PERFORMANCE_GC_FLAGS_FORCED); 329 NODE_DEFINE_CONSTANT( 330 constants, NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING); 331 NODE_DEFINE_CONSTANT( 332 constants, NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE); 333 NODE_DEFINE_CONSTANT( 334 constants, NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY); 335 NODE_DEFINE_CONSTANT( 336 constants, NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE); 337 338#define V(name, _) \ 339 NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_ENTRY_TYPE_##name); 340 NODE_PERFORMANCE_ENTRY_TYPES(V) 341#undef V 342 343#define V(name, _) \ 344 NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_MILESTONE_##name); 345 NODE_PERFORMANCE_MILESTONES(V) 346#undef V 347 348 PropertyAttribute attr = 349 static_cast<PropertyAttribute>(ReadOnly | DontDelete); 350 351 target->DefineOwnProperty(context, 352 env->constants_string(), 353 constants, 354 attr).ToChecked(); 355 356 HistogramBase::Initialize(env, target); 357} 358 359void RegisterExternalReferences(ExternalReferenceRegistry* registry) { 360 registry->Register(MarkMilestone); 361 registry->Register(SetupPerformanceObservers); 362 registry->Register(InstallGarbageCollectionTracking); 363 registry->Register(RemoveGarbageCollectionTracking); 364 registry->Register(Notify); 365 registry->Register(LoopIdleTime); 366 registry->Register(GetTimeOrigin); 367 registry->Register(GetTimeOriginTimeStamp); 368 registry->Register(CreateELDHistogram); 369 registry->Register(MarkBootstrapComplete); 370 HistogramBase::RegisterExternalReferences(registry); 371 IntervalHistogram::RegisterExternalReferences(registry); 372} 373} // namespace performance 374} // namespace node 375 376NODE_BINDING_CONTEXT_AWARE_INTERNAL(performance, node::performance::Initialize) 377NODE_BINDING_EXTERNAL_REFERENCE(performance, 378 node::performance::RegisterExternalReferences) 379