GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_perf.cc Lines: 165 183 90.2 %
Date: 2022-08-06 04:16:36 Branches: 34 52 65.4 %

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
6094
PerformanceState::PerformanceState(Isolate* isolate,
44
6094
                                   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
6094
                 root,
52
                 MAYBE_FIELD_PTR(info, milestones)),
53
      observers(isolate,
54
                offsetof(performance_state_internal, observers),
55
                NODE_PERFORMANCE_ENTRY_TYPE_INVALID,
56
6094
                root,
57

6094
                MAYBE_FIELD_PTR(info, observers)) {
58
6094
  if (info == nullptr) {
59
5467
    for (size_t i = 0; i < milestones.Length(); i++) milestones[i] = -1.;
60
  }
61
6094
}
62
63
6
PerformanceState::SerializeInfo PerformanceState::Serialize(
64
    v8::Local<v8::Context> context, v8::SnapshotCreator* creator) {
65
6
  SerializeInfo info{root.Serialize(context, creator),
66
6
                     milestones.Serialize(context, creator),
67
6
                     observers.Serialize(context, creator)};
68
6
  return info;
69
}
70
71
5313
void PerformanceState::Deserialize(v8::Local<v8::Context> context) {
72
5313
  root.Deserialize(context);
73
  // This is just done to set up the pointers, we will actually reset
74
  // all the milestones after deserialization.
75
5313
  milestones.Deserialize(context);
76
5313
  observers.Deserialize(context);
77
5313
}
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
35547
void PerformanceState::Mark(PerformanceMilestone milestone, uint64_t ts) {
90
35547
  this->milestones[milestone] = static_cast<double>(ts);
91

40877
  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
92
      TRACING_CATEGORY_NODE1(bootstrap),
93
      GetPerformanceMilestoneName(milestone),
94
      TRACE_EVENT_SCOPE_THREAD, ts / 1000);
95
35547
}
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
781
void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
107
781
  Environment* env = Environment::GetCurrent(args);
108
781
  CHECK(args[0]->IsFunction());
109
1562
  env->set_performance_entry_callback(args[0].As<Function>());
110
781
}
111
112
// Marks the start of a GC cycle
113
1
void MarkGarbageCollectionStart(
114
    Isolate* isolate,
115
    GCType type,
116
    GCCallbackFlags flags,
117
    void* data) {
118
1
  Environment* env = static_cast<Environment*>(data);
119
1
  env->performance_state()->performance_last_gc_start_mark = PERFORMANCE_NOW();
120
1
}
121
122
1
MaybeLocal<Object> GCPerformanceEntryTraits::GetDetails(
123
    Environment* env,
124
    const GCPerformanceEntry& entry) {
125
1
  Local<Object> obj = Object::New(env->isolate());
126
127
2
  if (!obj->Set(
128
          env->context(),
129
          env->kind_string(),
130
          Integer::NewFromUnsigned(
131
              env->isolate(),
132
4
              entry.details.kind)).IsJust()) {
133
    return MaybeLocal<Object>();
134
  }
135
136
2
  if (!obj->Set(
137
          env->context(),
138
          env->flags_string(),
139
          Integer::NewFromUnsigned(
140
              env->isolate(),
141
4
              entry.details.flags)).IsJust()) {
142
    return MaybeLocal<Object>();
143
  }
144
145
1
  return obj;
146
}
147
148
// Marks the end of a GC cycle
149
1
void MarkGarbageCollectionEnd(
150
    Isolate* isolate,
151
    GCType type,
152
    GCCallbackFlags flags,
153
    void* data) {
154
1
  Environment* env = static_cast<Environment*>(data);
155
1
  PerformanceState* state = env->performance_state();
156
  // If no one is listening to gc performance entries, do not create them.
157
1
  if (LIKELY(!state->observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]))
158
    return;
159
160
  double start_time =
161
1
      (state->performance_last_gc_start_mark - env->time_origin()) /
162
1
      NANOS_PER_MILLIS;
163
1
  double duration = (PERFORMANCE_NOW() / NANOS_PER_MILLIS) -
164
2
                    (state->performance_last_gc_start_mark / NANOS_PER_MILLIS);
165
166
  std::unique_ptr<GCPerformanceEntry> entry =
167
      std::make_unique<GCPerformanceEntry>(
168
          "gc",
169
          start_time,
170
          duration,
171
1
          GCPerformanceEntry::Details(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
60
void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
232
60
  Environment* env = Environment::GetCurrent(args);
233
60
  uint64_t idle_time = uv_metrics_idle_time(env->event_loop());
234
60
  args.GetReturnValue().Set(1.0 * idle_time / 1e6);
235
60
}
236
237
3
void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) {
238
3
  Environment* env = Environment::GetCurrent(args);
239
6
  int64_t interval = args[0].As<Integer>()->Value();
240
3
  CHECK_GT(interval, 0);
241
  BaseObjectPtr<IntervalHistogram> histogram =
242
1760
      IntervalHistogram::Create(env, interval, [](Histogram& histogram) {
243
1760
        uint64_t delta = histogram.RecordDelta();
244

1762
        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
245
                        "delay", delta);
246

1762
        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
247
                      "min", histogram.Min());
248

1762
        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
249
                      "max", histogram.Max());
250

1762
        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
251
                      "mean", histogram.Mean());
252

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