• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "aliased_buffer.h"
2 #include "memory_tracker-inl.h"
3 #include "node_internals.h"
4 #include "node_perf.h"
5 #include "node_buffer.h"
6 #include "node_process.h"
7 #include "util-inl.h"
8 
9 #include <cinttypes>
10 
11 namespace node {
12 namespace performance {
13 
14 using v8::Context;
15 using v8::DontDelete;
16 using v8::Function;
17 using v8::FunctionCallbackInfo;
18 using v8::FunctionTemplate;
19 using v8::GCCallbackFlags;
20 using v8::GCType;
21 using v8::HandleScope;
22 using v8::Integer;
23 using v8::Isolate;
24 using v8::Local;
25 using v8::Map;
26 using v8::MaybeLocal;
27 using v8::NewStringType;
28 using v8::Number;
29 using v8::Object;
30 using v8::PropertyAttribute;
31 using v8::ReadOnly;
32 using v8::String;
33 using v8::Value;
34 
35 // Microseconds in a millisecond, as a float.
36 #define MICROS_PER_MILLIS 1e3
37 
38 // https://w3c.github.io/hr-time/#dfn-time-origin
39 const uint64_t timeOrigin = PERFORMANCE_NOW();
40 // https://w3c.github.io/hr-time/#dfn-time-origin-timestamp
41 const double timeOriginTimestamp = GetCurrentTimeInMicroseconds();
42 uint64_t performance_v8_start;
43 
Mark(enum PerformanceMilestone milestone,uint64_t ts)44 void PerformanceState::Mark(enum PerformanceMilestone milestone,
45                              uint64_t ts) {
46   this->milestones[milestone] = ts;
47   TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
48       TRACING_CATEGORY_NODE1(bootstrap),
49       GetPerformanceMilestoneName(milestone),
50       TRACE_EVENT_SCOPE_THREAD, ts / 1000);
51 }
52 
53 // Initialize the performance entry object properties
InitObject(const PerformanceEntry & entry,Local<Object> obj)54 inline void InitObject(const PerformanceEntry& entry, Local<Object> obj) {
55   Environment* env = entry.env();
56   Isolate* isolate = env->isolate();
57   Local<Context> context = env->context();
58   PropertyAttribute attr =
59       static_cast<PropertyAttribute>(ReadOnly | DontDelete);
60   obj->DefineOwnProperty(context,
61                          env->name_string(),
62                          String::NewFromUtf8(isolate,
63                                              entry.name().c_str(),
64                                              NewStringType::kNormal)
65                              .ToLocalChecked(),
66                          attr)
67       .Check();
68   obj->DefineOwnProperty(context,
69                          env->entry_type_string(),
70                          String::NewFromUtf8(isolate,
71                                              entry.type().c_str(),
72                                              NewStringType::kNormal)
73                              .ToLocalChecked(),
74                          attr)
75       .Check();
76   obj->DefineOwnProperty(context,
77                          env->start_time_string(),
78                          Number::New(isolate, entry.startTime()),
79                          attr).Check();
80   obj->DefineOwnProperty(context,
81                          env->duration_string(),
82                          Number::New(isolate, entry.duration()),
83                          attr).Check();
84 }
85 
86 // Create a new PerformanceEntry object
ToObject() const87 MaybeLocal<Object> PerformanceEntry::ToObject() const {
88   Local<Object> obj;
89   if (!env_->performance_entry_template()
90            ->NewInstance(env_->context())
91            .ToLocal(&obj)) {
92     return MaybeLocal<Object>();
93   }
94   InitObject(*this, obj);
95   return obj;
96 }
97 
98 // Allow creating a PerformanceEntry object from JavaScript
New(const FunctionCallbackInfo<Value> & args)99 void PerformanceEntry::New(const FunctionCallbackInfo<Value>& args) {
100   Environment* env = Environment::GetCurrent(args);
101   Isolate* isolate = env->isolate();
102   Utf8Value name(isolate, args[0]);
103   Utf8Value type(isolate, args[1]);
104   uint64_t now = PERFORMANCE_NOW();
105   PerformanceEntry entry(env, *name, *type, now, now);
106   Local<Object> obj = args.This();
107   InitObject(entry, obj);
108   PerformanceEntry::Notify(env, entry.kind(), obj);
109 }
110 
111 // Pass the PerformanceEntry object to the PerformanceObservers
Notify(Environment * env,PerformanceEntryType type,Local<Value> object)112 void PerformanceEntry::Notify(Environment* env,
113                               PerformanceEntryType type,
114                               Local<Value> object) {
115   Context::Scope scope(env->context());
116   AliasedUint32Array& observers = env->performance_state()->observers;
117   if (type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
118       observers[type]) {
119     node::MakeCallback(env->isolate(),
120                        object.As<Object>(),
121                        env->performance_entry_callback(),
122                        1, &object,
123                        node::async_context{0, 0});
124   }
125 }
126 
127 // Create a User Timing Mark
Mark(const FunctionCallbackInfo<Value> & args)128 void Mark(const FunctionCallbackInfo<Value>& args) {
129   Environment* env = Environment::GetCurrent(args);
130   HandleScope scope(env->isolate());
131   Utf8Value name(env->isolate(), args[0]);
132   uint64_t now = PERFORMANCE_NOW();
133   auto marks = env->performance_marks();
134   (*marks)[*name] = now;
135 
136   TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP(
137       TRACING_CATEGORY_NODE2(perf, usertiming),
138       *name, now / 1000);
139 
140   PerformanceEntry entry(env, *name, "mark", now, now);
141   Local<Object> obj;
142   if (!entry.ToObject().ToLocal(&obj)) return;
143   PerformanceEntry::Notify(env, entry.kind(), obj);
144   args.GetReturnValue().Set(obj);
145 }
146 
ClearMark(const FunctionCallbackInfo<Value> & args)147 void ClearMark(const FunctionCallbackInfo<Value>& args) {
148   Environment* env = Environment::GetCurrent(args);
149   auto marks = env->performance_marks();
150 
151   if (args.Length() == 0) {
152     marks->clear();
153   } else {
154     Utf8Value name(env->isolate(), args[0]);
155     marks->erase(*name);
156   }
157 }
158 
GetPerformanceMark(Environment * env,const std::string & name)159 inline uint64_t GetPerformanceMark(Environment* env, const std::string& name) {
160   auto marks = env->performance_marks();
161   auto res = marks->find(name);
162   return res != marks->end() ? res->second : 0;
163 }
164 
165 // Create a User Timing Measure. A Measure is a PerformanceEntry that
166 // measures the duration between two distinct user timing marks
Measure(const FunctionCallbackInfo<Value> & args)167 void Measure(const FunctionCallbackInfo<Value>& args) {
168   Environment* env = Environment::GetCurrent(args);
169   HandleScope scope(env->isolate());
170   Utf8Value name(env->isolate(), args[0]);
171   Utf8Value startMark(env->isolate(), args[1]);
172 
173   AliasedFloat64Array& milestones = env->performance_state()->milestones;
174 
175   uint64_t startTimestamp = timeOrigin;
176   uint64_t start = GetPerformanceMark(env, *startMark);
177   if (start != 0) {
178     startTimestamp = start;
179   } else {
180     PerformanceMilestone milestone = ToPerformanceMilestoneEnum(*startMark);
181     if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
182       startTimestamp = milestones[milestone];
183   }
184 
185   uint64_t endTimestamp = 0;
186   if (args[2]->IsUndefined()) {
187     endTimestamp = PERFORMANCE_NOW();
188   } else {
189     Utf8Value endMark(env->isolate(), args[2]);
190     endTimestamp = GetPerformanceMark(env, *endMark);
191     if (endTimestamp == 0) {
192       PerformanceMilestone milestone = ToPerformanceMilestoneEnum(*endMark);
193       if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
194         endTimestamp = milestones[milestone];
195     }
196   }
197 
198   if (endTimestamp < startTimestamp)
199     endTimestamp = startTimestamp;
200 
201   TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
202       TRACING_CATEGORY_NODE2(perf, usertiming),
203       *name, *name, startTimestamp / 1000);
204   TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
205       TRACING_CATEGORY_NODE2(perf, usertiming),
206       *name, *name, endTimestamp / 1000);
207 
208   PerformanceEntry entry(env, *name, "measure", startTimestamp, endTimestamp);
209   Local<Object> obj;
210   if (!entry.ToObject().ToLocal(&obj)) return;
211   PerformanceEntry::Notify(env, entry.kind(), obj);
212   args.GetReturnValue().Set(obj);
213 }
214 
215 // Allows specific Node.js lifecycle milestones to be set from JavaScript
MarkMilestone(const FunctionCallbackInfo<Value> & args)216 void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
217   Environment* env = Environment::GetCurrent(args);
218   Local<Context> context = env->context();
219   PerformanceMilestone milestone =
220       static_cast<PerformanceMilestone>(
221           args[0]->Int32Value(context).ToChecked());
222   if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
223     env->performance_state()->Mark(milestone);
224 }
225 
226 
SetupPerformanceObservers(const FunctionCallbackInfo<Value> & args)227 void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
228   Environment* env = Environment::GetCurrent(args);
229   CHECK(args[0]->IsFunction());
230   env->set_performance_entry_callback(args[0].As<Function>());
231 }
232 
233 // Creates a GC Performance Entry and passes it to observers
PerformanceGCCallback(Environment * env,std::unique_ptr<GCPerformanceEntry> entry)234 void PerformanceGCCallback(Environment* env,
235                            std::unique_ptr<GCPerformanceEntry> entry) {
236   HandleScope scope(env->isolate());
237   Local<Context> context = env->context();
238 
239   AliasedUint32Array& observers = env->performance_state()->observers;
240   if (observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) {
241     Local<Object> obj;
242     if (!entry->ToObject().ToLocal(&obj)) return;
243     PropertyAttribute attr =
244         static_cast<PropertyAttribute>(ReadOnly | DontDelete);
245     obj->DefineOwnProperty(context,
246                            env->kind_string(),
247                            Integer::New(env->isolate(), entry->gckind()),
248                            attr).Check();
249     obj->DefineOwnProperty(context,
250                            env->flags_string(),
251                            Integer::New(env->isolate(), entry->gcflags()),
252                            attr).Check();
253     PerformanceEntry::Notify(env, entry->kind(), obj);
254   }
255 }
256 
257 // Marks the start of a GC cycle
MarkGarbageCollectionStart(Isolate * isolate,GCType type,GCCallbackFlags flags,void * data)258 void MarkGarbageCollectionStart(Isolate* isolate,
259                                 GCType type,
260                                 GCCallbackFlags flags,
261                                 void* data) {
262   Environment* env = static_cast<Environment*>(data);
263   env->performance_state()->performance_last_gc_start_mark = PERFORMANCE_NOW();
264 }
265 
266 // Marks the end of a GC cycle
MarkGarbageCollectionEnd(Isolate * isolate,GCType type,GCCallbackFlags flags,void * data)267 void MarkGarbageCollectionEnd(Isolate* isolate,
268                               GCType type,
269                               GCCallbackFlags flags,
270                               void* data) {
271   Environment* env = static_cast<Environment*>(data);
272   PerformanceState* state = env->performance_state();
273   // If no one is listening to gc performance entries, do not create them.
274   if (!state->observers[NODE_PERFORMANCE_ENTRY_TYPE_GC])
275     return;
276   auto entry = std::make_unique<GCPerformanceEntry>(
277       env,
278       static_cast<PerformanceGCKind>(type),
279       static_cast<PerformanceGCFlags>(flags),
280       state->performance_last_gc_start_mark,
281       PERFORMANCE_NOW());
282   env->SetImmediate([entry = std::move(entry)](Environment* env) mutable {
283     PerformanceGCCallback(env, std::move(entry));
284   }, CallbackFlags::kUnrefed);
285 }
286 
GarbageCollectionCleanupHook(void * data)287 void GarbageCollectionCleanupHook(void* data) {
288   Environment* env = static_cast<Environment*>(data);
289   env->isolate()->RemoveGCPrologueCallback(MarkGarbageCollectionStart, data);
290   env->isolate()->RemoveGCEpilogueCallback(MarkGarbageCollectionEnd, data);
291 }
292 
InstallGarbageCollectionTracking(const FunctionCallbackInfo<Value> & args)293 static void InstallGarbageCollectionTracking(
294     const FunctionCallbackInfo<Value>& args) {
295   Environment* env = Environment::GetCurrent(args);
296 
297   env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart,
298                                         static_cast<void*>(env));
299   env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd,
300                                         static_cast<void*>(env));
301   env->AddCleanupHook(GarbageCollectionCleanupHook, env);
302 }
303 
RemoveGarbageCollectionTracking(const FunctionCallbackInfo<Value> & args)304 static void RemoveGarbageCollectionTracking(
305   const FunctionCallbackInfo<Value> &args) {
306   Environment* env = Environment::GetCurrent(args);
307 
308   env->RemoveCleanupHook(GarbageCollectionCleanupHook, env);
309   GarbageCollectionCleanupHook(env);
310 }
311 
312 // Gets the name of a function
GetName(Local<Function> fn)313 inline Local<Value> GetName(Local<Function> fn) {
314   Local<Value> val = fn->GetDebugName();
315   if (val.IsEmpty() || val->IsUndefined()) {
316     Local<Value> boundFunction = fn->GetBoundFunction();
317     if (!boundFunction.IsEmpty() && !boundFunction->IsUndefined()) {
318       val = GetName(boundFunction.As<Function>());
319     }
320   }
321   return val;
322 }
323 
324 // Executes a wrapped Function and captures timing information, causing a
325 // Function PerformanceEntry to be emitted to PerformanceObservers after
326 // execution.
TimerFunctionCall(const FunctionCallbackInfo<Value> & args)327 void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
328   Isolate* isolate = args.GetIsolate();
329   Local<Context> context = isolate->GetCurrentContext();
330   Environment* env = Environment::GetCurrent(context);
331   CHECK_NOT_NULL(env);
332   Local<Function> fn = args.Data().As<Function>();
333   size_t count = args.Length();
334   size_t idx;
335   SlicedArguments call_args(args);
336   Utf8Value name(isolate, GetName(fn));
337   bool is_construct_call = args.IsConstructCall();
338 
339   uint64_t start = PERFORMANCE_NOW();
340   TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
341       TRACING_CATEGORY_NODE2(perf, timerify),
342       *name, *name, start / 1000);
343   v8::MaybeLocal<Value> ret;
344 
345   if (is_construct_call) {
346     ret = fn->NewInstance(context, call_args.length(), call_args.out())
347         .FromMaybe(Local<Object>());
348   } else {
349     ret = fn->Call(context, args.This(), call_args.length(), call_args.out());
350   }
351 
352   uint64_t end = PERFORMANCE_NOW();
353   TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
354       TRACING_CATEGORY_NODE2(perf, timerify),
355       *name, *name, end / 1000);
356 
357   if (ret.IsEmpty())
358     return;
359   args.GetReturnValue().Set(ret.ToLocalChecked());
360 
361   AliasedUint32Array& observers = env->performance_state()->observers;
362   if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION])
363     return;
364 
365   PerformanceEntry entry(env, *name, "function", start, end);
366   Local<Object> obj;
367   if (!entry.ToObject().ToLocal(&obj)) return;
368   for (idx = 0; idx < count; idx++)
369     obj->Set(context, idx, args[idx]).Check();
370   PerformanceEntry::Notify(env, entry.kind(), obj);
371 }
372 
373 // Wraps a Function in a TimerFunctionCall
Timerify(const FunctionCallbackInfo<Value> & args)374 void Timerify(const FunctionCallbackInfo<Value>& args) {
375   Environment* env = Environment::GetCurrent(args);
376   Local<Context> context = env->context();
377   CHECK(args[0]->IsFunction());
378   CHECK(args[1]->IsNumber());
379   Local<Function> fn = args[0].As<Function>();
380   int length = args[1]->IntegerValue(context).ToChecked();
381   Local<Function> wrap =
382       Function::New(context, TimerFunctionCall, fn, length).ToLocalChecked();
383   args.GetReturnValue().Set(wrap);
384 }
385 
386 // Notify a custom PerformanceEntry to observers
Notify(const FunctionCallbackInfo<Value> & args)387 void Notify(const FunctionCallbackInfo<Value>& args) {
388   Environment* env = Environment::GetCurrent(args);
389   Utf8Value type(env->isolate(), args[0]);
390   Local<Value> entry = args[1];
391   PerformanceEntryType entry_type = ToPerformanceEntryTypeEnum(*type);
392   AliasedUint32Array& observers = env->performance_state()->observers;
393   if (entry_type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
394       observers[entry_type]) {
395     USE(env->performance_entry_callback()->
396       Call(env->context(), Undefined(env->isolate()), 1, &entry));
397   }
398 }
399 
400 // Return idle time of the event loop
LoopIdleTime(const FunctionCallbackInfo<Value> & args)401 void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
402   Environment* env = Environment::GetCurrent(args);
403   uint64_t idle_time = uv_metrics_idle_time(env->event_loop());
404   args.GetReturnValue().Set(1.0 * idle_time / 1e6);
405 }
406 
407 
408 // Event Loop Timing Histogram
409 namespace {
ELDHistogramMin(const FunctionCallbackInfo<Value> & args)410 static void ELDHistogramMin(const FunctionCallbackInfo<Value>& args) {
411   ELDHistogram* histogram;
412   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
413   double value = static_cast<double>(histogram->Min());
414   args.GetReturnValue().Set(value);
415 }
416 
ELDHistogramMax(const FunctionCallbackInfo<Value> & args)417 static void ELDHistogramMax(const FunctionCallbackInfo<Value>& args) {
418   ELDHistogram* histogram;
419   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
420   double value = static_cast<double>(histogram->Max());
421   args.GetReturnValue().Set(value);
422 }
423 
ELDHistogramMean(const FunctionCallbackInfo<Value> & args)424 static void ELDHistogramMean(const FunctionCallbackInfo<Value>& args) {
425   ELDHistogram* histogram;
426   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
427   args.GetReturnValue().Set(histogram->Mean());
428 }
429 
ELDHistogramExceeds(const FunctionCallbackInfo<Value> & args)430 static void ELDHistogramExceeds(const FunctionCallbackInfo<Value>& args) {
431   ELDHistogram* histogram;
432   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
433   double value = static_cast<double>(histogram->Exceeds());
434   args.GetReturnValue().Set(value);
435 }
436 
ELDHistogramStddev(const FunctionCallbackInfo<Value> & args)437 static void ELDHistogramStddev(const FunctionCallbackInfo<Value>& args) {
438   ELDHistogram* histogram;
439   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
440   args.GetReturnValue().Set(histogram->Stddev());
441 }
442 
ELDHistogramPercentile(const FunctionCallbackInfo<Value> & args)443 static void ELDHistogramPercentile(const FunctionCallbackInfo<Value>& args) {
444   ELDHistogram* histogram;
445   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
446   CHECK(args[0]->IsNumber());
447   double percentile = args[0].As<Number>()->Value();
448   args.GetReturnValue().Set(histogram->Percentile(percentile));
449 }
450 
ELDHistogramPercentiles(const FunctionCallbackInfo<Value> & args)451 static void ELDHistogramPercentiles(const FunctionCallbackInfo<Value>& args) {
452   Environment* env = Environment::GetCurrent(args);
453   ELDHistogram* histogram;
454   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
455   CHECK(args[0]->IsMap());
456   Local<Map> map = args[0].As<Map>();
457   histogram->Percentiles([&](double key, double value) {
458     map->Set(env->context(),
459              Number::New(env->isolate(), key),
460              Number::New(env->isolate(), value)).IsEmpty();
461   });
462 }
463 
ELDHistogramEnable(const FunctionCallbackInfo<Value> & args)464 static void ELDHistogramEnable(const FunctionCallbackInfo<Value>& args) {
465   ELDHistogram* histogram;
466   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
467   args.GetReturnValue().Set(histogram->Enable());
468 }
469 
ELDHistogramDisable(const FunctionCallbackInfo<Value> & args)470 static void ELDHistogramDisable(const FunctionCallbackInfo<Value>& args) {
471   ELDHistogram* histogram;
472   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
473   args.GetReturnValue().Set(histogram->Disable());
474 }
475 
ELDHistogramReset(const FunctionCallbackInfo<Value> & args)476 static void ELDHistogramReset(const FunctionCallbackInfo<Value>& args) {
477   ELDHistogram* histogram;
478   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
479   histogram->ResetState();
480 }
481 
ELDHistogramNew(const FunctionCallbackInfo<Value> & args)482 static void ELDHistogramNew(const FunctionCallbackInfo<Value>& args) {
483   Environment* env = Environment::GetCurrent(args);
484   CHECK(args.IsConstructCall());
485   int32_t resolution = args[0]->IntegerValue(env->context()).FromJust();
486   CHECK_GT(resolution, 0);
487   new ELDHistogram(env, args.This(), resolution);
488 }
489 }  // namespace
490 
ELDHistogram(Environment * env,Local<Object> wrap,int32_t resolution)491 ELDHistogram::ELDHistogram(
492     Environment* env,
493     Local<Object> wrap,
494     int32_t resolution) : HandleWrap(env,
495                                      wrap,
496                                      reinterpret_cast<uv_handle_t*>(&timer_),
497                                      AsyncWrap::PROVIDER_ELDHISTOGRAM),
498                           Histogram(1, 3.6e12),
499                           resolution_(resolution) {
500   MakeWeak();
501   uv_timer_init(env->event_loop(), &timer_);
502 }
503 
DelayIntervalCallback(uv_timer_t * req)504 void ELDHistogram::DelayIntervalCallback(uv_timer_t* req) {
505   ELDHistogram* histogram = ContainerOf(&ELDHistogram::timer_, req);
506   histogram->RecordDelta();
507   TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
508                  "min", histogram->Min());
509   TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
510                  "max", histogram->Max());
511   TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
512                  "mean", histogram->Mean());
513   TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
514                  "stddev", histogram->Stddev());
515 }
516 
RecordDelta()517 bool ELDHistogram::RecordDelta() {
518   uint64_t time = uv_hrtime();
519   bool ret = true;
520   if (prev_ > 0) {
521     int64_t delta = time - prev_;
522     if (delta > 0) {
523       ret = Record(delta);
524       TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
525                      "delay", delta);
526       if (!ret) {
527         if (exceeds_ < 0xFFFFFFFF)
528           exceeds_++;
529         ProcessEmitWarning(
530             env(),
531             "Event loop delay exceeded 1 hour: %" PRId64 " nanoseconds",
532             delta);
533       }
534     }
535   }
536   prev_ = time;
537   return ret;
538 }
539 
Enable()540 bool ELDHistogram::Enable() {
541   if (enabled_ || IsHandleClosing()) return false;
542   enabled_ = true;
543   prev_ = 0;
544   uv_timer_start(&timer_,
545                  DelayIntervalCallback,
546                  resolution_,
547                  resolution_);
548   uv_unref(reinterpret_cast<uv_handle_t*>(&timer_));
549   return true;
550 }
551 
Disable()552 bool ELDHistogram::Disable() {
553   if (!enabled_ || IsHandleClosing()) return false;
554   enabled_ = false;
555   uv_timer_stop(&timer_);
556   return true;
557 }
558 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)559 void Initialize(Local<Object> target,
560                 Local<Value> unused,
561                 Local<Context> context,
562                 void* priv) {
563   Environment* env = Environment::GetCurrent(context);
564   Isolate* isolate = env->isolate();
565   PerformanceState* state = env->performance_state();
566 
567   target->Set(context,
568               FIXED_ONE_BYTE_STRING(isolate, "observerCounts"),
569               state->observers.GetJSArray()).Check();
570   target->Set(context,
571               FIXED_ONE_BYTE_STRING(isolate, "milestones"),
572               state->milestones.GetJSArray()).Check();
573 
574   Local<String> performanceEntryString =
575       FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry");
576 
577   Local<FunctionTemplate> pe = FunctionTemplate::New(isolate);
578   pe->SetClassName(performanceEntryString);
579   Local<Function> fn = pe->GetFunction(context).ToLocalChecked();
580   target->Set(context, performanceEntryString, fn).Check();
581   env->set_performance_entry_template(fn);
582 
583   env->SetMethod(target, "clearMark", ClearMark);
584   env->SetMethod(target, "mark", Mark);
585   env->SetMethod(target, "measure", Measure);
586   env->SetMethod(target, "markMilestone", MarkMilestone);
587   env->SetMethod(target, "setupObservers", SetupPerformanceObservers);
588   env->SetMethod(target, "timerify", Timerify);
589   env->SetMethod(target,
590                  "installGarbageCollectionTracking",
591                  InstallGarbageCollectionTracking);
592   env->SetMethod(target,
593                  "removeGarbageCollectionTracking",
594                  RemoveGarbageCollectionTracking);
595   env->SetMethod(target, "notify", Notify);
596   env->SetMethod(target, "loopIdleTime", LoopIdleTime);
597 
598   Local<Object> constants = Object::New(isolate);
599 
600   NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MAJOR);
601   NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MINOR);
602   NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_INCREMENTAL);
603   NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_WEAKCB);
604 
605   NODE_DEFINE_CONSTANT(
606     constants, NODE_PERFORMANCE_GC_FLAGS_NO);
607   NODE_DEFINE_CONSTANT(
608     constants, NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED);
609   NODE_DEFINE_CONSTANT(
610     constants, NODE_PERFORMANCE_GC_FLAGS_FORCED);
611   NODE_DEFINE_CONSTANT(
612     constants, NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING);
613   NODE_DEFINE_CONSTANT(
614     constants, NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE);
615   NODE_DEFINE_CONSTANT(
616     constants, NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY);
617   NODE_DEFINE_CONSTANT(
618     constants, NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE);
619 
620 #define V(name, _)                                                            \
621   NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_ENTRY_TYPE_##name);
622   NODE_PERFORMANCE_ENTRY_TYPES(V)
623 #undef V
624 
625 #define V(name, _)                                                            \
626   NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_MILESTONE_##name);
627   NODE_PERFORMANCE_MILESTONES(V)
628 #undef V
629 
630   PropertyAttribute attr =
631       static_cast<PropertyAttribute>(ReadOnly | DontDelete);
632 
633   target->DefineOwnProperty(context,
634                             FIXED_ONE_BYTE_STRING(isolate, "timeOrigin"),
635                             Number::New(isolate, timeOrigin / 1e6),
636                             attr).ToChecked();
637 
638   target->DefineOwnProperty(
639       context,
640       FIXED_ONE_BYTE_STRING(isolate, "timeOriginTimestamp"),
641       Number::New(isolate, timeOriginTimestamp / MICROS_PER_MILLIS),
642       attr).ToChecked();
643 
644   target->DefineOwnProperty(context,
645                             env->constants_string(),
646                             constants,
647                             attr).ToChecked();
648 
649   Local<String> eldh_classname = FIXED_ONE_BYTE_STRING(isolate, "ELDHistogram");
650   Local<FunctionTemplate> eldh =
651       env->NewFunctionTemplate(ELDHistogramNew);
652   eldh->SetClassName(eldh_classname);
653   eldh->InstanceTemplate()->SetInternalFieldCount(
654       ELDHistogram::kInternalFieldCount);
655   eldh->Inherit(BaseObject::GetConstructorTemplate(env));
656   env->SetProtoMethod(eldh, "exceeds", ELDHistogramExceeds);
657   env->SetProtoMethod(eldh, "min", ELDHistogramMin);
658   env->SetProtoMethod(eldh, "max", ELDHistogramMax);
659   env->SetProtoMethod(eldh, "mean", ELDHistogramMean);
660   env->SetProtoMethod(eldh, "stddev", ELDHistogramStddev);
661   env->SetProtoMethod(eldh, "percentile", ELDHistogramPercentile);
662   env->SetProtoMethod(eldh, "percentiles", ELDHistogramPercentiles);
663   env->SetProtoMethod(eldh, "enable", ELDHistogramEnable);
664   env->SetProtoMethod(eldh, "disable", ELDHistogramDisable);
665   env->SetProtoMethod(eldh, "reset", ELDHistogramReset);
666   target->Set(context, eldh_classname,
667               eldh->GetFunction(env->context()).ToLocalChecked()).Check();
668 }
669 
670 }  // namespace performance
671 }  // namespace node
672 
673 NODE_MODULE_CONTEXT_AWARE_INTERNAL(performance, node::performance::Initialize)
674