• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "aliased_buffer.h"
2 #include "histogram-inl.h"
3 #include "memory_tracker-inl.h"
4 #include "node_internals.h"
5 #include "node_perf.h"
6 #include "node_buffer.h"
7 #include "node_process-inl.h"
8 #include "util-inl.h"
9 
10 #include <cinttypes>
11 
12 namespace node {
13 namespace performance {
14 
15 using v8::Context;
16 using v8::DontDelete;
17 using v8::Function;
18 using v8::FunctionCallbackInfo;
19 using v8::FunctionTemplate;
20 using v8::GCCallbackFlags;
21 using v8::GCType;
22 using v8::HandleScope;
23 using v8::Int32;
24 using v8::Integer;
25 using v8::Isolate;
26 using v8::Local;
27 using v8::MaybeLocal;
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                              .ToLocalChecked(),
65                          attr)
66       .Check();
67   obj->DefineOwnProperty(context,
68                          env->entry_type_string(),
69                          String::NewFromUtf8(isolate,
70                                              entry.type().c_str())
71                              .ToLocalChecked(),
72                          attr)
73       .Check();
74   obj->DefineOwnProperty(context,
75                          env->start_time_string(),
76                          Number::New(isolate, entry.startTime()),
77                          attr).Check();
78   obj->DefineOwnProperty(context,
79                          env->duration_string(),
80                          Number::New(isolate, entry.duration()),
81                          attr).Check();
82 }
83 
84 // Create a new PerformanceEntry object
ToObject() const85 MaybeLocal<Object> PerformanceEntry::ToObject() const {
86   Local<Object> obj;
87   if (!env_->performance_entry_template()
88            ->NewInstance(env_->context())
89            .ToLocal(&obj)) {
90     return MaybeLocal<Object>();
91   }
92   InitObject(*this, obj);
93   return obj;
94 }
95 
96 // Allow creating a PerformanceEntry object from JavaScript
New(const FunctionCallbackInfo<Value> & args)97 void PerformanceEntry::New(const FunctionCallbackInfo<Value>& args) {
98   Environment* env = Environment::GetCurrent(args);
99   Isolate* isolate = env->isolate();
100   Utf8Value name(isolate, args[0]);
101   Utf8Value type(isolate, args[1]);
102   uint64_t now = PERFORMANCE_NOW();
103   PerformanceEntry entry(env, *name, *type, now, now);
104   Local<Object> obj = args.This();
105   InitObject(entry, obj);
106   PerformanceEntry::Notify(env, entry.kind(), obj);
107 }
108 
109 // Pass the PerformanceEntry object to the PerformanceObservers
Notify(Environment * env,PerformanceEntryType type,Local<Value> object)110 void PerformanceEntry::Notify(Environment* env,
111                               PerformanceEntryType type,
112                               Local<Value> object) {
113   Context::Scope scope(env->context());
114   AliasedUint32Array& observers = env->performance_state()->observers;
115   if (type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
116       observers[type]) {
117     node::MakeCallback(env->isolate(),
118                        object.As<Object>(),
119                        env->performance_entry_callback(),
120                        1, &object,
121                        node::async_context{0, 0});
122   }
123 }
124 
125 // Create a User Timing Mark
Mark(const FunctionCallbackInfo<Value> & args)126 void Mark(const FunctionCallbackInfo<Value>& args) {
127   Environment* env = Environment::GetCurrent(args);
128   HandleScope scope(env->isolate());
129   Utf8Value name(env->isolate(), args[0]);
130   uint64_t now = PERFORMANCE_NOW();
131   auto marks = env->performance_marks();
132   (*marks)[*name] = now;
133 
134   TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP(
135       TRACING_CATEGORY_NODE2(perf, usertiming),
136       *name, now / 1000);
137 
138   PerformanceEntry entry(env, *name, "mark", now, now);
139   Local<Object> obj;
140   if (!entry.ToObject().ToLocal(&obj)) return;
141   PerformanceEntry::Notify(env, entry.kind(), obj);
142   args.GetReturnValue().Set(obj);
143 }
144 
ClearMark(const FunctionCallbackInfo<Value> & args)145 void ClearMark(const FunctionCallbackInfo<Value>& args) {
146   Environment* env = Environment::GetCurrent(args);
147   auto marks = env->performance_marks();
148 
149   if (args.Length() == 0) {
150     marks->clear();
151   } else {
152     Utf8Value name(env->isolate(), args[0]);
153     marks->erase(*name);
154   }
155 }
156 
GetPerformanceMark(Environment * env,const std::string & name)157 inline uint64_t GetPerformanceMark(Environment* env, const std::string& name) {
158   auto marks = env->performance_marks();
159   auto res = marks->find(name);
160   return res != marks->end() ? res->second : 0;
161 }
162 
163 // Create a User Timing Measure. A Measure is a PerformanceEntry that
164 // measures the duration between two distinct user timing marks
Measure(const FunctionCallbackInfo<Value> & args)165 void Measure(const FunctionCallbackInfo<Value>& args) {
166   Environment* env = Environment::GetCurrent(args);
167   HandleScope scope(env->isolate());
168   Utf8Value name(env->isolate(), args[0]);
169   Utf8Value startMark(env->isolate(), args[1]);
170 
171   AliasedFloat64Array& milestones = env->performance_state()->milestones;
172 
173   uint64_t startTimestamp = timeOrigin;
174   uint64_t start = GetPerformanceMark(env, *startMark);
175   if (start != 0) {
176     startTimestamp = start;
177   } else {
178     PerformanceMilestone milestone = ToPerformanceMilestoneEnum(*startMark);
179     if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
180       startTimestamp = milestones[milestone];
181   }
182 
183   uint64_t endTimestamp = 0;
184   if (args[2]->IsUndefined()) {
185     endTimestamp = PERFORMANCE_NOW();
186   } else {
187     Utf8Value endMark(env->isolate(), args[2]);
188     endTimestamp = GetPerformanceMark(env, *endMark);
189     if (endTimestamp == 0) {
190       PerformanceMilestone milestone = ToPerformanceMilestoneEnum(*endMark);
191       if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
192         endTimestamp = milestones[milestone];
193     }
194   }
195 
196   if (endTimestamp < startTimestamp)
197     endTimestamp = startTimestamp;
198 
199   TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
200       TRACING_CATEGORY_NODE2(perf, usertiming),
201       *name, *name, startTimestamp / 1000);
202   TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
203       TRACING_CATEGORY_NODE2(perf, usertiming),
204       *name, *name, endTimestamp / 1000);
205 
206   PerformanceEntry entry(env, *name, "measure", startTimestamp, endTimestamp);
207   Local<Object> obj;
208   if (!entry.ToObject().ToLocal(&obj)) return;
209   PerformanceEntry::Notify(env, entry.kind(), obj);
210   args.GetReturnValue().Set(obj);
211 }
212 
213 // Allows specific Node.js lifecycle milestones to be set from JavaScript
MarkMilestone(const FunctionCallbackInfo<Value> & args)214 void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
215   Environment* env = Environment::GetCurrent(args);
216   Local<Context> context = env->context();
217   PerformanceMilestone milestone =
218       static_cast<PerformanceMilestone>(
219           args[0]->Int32Value(context).ToChecked());
220   if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
221     env->performance_state()->Mark(milestone);
222 }
223 
224 
SetupPerformanceObservers(const FunctionCallbackInfo<Value> & args)225 void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
226   Environment* env = Environment::GetCurrent(args);
227   CHECK(args[0]->IsFunction());
228   env->set_performance_entry_callback(args[0].As<Function>());
229 }
230 
231 // Creates a GC Performance Entry and passes it to observers
PerformanceGCCallback(Environment * env,std::unique_ptr<GCPerformanceEntry> entry)232 void PerformanceGCCallback(Environment* env,
233                            std::unique_ptr<GCPerformanceEntry> entry) {
234   HandleScope scope(env->isolate());
235   Local<Context> context = env->context();
236 
237   AliasedUint32Array& observers = env->performance_state()->observers;
238   if (observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) {
239     Local<Object> obj;
240     if (!entry->ToObject().ToLocal(&obj)) return;
241     PropertyAttribute attr =
242         static_cast<PropertyAttribute>(ReadOnly | DontDelete);
243     obj->DefineOwnProperty(context,
244                            env->kind_string(),
245                            Integer::New(env->isolate(), entry->gckind()),
246                            attr).Check();
247     obj->DefineOwnProperty(context,
248                            env->flags_string(),
249                            Integer::New(env->isolate(), entry->gcflags()),
250                            attr).Check();
251     PerformanceEntry::Notify(env, entry->kind(), obj);
252   }
253 }
254 
255 // Marks the start of a GC cycle
MarkGarbageCollectionStart(Isolate * isolate,GCType type,GCCallbackFlags flags,void * data)256 void MarkGarbageCollectionStart(Isolate* isolate,
257                                 GCType type,
258                                 GCCallbackFlags flags,
259                                 void* data) {
260   Environment* env = static_cast<Environment*>(data);
261   env->performance_state()->performance_last_gc_start_mark = PERFORMANCE_NOW();
262 }
263 
264 // Marks the end of a GC cycle
MarkGarbageCollectionEnd(Isolate * isolate,GCType type,GCCallbackFlags flags,void * data)265 void MarkGarbageCollectionEnd(Isolate* isolate,
266                               GCType type,
267                               GCCallbackFlags flags,
268                               void* data) {
269   Environment* env = static_cast<Environment*>(data);
270   PerformanceState* state = env->performance_state();
271   // If no one is listening to gc performance entries, do not create them.
272   if (!state->observers[NODE_PERFORMANCE_ENTRY_TYPE_GC])
273     return;
274   auto entry = std::make_unique<GCPerformanceEntry>(
275       env,
276       static_cast<PerformanceGCKind>(type),
277       static_cast<PerformanceGCFlags>(flags),
278       state->performance_last_gc_start_mark,
279       PERFORMANCE_NOW());
280   env->SetImmediate([entry = std::move(entry)](Environment* env) mutable {
281     PerformanceGCCallback(env, std::move(entry));
282   }, CallbackFlags::kUnrefed);
283 }
284 
GarbageCollectionCleanupHook(void * data)285 void GarbageCollectionCleanupHook(void* data) {
286   Environment* env = static_cast<Environment*>(data);
287   env->isolate()->RemoveGCPrologueCallback(MarkGarbageCollectionStart, data);
288   env->isolate()->RemoveGCEpilogueCallback(MarkGarbageCollectionEnd, data);
289 }
290 
InstallGarbageCollectionTracking(const FunctionCallbackInfo<Value> & args)291 static void InstallGarbageCollectionTracking(
292     const FunctionCallbackInfo<Value>& args) {
293   Environment* env = Environment::GetCurrent(args);
294 
295   env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart,
296                                         static_cast<void*>(env));
297   env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd,
298                                         static_cast<void*>(env));
299   env->AddCleanupHook(GarbageCollectionCleanupHook, env);
300 }
301 
RemoveGarbageCollectionTracking(const FunctionCallbackInfo<Value> & args)302 static void RemoveGarbageCollectionTracking(
303   const FunctionCallbackInfo<Value> &args) {
304   Environment* env = Environment::GetCurrent(args);
305 
306   env->RemoveCleanupHook(GarbageCollectionCleanupHook, env);
307   GarbageCollectionCleanupHook(env);
308 }
309 
310 // Gets the name of a function
GetName(Local<Function> fn)311 inline Local<Value> GetName(Local<Function> fn) {
312   Local<Value> val = fn->GetDebugName();
313   if (val.IsEmpty() || val->IsUndefined()) {
314     Local<Value> boundFunction = fn->GetBoundFunction();
315     if (!boundFunction.IsEmpty() && !boundFunction->IsUndefined()) {
316       val = GetName(boundFunction.As<Function>());
317     }
318   }
319   return val;
320 }
321 
322 // Executes a wrapped Function and captures timing information, causing a
323 // Function PerformanceEntry to be emitted to PerformanceObservers after
324 // execution.
TimerFunctionCall(const FunctionCallbackInfo<Value> & args)325 void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
326   Isolate* isolate = args.GetIsolate();
327   Local<Context> context = isolate->GetCurrentContext();
328   Environment* env = Environment::GetCurrent(context);
329   CHECK_NOT_NULL(env);
330   Local<Function> fn = args.Data().As<Function>();
331   size_t count = args.Length();
332   size_t idx;
333   SlicedArguments call_args(args);
334   Utf8Value name(isolate, GetName(fn));
335   bool is_construct_call = args.IsConstructCall();
336 
337   uint64_t start = PERFORMANCE_NOW();
338   TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
339       TRACING_CATEGORY_NODE2(perf, timerify),
340       *name, *name, start / 1000);
341   v8::MaybeLocal<Value> ret;
342 
343   if (is_construct_call) {
344     ret = fn->NewInstance(context, call_args.length(), call_args.out())
345         .FromMaybe(Local<Object>());
346   } else {
347     ret = fn->Call(context, args.This(), call_args.length(), call_args.out());
348   }
349 
350   uint64_t end = PERFORMANCE_NOW();
351   TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
352       TRACING_CATEGORY_NODE2(perf, timerify),
353       *name, *name, end / 1000);
354 
355   if (ret.IsEmpty())
356     return;
357   args.GetReturnValue().Set(ret.ToLocalChecked());
358 
359   AliasedUint32Array& observers = env->performance_state()->observers;
360   if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION])
361     return;
362 
363   PerformanceEntry entry(env, *name, "function", start, end);
364   Local<Object> obj;
365   if (!entry.ToObject().ToLocal(&obj)) return;
366   for (idx = 0; idx < count; idx++)
367     obj->Set(context, idx, args[idx]).Check();
368   PerformanceEntry::Notify(env, entry.kind(), obj);
369 }
370 
371 // Wraps a Function in a TimerFunctionCall
Timerify(const FunctionCallbackInfo<Value> & args)372 void Timerify(const FunctionCallbackInfo<Value>& args) {
373   Environment* env = Environment::GetCurrent(args);
374   Local<Context> context = env->context();
375   CHECK(args[0]->IsFunction());
376   CHECK(args[1]->IsNumber());
377   Local<Function> fn = args[0].As<Function>();
378   int length = args[1]->IntegerValue(context).ToChecked();
379   Local<Function> wrap =
380       Function::New(context, TimerFunctionCall, fn, length).ToLocalChecked();
381   args.GetReturnValue().Set(wrap);
382 }
383 
384 // Notify a custom PerformanceEntry to observers
Notify(const FunctionCallbackInfo<Value> & args)385 void Notify(const FunctionCallbackInfo<Value>& args) {
386   Environment* env = Environment::GetCurrent(args);
387   Utf8Value type(env->isolate(), args[0]);
388   Local<Value> entry = args[1];
389   PerformanceEntryType entry_type = ToPerformanceEntryTypeEnum(*type);
390   AliasedUint32Array& observers = env->performance_state()->observers;
391   if (entry_type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
392       observers[entry_type]) {
393     USE(env->performance_entry_callback()->
394       Call(env->context(), Undefined(env->isolate()), 1, &entry));
395   }
396 }
397 
398 // Return idle time of the event loop
LoopIdleTime(const FunctionCallbackInfo<Value> & args)399 void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
400   Environment* env = Environment::GetCurrent(args);
401   uint64_t idle_time = uv_metrics_idle_time(env->event_loop());
402   args.GetReturnValue().Set(1.0 * idle_time / 1e6);
403 }
404 
405 // Event Loop Timing Histogram
New(const FunctionCallbackInfo<Value> & args)406 void ELDHistogram::New(const FunctionCallbackInfo<Value>& args) {
407   Environment* env = Environment::GetCurrent(args);
408   CHECK(args.IsConstructCall());
409   int32_t resolution = args[0].As<Int32>()->Value();
410   CHECK_GT(resolution, 0);
411   new ELDHistogram(env, args.This(), resolution);
412 }
413 
Initialize(Environment * env,Local<Object> target)414 void ELDHistogram::Initialize(Environment* env, Local<Object> target) {
415   Local<FunctionTemplate> tmpl = env->NewFunctionTemplate(New);
416   tmpl->Inherit(IntervalHistogram::GetConstructorTemplate(env));
417   tmpl->InstanceTemplate()->SetInternalFieldCount(
418       ELDHistogram::kInternalFieldCount);
419   env->SetConstructorFunction(target, "ELDHistogram", tmpl);
420 }
421 
ELDHistogram(Environment * env,Local<Object> wrap,int32_t interval)422 ELDHistogram::ELDHistogram(
423     Environment* env,
424     Local<Object> wrap,
425     int32_t interval)
426     : IntervalHistogram(
427           env,
428           wrap,
429           AsyncWrap::PROVIDER_ELDHISTOGRAM,
430           interval, 1, 3.6e12, 3) {}
431 
OnInterval()432 void ELDHistogram::OnInterval() {
433   uint64_t delta = histogram()->RecordDelta();
434   TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
435                   "delay", delta);
436   TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
437                  "min", histogram()->Min());
438   TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
439                  "max", histogram()->Max());
440   TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
441                  "mean", histogram()->Mean());
442   TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
443                  "stddev", histogram()->Stddev());
444 }
445 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)446 void Initialize(Local<Object> target,
447                 Local<Value> unused,
448                 Local<Context> context,
449                 void* priv) {
450   Environment* env = Environment::GetCurrent(context);
451   Isolate* isolate = env->isolate();
452   PerformanceState* state = env->performance_state();
453 
454   target->Set(context,
455               FIXED_ONE_BYTE_STRING(isolate, "observerCounts"),
456               state->observers.GetJSArray()).Check();
457   target->Set(context,
458               FIXED_ONE_BYTE_STRING(isolate, "milestones"),
459               state->milestones.GetJSArray()).Check();
460 
461   Local<String> performanceEntryString =
462       FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry");
463 
464   Local<FunctionTemplate> pe = FunctionTemplate::New(isolate);
465   pe->SetClassName(performanceEntryString);
466   Local<Function> fn = pe->GetFunction(context).ToLocalChecked();
467   target->Set(context, performanceEntryString, fn).Check();
468   env->set_performance_entry_template(fn);
469 
470   env->SetMethod(target, "clearMark", ClearMark);
471   env->SetMethod(target, "mark", Mark);
472   env->SetMethod(target, "measure", Measure);
473   env->SetMethod(target, "markMilestone", MarkMilestone);
474   env->SetMethod(target, "setupObservers", SetupPerformanceObservers);
475   env->SetMethod(target, "timerify", Timerify);
476   env->SetMethod(target,
477                  "installGarbageCollectionTracking",
478                  InstallGarbageCollectionTracking);
479   env->SetMethod(target,
480                  "removeGarbageCollectionTracking",
481                  RemoveGarbageCollectionTracking);
482   env->SetMethod(target, "notify", Notify);
483   env->SetMethod(target, "loopIdleTime", LoopIdleTime);
484 
485   Local<Object> constants = Object::New(isolate);
486 
487   NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MAJOR);
488   NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MINOR);
489   NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_INCREMENTAL);
490   NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_WEAKCB);
491 
492   NODE_DEFINE_CONSTANT(
493     constants, NODE_PERFORMANCE_GC_FLAGS_NO);
494   NODE_DEFINE_CONSTANT(
495     constants, NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED);
496   NODE_DEFINE_CONSTANT(
497     constants, NODE_PERFORMANCE_GC_FLAGS_FORCED);
498   NODE_DEFINE_CONSTANT(
499     constants, NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING);
500   NODE_DEFINE_CONSTANT(
501     constants, NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE);
502   NODE_DEFINE_CONSTANT(
503     constants, NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY);
504   NODE_DEFINE_CONSTANT(
505     constants, NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE);
506 
507 #define V(name, _)                                                            \
508   NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_ENTRY_TYPE_##name);
509   NODE_PERFORMANCE_ENTRY_TYPES(V)
510 #undef V
511 
512 #define V(name, _)                                                            \
513   NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_MILESTONE_##name);
514   NODE_PERFORMANCE_MILESTONES(V)
515 #undef V
516 
517   PropertyAttribute attr =
518       static_cast<PropertyAttribute>(ReadOnly | DontDelete);
519 
520   target->DefineOwnProperty(context,
521                             FIXED_ONE_BYTE_STRING(isolate, "timeOrigin"),
522                             Number::New(isolate, timeOrigin / 1e6),
523                             attr).ToChecked();
524 
525   target->DefineOwnProperty(
526       context,
527       FIXED_ONE_BYTE_STRING(isolate, "timeOriginTimestamp"),
528       Number::New(isolate, timeOriginTimestamp / MICROS_PER_MILLIS),
529       attr).ToChecked();
530 
531   target->DefineOwnProperty(context,
532                             env->constants_string(),
533                             constants,
534                             attr).ToChecked();
535 
536   HistogramBase::Initialize(env, target);
537   ELDHistogram::Initialize(env, target);
538 }
539 
540 }  // namespace performance
541 }  // namespace node
542 
543 NODE_MODULE_CONTEXT_AWARE_INTERNAL(performance, node::performance::Initialize)
544