GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_perf.cc Lines: 170 190 89.5 %
Date: 2022-11-05 03:21:31 Branches: 36 56 64.3 %

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
// Nanoseconds in a millisecond, as a float.
39
#define NANOS_PER_MILLIS 1e6
40
41
uint64_t performance_v8_start;
42
43
6365
PerformanceState::PerformanceState(Isolate* isolate,
44
6365
                                   const PerformanceState::SerializeInfo* info)
45
    : root(isolate,
46
           sizeof(performance_state_internal),
47
           MAYBE_FIELD_PTR(info, root)),
48
      milestones(isolate,
49
                 offsetof(performance_state_internal, milestones),
50
                 NODE_PERFORMANCE_MILESTONE_INVALID,
51
6365
                 root,
52
                 MAYBE_FIELD_PTR(info, milestones)),
53
      observers(isolate,
54
                offsetof(performance_state_internal, observers),
55
                NODE_PERFORMANCE_ENTRY_TYPE_INVALID,
56
6365
                root,
57

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

42655
  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
92
      TRACING_CATEGORY_NODE1(bootstrap),
93
      GetPerformanceMilestoneName(milestone),
94
      TRACE_EVENT_SCOPE_THREAD, ts / 1000);
95
37057
}
96
97
// Allows specific Node.js lifecycle milestones to be set from JavaScript
98
void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
99
  Environment* env = Environment::GetCurrent(args);
100
  PerformanceMilestone milestone =
101
      static_cast<PerformanceMilestone>(args[0].As<Int32>()->Value());
102
  if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
103
    env->performance_state()->Mark(milestone);
104
}
105
106
785
void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
107
785
  Environment* env = Environment::GetCurrent(args);
108
785
  CHECK(args[0]->IsFunction());
109
1570
  env->set_performance_entry_callback(args[0].As<Function>());
110
785
}
111
112
// Marks the start of a GC cycle
113
2
void MarkGarbageCollectionStart(
114
    Isolate* isolate,
115
    GCType type,
116
    GCCallbackFlags flags,
117
    void* data) {
118
2
  Environment* env = static_cast<Environment*>(data);
119
  // Prevent gc callback from reentering with different type
120
  // See https://github.com/nodejs/node/issues/44046
121
2
  if (env->performance_state()->current_gc_type != 0) {
122
    return;
123
  }
124
2
  env->performance_state()->performance_last_gc_start_mark = PERFORMANCE_NOW();
125
2
  env->performance_state()->current_gc_type = type;
126
}
127
128
1
MaybeLocal<Object> GCPerformanceEntryTraits::GetDetails(
129
    Environment* env,
130
    const GCPerformanceEntry& entry) {
131
1
  Local<Object> obj = Object::New(env->isolate());
132
133
2
  if (!obj->Set(
134
          env->context(),
135
          env->kind_string(),
136
          Integer::NewFromUnsigned(
137
              env->isolate(),
138
4
              entry.details.kind)).IsJust()) {
139
    return MaybeLocal<Object>();
140
  }
141
142
2
  if (!obj->Set(
143
          env->context(),
144
          env->flags_string(),
145
          Integer::NewFromUnsigned(
146
              env->isolate(),
147
4
              entry.details.flags)).IsJust()) {
148
    return MaybeLocal<Object>();
149
  }
150
151
1
  return obj;
152
}
153
154
// Marks the end of a GC cycle
155
2
void MarkGarbageCollectionEnd(
156
    Isolate* isolate,
157
    GCType type,
158
    GCCallbackFlags flags,
159
    void* data) {
160
2
  Environment* env = static_cast<Environment*>(data);
161
2
  PerformanceState* state = env->performance_state();
162
2
  if (type != state->current_gc_type) {
163
    return;
164
  }
165
2
  env->performance_state()->current_gc_type = 0;
166
  // If no one is listening to gc performance entries, do not create them.
167
2
  if (LIKELY(!state->observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]))
168
    return;
169
170
  double start_time =
171
2
      (state->performance_last_gc_start_mark - env->time_origin()) /
172
2
      NANOS_PER_MILLIS;
173
2
  double duration = (PERFORMANCE_NOW() / NANOS_PER_MILLIS) -
174
4
                    (state->performance_last_gc_start_mark / NANOS_PER_MILLIS);
175
176
  std::unique_ptr<GCPerformanceEntry> entry =
177
      std::make_unique<GCPerformanceEntry>(
178
          "gc",
179
          start_time,
180
          duration,
181
2
          GCPerformanceEntry::Details(static_cast<PerformanceGCKind>(type),
182
2
                                      static_cast<PerformanceGCFlags>(flags)));
183
184
2
  env->SetImmediate([entry = std::move(entry)](Environment* env) {
185
1
    entry->Notify(env);
186
1
  }, CallbackFlags::kUnrefed);
187
}
188
189
4
void GarbageCollectionCleanupHook(void* data) {
190
4
  Environment* env = static_cast<Environment*>(data);
191
  // Reset current_gc_type to 0
192
4
  env->performance_state()->current_gc_type = 0;
193
4
  env->isolate()->RemoveGCPrologueCallback(MarkGarbageCollectionStart, data);
194
4
  env->isolate()->RemoveGCEpilogueCallback(MarkGarbageCollectionEnd, data);
195
4
}
196
197
4
static void InstallGarbageCollectionTracking(
198
    const FunctionCallbackInfo<Value>& args) {
199
4
  Environment* env = Environment::GetCurrent(args);
200
  // Reset current_gc_type to 0
201
4
  env->performance_state()->current_gc_type = 0;
202
4
  env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart,
203
                                        static_cast<void*>(env));
204
4
  env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd,
205
                                        static_cast<void*>(env));
206
4
  env->AddCleanupHook(GarbageCollectionCleanupHook, env);
207
4
}
208
209
3
static void RemoveGarbageCollectionTracking(
210
  const FunctionCallbackInfo<Value> &args) {
211
3
  Environment* env = Environment::GetCurrent(args);
212
213
3
  env->RemoveCleanupHook(GarbageCollectionCleanupHook, env);
214
3
  GarbageCollectionCleanupHook(env);
215
3
}
216
217
// Gets the name of a function
218
inline Local<Value> GetName(Local<Function> fn) {
219
  Local<Value> val = fn->GetDebugName();
220
  if (val.IsEmpty() || val->IsUndefined()) {
221
    Local<Value> boundFunction = fn->GetBoundFunction();
222
    if (!boundFunction.IsEmpty() && !boundFunction->IsUndefined()) {
223
      val = GetName(boundFunction.As<Function>());
224
    }
225
  }
226
  return val;
227
}
228
229
// Notify a custom PerformanceEntry to observers
230
void Notify(const FunctionCallbackInfo<Value>& args) {
231
  Environment* env = Environment::GetCurrent(args);
232
  Utf8Value type(env->isolate(), args[0]);
233
  Local<Value> entry = args[1];
234
  PerformanceEntryType entry_type = ToPerformanceEntryTypeEnum(*type);
235
  AliasedUint32Array& observers = env->performance_state()->observers;
236
  if (entry_type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
237
      observers[entry_type]) {
238
    USE(env->performance_entry_callback()->
239
      Call(env->context(), Undefined(env->isolate()), 1, &entry));
240
  }
241
}
242
243
// Return idle time of the event loop
244
59
void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
245
59
  Environment* env = Environment::GetCurrent(args);
246
59
  uint64_t idle_time = uv_metrics_idle_time(env->event_loop());
247
59
  args.GetReturnValue().Set(1.0 * idle_time / 1e6);
248
59
}
249
250
3
void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) {
251
3
  Environment* env = Environment::GetCurrent(args);
252
6
  int64_t interval = args[0].As<Integer>()->Value();
253
3
  CHECK_GT(interval, 0);
254
  BaseObjectPtr<IntervalHistogram> histogram =
255
1750
      IntervalHistogram::Create(env, interval, [](Histogram& histogram) {
256
1750
        uint64_t delta = histogram.RecordDelta();
257

1752
        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
258
                        "delay", delta);
259

1752
        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
260
                      "min", histogram.Min());
261

1752
        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
262
                      "max", histogram.Max());
263

1752
        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
264
                      "mean", histogram.Mean());
265

1752
        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
266
                      "stddev", histogram.Stddev());
267
6
      }, Histogram::Options { 1000 });
268
6
  args.GetReturnValue().Set(histogram->object());
269
3
}
270
271
7116
void GetTimeOrigin(const FunctionCallbackInfo<Value>& args) {
272
7116
  Environment* env = Environment::GetCurrent(args);
273
7116
  args.GetReturnValue().Set(
274
7116
      Number::New(args.GetIsolate(), env->time_origin() / 1e6));
275
7116
}
276
277
8
void GetTimeOriginTimeStamp(const FunctionCallbackInfo<Value>& args) {
278
8
  Environment* env = Environment::GetCurrent(args);
279
8
  args.GetReturnValue().Set(Number::New(
280
8
      args.GetIsolate(), env->time_origin_timestamp() / MICROS_PER_MILLIS));
281
8
}
282
283
6320
void MarkBootstrapComplete(const FunctionCallbackInfo<Value>& args) {
284
6320
  Environment* env = Environment::GetCurrent(args);
285
6320
  env->performance_state()->Mark(
286
      performance::NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE);
287
6320
}
288
289
785
void Initialize(Local<Object> target,
290
                Local<Value> unused,
291
                Local<Context> context,
292
                void* priv) {
293
785
  Environment* env = Environment::GetCurrent(context);
294
785
  Isolate* isolate = env->isolate();
295
785
  PerformanceState* state = env->performance_state();
296
297
785
  target->Set(context,
298
              FIXED_ONE_BYTE_STRING(isolate, "observerCounts"),
299
3140
              state->observers.GetJSArray()).Check();
300
785
  target->Set(context,
301
              FIXED_ONE_BYTE_STRING(isolate, "milestones"),
302
2355
              state->milestones.GetJSArray()).Check();
303
304
  Local<String> performanceEntryString =
305
785
      FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry");
306
307
785
  Local<FunctionTemplate> pe = FunctionTemplate::New(isolate);
308
785
  pe->SetClassName(performanceEntryString);
309
1570
  Local<Function> fn = pe->GetFunction(context).ToLocalChecked();
310
785
  target->Set(context, performanceEntryString, fn).Check();
311
785
  env->set_performance_entry_template(fn);
312
313
785
  SetMethod(context, target, "markMilestone", MarkMilestone);
314
785
  SetMethod(context, target, "setupObservers", SetupPerformanceObservers);
315
785
  SetMethod(context,
316
            target,
317
            "installGarbageCollectionTracking",
318
            InstallGarbageCollectionTracking);
319
785
  SetMethod(context,
320
            target,
321
            "removeGarbageCollectionTracking",
322
            RemoveGarbageCollectionTracking);
323
785
  SetMethod(context, target, "notify", Notify);
324
785
  SetMethod(context, target, "loopIdleTime", LoopIdleTime);
325
785
  SetMethod(context, target, "getTimeOrigin", GetTimeOrigin);
326
785
  SetMethod(context, target, "getTimeOriginTimestamp", GetTimeOriginTimeStamp);
327
785
  SetMethod(context, target, "createELDHistogram", CreateELDHistogram);
328
785
  SetMethod(context, target, "markBootstrapComplete", MarkBootstrapComplete);
329
330
785
  Local<Object> constants = Object::New(isolate);
331
332
2355
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MAJOR);
333
2355
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MINOR);
334
2355
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_INCREMENTAL);
335
2355
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_WEAKCB);
336
337
2355
  NODE_DEFINE_CONSTANT(
338
    constants, NODE_PERFORMANCE_GC_FLAGS_NO);
339
2355
  NODE_DEFINE_CONSTANT(
340
    constants, NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED);
341
2355
  NODE_DEFINE_CONSTANT(
342
    constants, NODE_PERFORMANCE_GC_FLAGS_FORCED);
343
2355
  NODE_DEFINE_CONSTANT(
344
    constants, NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING);
345
2355
  NODE_DEFINE_CONSTANT(
346
    constants, NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE);
347
2355
  NODE_DEFINE_CONSTANT(
348
    constants, NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY);
349
2355
  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
8635
  NODE_PERFORMANCE_ENTRY_TYPES(V)
355
#undef V
356
357
#define V(name, _)                                                            \
358
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_MILESTONE_##name);
359
9420
  NODE_PERFORMANCE_MILESTONES(V)
360
#undef V
361
362
785
  PropertyAttribute attr =
363
      static_cast<PropertyAttribute>(ReadOnly | DontDelete);
364
365
785
  target->DefineOwnProperty(context,
366
                            env->constants_string(),
367
                            constants,
368
1570
                            attr).ToChecked();
369
370
785
  HistogramBase::Initialize(env, target);
371
785
}
372
373
5588
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
374
5588
  registry->Register(MarkMilestone);
375
5588
  registry->Register(SetupPerformanceObservers);
376
5588
  registry->Register(InstallGarbageCollectionTracking);
377
5588
  registry->Register(RemoveGarbageCollectionTracking);
378
5588
  registry->Register(Notify);
379
5588
  registry->Register(LoopIdleTime);
380
5588
  registry->Register(GetTimeOrigin);
381
5588
  registry->Register(GetTimeOriginTimeStamp);
382
5588
  registry->Register(CreateELDHistogram);
383
5588
  registry->Register(MarkBootstrapComplete);
384
5588
  HistogramBase::RegisterExternalReferences(registry);
385
5588
  IntervalHistogram::RegisterExternalReferences(registry);
386
5588
}
387
}  // namespace performance
388
}  // namespace node
389
390
5659
NODE_MODULE_CONTEXT_AWARE_INTERNAL(performance, node::performance::Initialize)
391
5588
NODE_MODULE_EXTERNAL_REFERENCE(performance,
392
                               node::performance::RegisterExternalReferences)