GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_perf.cc Lines: 166 184 90.2 %
Date: 2021-09-08 04:14:13 Branches: 35 54 64.8 %

Line Branch Exec Source
1
#include "node_perf.h"
2
#include "aliased_buffer.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
14
namespace node {
15
namespace performance {
16
17
using v8::Context;
18
using v8::DontDelete;
19
using v8::Function;
20
using v8::FunctionCallbackInfo;
21
using v8::FunctionTemplate;
22
using v8::GCCallbackFlags;
23
using v8::GCType;
24
using v8::Int32;
25
using v8::Integer;
26
using v8::Isolate;
27
using v8::Local;
28
using v8::MaybeLocal;
29
using v8::Number;
30
using v8::Object;
31
using v8::PropertyAttribute;
32
using v8::ReadOnly;
33
using v8::String;
34
using 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
40
const uint64_t timeOrigin = PERFORMANCE_NOW();
41
// https://w3c.github.io/hr-time/#dfn-time-origin-timestamp
42
const double timeOriginTimestamp = GetCurrentTimeInMicroseconds();
43
uint64_t performance_v8_start;
44
45
5442
PerformanceState::PerformanceState(Isolate* isolate,
46
5442
                                   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
5442
                 root,
54
                 MAYBE_FIELD_PTR(info, milestones)),
55
      observers(isolate,
56
                offsetof(performance_state_internal, observers),
57
                NODE_PERFORMANCE_ENTRY_TYPE_INVALID,
58
5442
                root,
59

5442
                MAYBE_FIELD_PTR(info, observers)) {
60
5442
  if (info == nullptr) {
61
4228
    for (size_t i = 0; i < milestones.Length(); i++) milestones[i] = -1.;
62
  }
63
5442
}
64
65
6
PerformanceState::SerializeInfo PerformanceState::Serialize(
66
    v8::Local<v8::Context> context, v8::SnapshotCreator* creator) {
67
6
  SerializeInfo info{root.Serialize(context, creator),
68
6
                     milestones.Serialize(context, creator),
69
6
                     observers.Serialize(context, creator)};
70
6
  return info;
71
}
72
73
4838
void PerformanceState::Deserialize(v8::Local<v8::Context> context) {
74
4838
  root.Deserialize(context);
75
  // This is just done to set up the pointers, we will actually reset
76
  // all the milestones after deserialization.
77
4838
  milestones.Deserialize(context);
78
4838
  observers.Deserialize(context);
79
4838
}
80
81
6
std::ostream& operator<<(std::ostream& o,
82
                         const PerformanceState::SerializeInfo& i) {
83
  o << "{\n"
84
6
    << "  " << i.root << ",  // root\n"
85
6
    << "  " << i.milestones << ",  // milestones\n"
86
6
    << "  " << i.observers << ",  // observers\n"
87
6
    << "}";
88
6
  return o;
89
}
90
91
31762
void PerformanceState::Mark(PerformanceMilestone milestone, uint64_t ts) {
92
31762
  this->milestones[milestone] = static_cast<double>(ts);
93

36615
  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
94
      TRACING_CATEGORY_NODE1(bootstrap),
95
      GetPerformanceMilestoneName(milestone),
96
      TRACE_EVENT_SCOPE_THREAD, ts / 1000);
97
31762
}
98
99
// Allows specific Node.js lifecycle milestones to be set from JavaScript
100
void 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
108
604
void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
109
604
  Environment* env = Environment::GetCurrent(args);
110
604
  CHECK(args[0]->IsFunction());
111
1208
  env->set_performance_entry_callback(args[0].As<Function>());
112
604
}
113
114
// Marks the start of a GC cycle
115
1
void MarkGarbageCollectionStart(
116
    Isolate* isolate,
117
    GCType type,
118
    GCCallbackFlags flags,
119
    void* data) {
120
1
  Environment* env = static_cast<Environment*>(data);
121
1
  env->performance_state()->performance_last_gc_start_mark = PERFORMANCE_NOW();
122
1
}
123
124
1
MaybeLocal<Object> GCPerformanceEntryTraits::GetDetails(
125
    Environment* env,
126
    const GCPerformanceEntry& entry) {
127
1
  Local<Object> obj = Object::New(env->isolate());
128
129
2
  if (!obj->Set(
130
          env->context(),
131
          env->kind_string(),
132
          Integer::NewFromUnsigned(
133
              env->isolate(),
134
4
              entry.details.kind)).IsJust()) {
135
    return MaybeLocal<Object>();
136
  }
137
138
2
  if (!obj->Set(
139
          env->context(),
140
          env->flags_string(),
141
          Integer::NewFromUnsigned(
142
              env->isolate(),
143
4
              entry.details.flags)).IsJust()) {
144
    return MaybeLocal<Object>();
145
  }
146
147
1
  return obj;
148
}
149
150
// Marks the end of a GC cycle
151
1
void MarkGarbageCollectionEnd(
152
    Isolate* isolate,
153
    GCType type,
154
    GCCallbackFlags flags,
155
    void* data) {
156
1
  Environment* env = static_cast<Environment*>(data);
157
1
  PerformanceState* state = env->performance_state();
158
  // If no one is listening to gc performance entries, do not create them.
159
1
  if (LIKELY(!state->observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]))
160
    return;
161
162
1
  double start_time = state->performance_last_gc_start_mark / 1e6;
163
1
  double duration = (PERFORMANCE_NOW() / 1e6) - start_time;
164
165
  std::unique_ptr<GCPerformanceEntry> entry =
166
      std::make_unique<GCPerformanceEntry>(
167
          "gc",
168
          start_time,
169
          duration,
170
1
          GCPerformanceEntry::Details(
171
            static_cast<PerformanceGCKind>(type),
172
1
            static_cast<PerformanceGCFlags>(flags)));
173
174
1
  env->SetImmediate([entry = std::move(entry)](Environment* env) {
175
1
    entry->Notify(env);
176
1
  }, CallbackFlags::kUnrefed);
177
}
178
179
4
void GarbageCollectionCleanupHook(void* data) {
180
4
  Environment* env = static_cast<Environment*>(data);
181
4
  env->isolate()->RemoveGCPrologueCallback(MarkGarbageCollectionStart, data);
182
4
  env->isolate()->RemoveGCEpilogueCallback(MarkGarbageCollectionEnd, data);
183
4
}
184
185
4
static void InstallGarbageCollectionTracking(
186
    const FunctionCallbackInfo<Value>& args) {
187
4
  Environment* env = Environment::GetCurrent(args);
188
189
4
  env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart,
190
                                        static_cast<void*>(env));
191
4
  env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd,
192
                                        static_cast<void*>(env));
193
4
  env->AddCleanupHook(GarbageCollectionCleanupHook, env);
194
4
}
195
196
3
static void RemoveGarbageCollectionTracking(
197
  const FunctionCallbackInfo<Value> &args) {
198
3
  Environment* env = Environment::GetCurrent(args);
199
200
3
  env->RemoveCleanupHook(GarbageCollectionCleanupHook, env);
201
3
  GarbageCollectionCleanupHook(env);
202
3
}
203
204
// Gets the name of a function
205
inline Local<Value> GetName(Local<Function> fn) {
206
  Local<Value> val = fn->GetDebugName();
207
  if (val.IsEmpty() || val->IsUndefined()) {
208
    Local<Value> boundFunction = fn->GetBoundFunction();
209
    if (!boundFunction.IsEmpty() && !boundFunction->IsUndefined()) {
210
      val = GetName(boundFunction.As<Function>());
211
    }
212
  }
213
  return val;
214
}
215
216
// Notify a custom PerformanceEntry to observers
217
void Notify(const FunctionCallbackInfo<Value>& args) {
218
  Environment* env = Environment::GetCurrent(args);
219
  Utf8Value type(env->isolate(), args[0]);
220
  Local<Value> entry = args[1];
221
  PerformanceEntryType entry_type = ToPerformanceEntryTypeEnum(*type);
222
  AliasedUint32Array& observers = env->performance_state()->observers;
223
  if (entry_type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
224
      observers[entry_type]) {
225
    USE(env->performance_entry_callback()->
226
      Call(env->context(), Undefined(env->isolate()), 1, &entry));
227
  }
228
}
229
230
// Return idle time of the event loop
231
50
void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
232
50
  Environment* env = Environment::GetCurrent(args);
233
50
  uint64_t idle_time = uv_metrics_idle_time(env->event_loop());
234
50
  args.GetReturnValue().Set(1.0 * idle_time / 1e6);
235
50
}
236
237
// Event Loop Timing Histogram
238
3
void ELDHistogram::New(const FunctionCallbackInfo<Value>& args) {
239
3
  Environment* env = Environment::GetCurrent(args);
240
3
  CHECK(args.IsConstructCall());
241
6
  int64_t resolution = args[0].As<Integer>()->Value();
242
3
  CHECK_GT(resolution, 0);
243
3
  new ELDHistogram(env, args.This(), resolution);
244
3
}
245
246
604
void ELDHistogram::Initialize(Environment* env, Local<Object> target) {
247
604
  Local<FunctionTemplate> tmpl = env->NewFunctionTemplate(New);
248
604
  tmpl->Inherit(IntervalHistogram::GetConstructorTemplate(env));
249
1208
  tmpl->InstanceTemplate()->SetInternalFieldCount(
250
      ELDHistogram::kInternalFieldCount);
251
604
  env->SetConstructorFunction(target, "ELDHistogram", tmpl);
252
604
}
253
254
4844
void ELDHistogram::RegisterExternalReferences(
255
    ExternalReferenceRegistry* registry) {
256
4844
  registry->Register(New);
257
4844
  IntervalHistogram::RegisterExternalReferences(registry);
258
4844
}
259
260
3
ELDHistogram::ELDHistogram(
261
    Environment* env,
262
    Local<Object> wrap,
263
3
    int64_t interval)
264
    : IntervalHistogram(
265
          env,
266
          wrap,
267
          AsyncWrap::PROVIDER_ELDHISTOGRAM,
268
3
          interval, 1, 3.6e12, 3) {}
269
270
1759
void ELDHistogram::OnInterval() {
271
1759
  uint64_t delta = histogram()->RecordDelta();
272

1761
  TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
273
                  "delay", delta);
274

1761
  TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
275
                 "min", histogram()->Min());
276

1761
  TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
277
                 "max", histogram()->Max());
278

1761
  TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
279
                 "mean", histogram()->Mean());
280

1761
  TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
281
                 "stddev", histogram()->Stddev());
282
1759
}
283
284
6014
void GetTimeOrigin(const FunctionCallbackInfo<Value>& args) {
285
6014
  args.GetReturnValue().Set(Number::New(args.GetIsolate(), timeOrigin / 1e6));
286
6014
}
287
288
6014
void GetTimeOriginTimeStamp(const FunctionCallbackInfo<Value>& args) {
289
6014
  args.GetReturnValue().Set(
290
      Number::New(args.GetIsolate(), timeOriginTimestamp / MICROS_PER_MILLIS));
291
6014
}
292
293
604
void Initialize(Local<Object> target,
294
                Local<Value> unused,
295
                Local<Context> context,
296
                void* priv) {
297
604
  Environment* env = Environment::GetCurrent(context);
298
604
  Isolate* isolate = env->isolate();
299
604
  PerformanceState* state = env->performance_state();
300
301
604
  target->Set(context,
302
              FIXED_ONE_BYTE_STRING(isolate, "observerCounts"),
303
2416
              state->observers.GetJSArray()).Check();
304
604
  target->Set(context,
305
              FIXED_ONE_BYTE_STRING(isolate, "milestones"),
306
1812
              state->milestones.GetJSArray()).Check();
307
308
  Local<String> performanceEntryString =
309
604
      FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry");
310
311
604
  Local<FunctionTemplate> pe = FunctionTemplate::New(isolate);
312
604
  pe->SetClassName(performanceEntryString);
313
1208
  Local<Function> fn = pe->GetFunction(context).ToLocalChecked();
314
604
  target->Set(context, performanceEntryString, fn).Check();
315
604
  env->set_performance_entry_template(fn);
316
317
604
  env->SetMethod(target, "markMilestone", MarkMilestone);
318
604
  env->SetMethod(target, "setupObservers", SetupPerformanceObservers);
319
604
  env->SetMethod(target,
320
                 "installGarbageCollectionTracking",
321
                 InstallGarbageCollectionTracking);
322
604
  env->SetMethod(target,
323
                 "removeGarbageCollectionTracking",
324
                 RemoveGarbageCollectionTracking);
325
604
  env->SetMethod(target, "notify", Notify);
326
604
  env->SetMethod(target, "loopIdleTime", LoopIdleTime);
327
604
  env->SetMethod(target, "getTimeOrigin", GetTimeOrigin);
328
604
  env->SetMethod(target, "getTimeOriginTimestamp", GetTimeOriginTimeStamp);
329
330
604
  Local<Object> constants = Object::New(isolate);
331
332
1812
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MAJOR);
333
1812
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MINOR);
334
1812
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_INCREMENTAL);
335
1812
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_WEAKCB);
336
337
1812
  NODE_DEFINE_CONSTANT(
338
    constants, NODE_PERFORMANCE_GC_FLAGS_NO);
339
1812
  NODE_DEFINE_CONSTANT(
340
    constants, NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED);
341
1812
  NODE_DEFINE_CONSTANT(
342
    constants, NODE_PERFORMANCE_GC_FLAGS_FORCED);
343
1812
  NODE_DEFINE_CONSTANT(
344
    constants, NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING);
345
1812
  NODE_DEFINE_CONSTANT(
346
    constants, NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE);
347
1812
  NODE_DEFINE_CONSTANT(
348
    constants, NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY);
349
1812
  NODE_DEFINE_CONSTANT(
350
    constants, NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE);
351
352
#define V(name, _)                                                            \
353
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_ENTRY_TYPE_##name);
354
4228
  NODE_PERFORMANCE_ENTRY_TYPES(V)
355
#undef V
356
357
#define V(name, _)                                                            \
358
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_MILESTONE_##name);
359
7248
  NODE_PERFORMANCE_MILESTONES(V)
360
#undef V
361
362
604
  PropertyAttribute attr =
363
      static_cast<PropertyAttribute>(ReadOnly | DontDelete);
364
365
604
  target->DefineOwnProperty(context,
366
                            env->constants_string(),
367
                            constants,
368
1208
                            attr).ToChecked();
369
370
604
  HistogramBase::Initialize(env, target);
371
604
  ELDHistogram::Initialize(env, target);
372
604
}
373
374
4844
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
375
4844
  registry->Register(MarkMilestone);
376
4844
  registry->Register(SetupPerformanceObservers);
377
4844
  registry->Register(InstallGarbageCollectionTracking);
378
4844
  registry->Register(RemoveGarbageCollectionTracking);
379
4844
  registry->Register(Notify);
380
4844
  registry->Register(LoopIdleTime);
381
4844
  registry->Register(GetTimeOrigin);
382
4844
  registry->Register(GetTimeOriginTimeStamp);
383
4844
  HistogramBase::RegisterExternalReferences(registry);
384
4844
  ELDHistogram::RegisterExternalReferences(registry);
385
4844
}
386
}  // namespace performance
387
}  // namespace node
388
389
4917
NODE_MODULE_CONTEXT_AWARE_INTERNAL(performance, node::performance::Initialize)
390
4844
NODE_MODULE_EXTERNAL_REFERENCE(performance,
391
                               node::performance::RegisterExternalReferences)