GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_perf.cc Lines: 10 213 4.7 %
Date: 2019-02-01 22:03:38 Branches: 5 122 4.1 %

Line Branch Exec Source
1
#include "node_internals.h"
2
#include "node_perf.h"
3
4
#ifdef __POSIX__
5
#include <sys/time.h>  // gettimeofday
6
#endif
7
8
namespace node {
9
namespace performance {
10
11
using v8::Array;
12
using v8::Context;
13
using v8::DontDelete;
14
using v8::Function;
15
using v8::FunctionCallbackInfo;
16
using v8::FunctionTemplate;
17
using v8::GCCallbackFlags;
18
using v8::GCType;
19
using v8::HandleScope;
20
using v8::Integer;
21
using v8::Isolate;
22
using v8::Local;
23
using v8::MaybeLocal;
24
using v8::Name;
25
using v8::NewStringType;
26
using v8::Number;
27
using v8::Object;
28
using v8::PropertyAttribute;
29
using v8::ReadOnly;
30
using v8::String;
31
using v8::Uint32Array;
32
using v8::Value;
33
34
// Microseconds in a second, as a float.
35
#define MICROS_PER_SEC 1e6
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
164
const uint64_t timeOrigin = PERFORMANCE_NOW();
41
// https://w3c.github.io/hr-time/#dfn-time-origin-timestamp
42
164
const double timeOriginTimestamp = GetCurrentTimeInMicroseconds();
43
uint64_t performance_node_start;
44
uint64_t performance_v8_start;
45
46
974
void performance_state::Mark(enum PerformanceMilestone milestone,
47
                             uint64_t ts) {
48
974
  this->milestones[milestone] = ts;
49

974
  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
50
      TRACING_CATEGORY_NODE1(bootstrap),
51
      GetPerformanceMilestoneName(milestone),
52
      TRACE_EVENT_SCOPE_THREAD, ts / 1000);
53
974
}
54
55
164
double GetCurrentTimeInMicroseconds() {
56
#ifdef _WIN32
57
// The difference between the Unix Epoch and the Windows Epoch in 100-ns ticks.
58
#define TICKS_TO_UNIX_EPOCH 116444736000000000LL
59
  FILETIME ft;
60
  GetSystemTimeAsFileTime(&ft);
61
  uint64_t filetime_int = static_cast<uint64_t>(ft.dwHighDateTime) << 32 |
62
                          ft.dwLowDateTime;
63
  // FILETIME is measured in terms of 100 ns. Convert that to 1 us (1000 ns).
64
  return (filetime_int - TICKS_TO_UNIX_EPOCH) / 10.;
65
#else
66
  struct timeval tp;
67
164
  gettimeofday(&tp, nullptr);
68
164
  return MICROS_PER_SEC * tp.tv_sec + tp.tv_usec;
69
#endif
70
}
71
72
// Initialize the performance entry object properties
73
inline void InitObject(const PerformanceEntry& entry, Local<Object> obj) {
74
  Environment* env = entry.env();
75
  Isolate* isolate = env->isolate();
76
  Local<Context> context = env->context();
77
  PropertyAttribute attr =
78
      static_cast<PropertyAttribute>(ReadOnly | DontDelete);
79
  obj->DefineOwnProperty(context,
80
                         env->name_string(),
81
                         String::NewFromUtf8(isolate,
82
                                             entry.name().c_str(),
83
                                             NewStringType::kNormal)
84
                             .ToLocalChecked(),
85
                         attr)
86
      .FromJust();
87
  obj->DefineOwnProperty(context,
88
                         env->entry_type_string(),
89
                         String::NewFromUtf8(isolate,
90
                                             entry.type().c_str(),
91
                                             NewStringType::kNormal)
92
                             .ToLocalChecked(),
93
                         attr)
94
      .FromJust();
95
  obj->DefineOwnProperty(context,
96
                         env->start_time_string(),
97
                         Number::New(isolate, entry.startTime()),
98
                         attr).FromJust();
99
  obj->DefineOwnProperty(context,
100
                         env->duration_string(),
101
                         Number::New(isolate, entry.duration()),
102
                         attr).FromJust();
103
}
104
105
// Create a new PerformanceEntry object
106
MaybeLocal<Object> PerformanceEntry::ToObject() const {
107
  Local<Object> obj;
108
  if (!env_->performance_entry_template()
109
           ->NewInstance(env_->context())
110
           .ToLocal(&obj)) {
111
    return MaybeLocal<Object>();
112
  }
113
  InitObject(*this, obj);
114
  return obj;
115
}
116
117
// Allow creating a PerformanceEntry object from JavaScript
118
void PerformanceEntry::New(const FunctionCallbackInfo<Value>& args) {
119
  Environment* env = Environment::GetCurrent(args);
120
  Isolate* isolate = env->isolate();
121
  Utf8Value name(isolate, args[0]);
122
  Utf8Value type(isolate, args[1]);
123
  uint64_t now = PERFORMANCE_NOW();
124
  PerformanceEntry entry(env, *name, *type, now, now);
125
  Local<Object> obj = args.This();
126
  InitObject(entry, obj);
127
  PerformanceEntry::Notify(env, entry.kind(), obj);
128
}
129
130
// Pass the PerformanceEntry object to the PerformanceObservers
131
void PerformanceEntry::Notify(Environment* env,
132
                              PerformanceEntryType type,
133
                              Local<Value> object) {
134
  Context::Scope scope(env->context());
135
  AliasedBuffer<uint32_t, Uint32Array>& observers =
136
      env->performance_state()->observers;
137
  if (type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
138
      observers[type]) {
139
    node::MakeCallback(env->isolate(),
140
                       object.As<Object>(),
141
                       env->performance_entry_callback(),
142
                       1, &object,
143
                       node::async_context{0, 0});
144
  }
145
}
146
147
// Create a User Timing Mark
148
void Mark(const FunctionCallbackInfo<Value>& args) {
149
  Environment* env = Environment::GetCurrent(args);
150
  HandleScope scope(env->isolate());
151
  Utf8Value name(env->isolate(), args[0]);
152
  uint64_t now = PERFORMANCE_NOW();
153
  auto marks = env->performance_marks();
154
  (*marks)[*name] = now;
155
156
  TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP(
157
      TRACING_CATEGORY_NODE2(perf, usertiming),
158
      *name, now / 1000);
159
160
  PerformanceEntry entry(env, *name, "mark", now, now);
161
  Local<Object> obj;
162
  if (!entry.ToObject().ToLocal(&obj)) return;
163
  PerformanceEntry::Notify(env, entry.kind(), obj);
164
  args.GetReturnValue().Set(obj);
165
}
166
167
void ClearMark(const FunctionCallbackInfo<Value>& args) {
168
  Environment* env = Environment::GetCurrent(args);
169
  auto marks = env->performance_marks();
170
171
  if (args.Length() == 0) {
172
    marks->clear();
173
  } else {
174
    Utf8Value name(env->isolate(), args[0]);
175
    marks->erase(*name);
176
  }
177
}
178
179
inline uint64_t GetPerformanceMark(Environment* env, std::string name) {
180
  auto marks = env->performance_marks();
181
  auto res = marks->find(name);
182
  return res != marks->end() ? res->second : 0;
183
}
184
185
// Create a User Timing Measure. A Measure is a PerformanceEntry that
186
// measures the duration between two distinct user timing marks
187
void Measure(const FunctionCallbackInfo<Value>& args) {
188
  Environment* env = Environment::GetCurrent(args);
189
  HandleScope scope(env->isolate());
190
  Utf8Value name(env->isolate(), args[0]);
191
  Utf8Value startMark(env->isolate(), args[1]);
192
  Utf8Value endMark(env->isolate(), args[2]);
193
194
  AliasedBuffer<double, v8::Float64Array>& milestones =
195
      env->performance_state()->milestones;
196
197
  uint64_t startTimestamp = timeOrigin;
198
  uint64_t start = GetPerformanceMark(env, *startMark);
199
  if (start != 0) {
200
    startTimestamp = start;
201
  } else {
202
    PerformanceMilestone milestone = ToPerformanceMilestoneEnum(*startMark);
203
    if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
204
      startTimestamp = milestones[milestone];
205
  }
206
207
  uint64_t endTimestamp = GetPerformanceMark(env, *endMark);
208
  if (endTimestamp == 0) {
209
    PerformanceMilestone milestone = ToPerformanceMilestoneEnum(*endMark);
210
    if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
211
      endTimestamp = milestones[milestone];
212
  }
213
214
  if (endTimestamp < startTimestamp)
215
    endTimestamp = startTimestamp;
216
217
  TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
218
      TRACING_CATEGORY_NODE2(perf, usertiming),
219
      *name, *name, startTimestamp / 1000);
220
  TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
221
      TRACING_CATEGORY_NODE2(perf, usertiming),
222
      *name, *name, endTimestamp / 1000);
223
224
  PerformanceEntry entry(env, *name, "measure", startTimestamp, endTimestamp);
225
  Local<Object> obj;
226
  if (!entry.ToObject().ToLocal(&obj)) return;
227
  PerformanceEntry::Notify(env, entry.kind(), obj);
228
  args.GetReturnValue().Set(obj);
229
}
230
231
// Allows specific Node.js lifecycle milestones to be set from JavaScript
232
void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
233
  Environment* env = Environment::GetCurrent(args);
234
  Local<Context> context = env->context();
235
  PerformanceMilestone milestone =
236
      static_cast<PerformanceMilestone>(
237
          args[0]->Int32Value(context).ToChecked());
238
  if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
239
    env->performance_state()->Mark(milestone);
240
}
241
242
243
void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
244
  Environment* env = Environment::GetCurrent(args);
245
  CHECK(args[0]->IsFunction());
246
  env->set_performance_entry_callback(args[0].As<Function>());
247
}
248
249
// Creates a GC Performance Entry and passes it to observers
250
void PerformanceGCCallback(Environment* env, void* ptr) {
251
  std::unique_ptr<GCPerformanceEntry> entry{
252
      static_cast<GCPerformanceEntry*>(ptr)};
253
  HandleScope scope(env->isolate());
254
  Local<Context> context = env->context();
255
256
  AliasedBuffer<uint32_t, Uint32Array>& observers =
257
      env->performance_state()->observers;
258
  if (observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) {
259
    Local<Object> obj;
260
    if (!entry->ToObject().ToLocal(&obj)) return;
261
    PropertyAttribute attr =
262
        static_cast<PropertyAttribute>(ReadOnly | DontDelete);
263
    obj->DefineOwnProperty(context,
264
                           env->kind_string(),
265
                           Integer::New(env->isolate(), entry->gckind()),
266
                           attr).FromJust();
267
    PerformanceEntry::Notify(env, entry->kind(), obj);
268
  }
269
}
270
271
// Marks the start of a GC cycle
272
void MarkGarbageCollectionStart(Isolate* isolate,
273
                                GCType type,
274
                                GCCallbackFlags flags,
275
                                void* data) {
276
  Environment* env = static_cast<Environment*>(data);
277
  env->performance_state()->performance_last_gc_start_mark = PERFORMANCE_NOW();
278
}
279
280
// Marks the end of a GC cycle
281
void MarkGarbageCollectionEnd(Isolate* isolate,
282
                              GCType type,
283
                              GCCallbackFlags flags,
284
                              void* data) {
285
  Environment* env = static_cast<Environment*>(data);
286
  performance_state* state = env->performance_state();
287
  // If no one is listening to gc performance entries, do not create them.
288
  if (!state->observers[NODE_PERFORMANCE_ENTRY_TYPE_GC])
289
    return;
290
  GCPerformanceEntry* entry =
291
      new GCPerformanceEntry(env,
292
                             static_cast<PerformanceGCKind>(type),
293
                             state->performance_last_gc_start_mark,
294
                             PERFORMANCE_NOW());
295
  env->SetUnrefImmediate(PerformanceGCCallback,
296
                         entry);
297
}
298
299
300
inline void SetupGarbageCollectionTracking(Environment* env) {
301
  env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart,
302
                                        static_cast<void*>(env));
303
  env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd,
304
                                        static_cast<void*>(env));
305
  env->AddCleanupHook([](void* data) {
306
    Environment* env = static_cast<Environment*>(data);
307
    env->isolate()->RemoveGCPrologueCallback(MarkGarbageCollectionStart, data);
308
    env->isolate()->RemoveGCEpilogueCallback(MarkGarbageCollectionEnd, data);
309
  }, env);
310
}
311
312
// Gets the name of a function
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.
327
void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
328
  Isolate* isolate = args.GetIsolate();
329
  HandleScope scope(isolate);
330
  Environment* env = Environment::GetCurrent(isolate);
331
  CHECK_NOT_NULL(env);
332
  Local<Context> context = env->context();
333
  Local<Function> fn = args.Data().As<Function>();
334
  size_t count = args.Length();
335
  size_t idx;
336
  SlicedArguments call_args(args);
337
  Utf8Value name(isolate, GetName(fn));
338
  bool is_construct_call = args.IsConstructCall();
339
340
  uint64_t start = PERFORMANCE_NOW();
341
  TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
342
      TRACING_CATEGORY_NODE2(perf, timerify),
343
      *name, *name, start / 1000);
344
  v8::MaybeLocal<Value> ret;
345
346
  if (is_construct_call) {
347
    ret = fn->NewInstance(context, call_args.length(), call_args.out())
348
        .FromMaybe(Local<Object>());
349
  } else {
350
    ret = fn->Call(context, args.This(), call_args.length(), call_args.out());
351
  }
352
353
  uint64_t end = PERFORMANCE_NOW();
354
  TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
355
      TRACING_CATEGORY_NODE2(perf, timerify),
356
      *name, *name, end / 1000);
357
358
  if (ret.IsEmpty())
359
    return;
360
  args.GetReturnValue().Set(ret.ToLocalChecked());
361
362
  AliasedBuffer<uint32_t, Uint32Array>& observers =
363
      env->performance_state()->observers;
364
  if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION])
365
    return;
366
367
  PerformanceEntry entry(env, *name, "function", start, end);
368
  Local<Object> obj;
369
  if (!entry.ToObject().ToLocal(&obj)) return;
370
  for (idx = 0; idx < count; idx++)
371
    obj->Set(context, idx, args[idx]).FromJust();
372
  PerformanceEntry::Notify(env, entry.kind(), obj);
373
}
374
375
// Wraps a Function in a TimerFunctionCall
376
void Timerify(const FunctionCallbackInfo<Value>& args) {
377
  Environment* env = Environment::GetCurrent(args);
378
  Local<Context> context = env->context();
379
  CHECK(args[0]->IsFunction());
380
  CHECK(args[1]->IsNumber());
381
  Local<Function> fn = args[0].As<Function>();
382
  int length = args[1]->IntegerValue(context).ToChecked();
383
  Local<Function> wrap =
384
      Function::New(context, TimerFunctionCall, fn, length).ToLocalChecked();
385
  args.GetReturnValue().Set(wrap);
386
}
387
388
389
void Initialize(Local<Object> target,
390
                Local<Value> unused,
391
                Local<Context> context,
392
                void* priv) {
393
  Environment* env = Environment::GetCurrent(context);
394
  Isolate* isolate = env->isolate();
395
  performance_state* state = env->performance_state();
396
397
  target->Set(context,
398
              FIXED_ONE_BYTE_STRING(isolate, "observerCounts"),
399
              state->observers.GetJSArray()).FromJust();
400
  target->Set(context,
401
              FIXED_ONE_BYTE_STRING(isolate, "milestones"),
402
              state->milestones.GetJSArray()).FromJust();
403
404
  Local<String> performanceEntryString =
405
      FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry");
406
407
  Local<FunctionTemplate> pe = FunctionTemplate::New(isolate);
408
  pe->SetClassName(performanceEntryString);
409
  Local<Function> fn = pe->GetFunction(context).ToLocalChecked();
410
  target->Set(context, performanceEntryString, fn).FromJust();
411
  env->set_performance_entry_template(fn);
412
413
  env->SetMethod(target, "clearMark", ClearMark);
414
  env->SetMethod(target, "mark", Mark);
415
  env->SetMethod(target, "measure", Measure);
416
  env->SetMethod(target, "markMilestone", MarkMilestone);
417
  env->SetMethod(target, "setupObservers", SetupPerformanceObservers);
418
  env->SetMethod(target, "timerify", Timerify);
419
420
  Local<Object> constants = Object::New(isolate);
421
422
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MAJOR);
423
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MINOR);
424
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_INCREMENTAL);
425
  NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_WEAKCB);
426
427
#define V(name, _)                                                            \
428
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_ENTRY_TYPE_##name);
429
  NODE_PERFORMANCE_ENTRY_TYPES(V)
430
#undef V
431
432
#define V(name, _)                                                            \
433
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_MILESTONE_##name);
434
  NODE_PERFORMANCE_MILESTONES(V)
435
#undef V
436
437
  PropertyAttribute attr =
438
      static_cast<PropertyAttribute>(ReadOnly | DontDelete);
439
440
  target->DefineOwnProperty(context,
441
                            FIXED_ONE_BYTE_STRING(isolate, "timeOrigin"),
442
                            Number::New(isolate, timeOrigin / 1e6),
443
                            attr).ToChecked();
444
445
  target->DefineOwnProperty(
446
      context,
447
      FIXED_ONE_BYTE_STRING(isolate, "timeOriginTimestamp"),
448
      Number::New(isolate, timeOriginTimestamp / MICROS_PER_MILLIS),
449
      attr).ToChecked();
450
451
  target->DefineOwnProperty(context,
452
                            env->constants_string(),
453
                            constants,
454
                            attr).ToChecked();
455
456
  SetupGarbageCollectionTracking(env);
457
}
458
459
}  // namespace performance
460
}  // namespace node
461
462

656
NODE_MODULE_CONTEXT_AWARE_INTERNAL(performance, node::performance::Initialize)