summaryrefslogtreecommitdiff
path: root/lib/internal/perf/timerify.js
blob: ad959aa42f0c755b5b4ad48c43e73c57a8947c45 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
'use strict';

const {
  FunctionPrototypeBind,
  ObjectDefineProperties,
  MathCeil,
  ReflectApply,
  ReflectConstruct,
} = primordials;

const { createPerformanceNodeEntry } = require('internal/perf/performance_entry');
const { now } = require('internal/perf/utils');

const {
  validateFunction,
  validateObject,
} = require('internal/validators');

const {
  isHistogram,
} = require('internal/histogram');

const {
  codes: {
    ERR_INVALID_ARG_TYPE,
  },
} = require('internal/errors');

const {
  enqueue,
} = require('internal/perf/observe');

const {
  kEmptyObject,
} = require('internal/util');

function processComplete(name, start, args, histogram) {
  const duration = now() - start;
  if (histogram !== undefined)
    histogram.record(MathCeil(duration * 1e6));
  const entry =
    createPerformanceNodeEntry(
      name,
      'function',
      start,
      duration,
      args);

  for (let n = 0; n < args.length; n++)
    entry[n] = args[n];

  enqueue(entry);
}

function timerify(fn, options = kEmptyObject) {
  validateFunction(fn, 'fn');

  validateObject(options, 'options');
  const {
    histogram,
  } = options;

  if (histogram !== undefined &&
      (!isHistogram(histogram) || typeof histogram.record !== 'function')) {
    throw new ERR_INVALID_ARG_TYPE(
      'options.histogram',
      'RecordableHistogram',
      histogram);
  }

  function timerified(...args) {
    const isConstructorCall = new.target !== undefined;
    const start = now();
    const result = isConstructorCall ?
      ReflectConstruct(fn, args, fn) :
      ReflectApply(fn, this, args);
    if (!isConstructorCall && typeof result?.finally === 'function') {
      return result.finally(
        FunctionPrototypeBind(
          processComplete,
          result,
          fn.name,
          start,
          args,
          histogram));
    }
    processComplete(fn.name, start, args, histogram);
    return result;
  }

  ObjectDefineProperties(timerified, {
    length: {
      __proto__: null,
      configurable: false,
      enumerable: true,
      value: fn.length,
    },
    name: {
      __proto__: null,
      configurable: false,
      enumerable: true,
      value: `timerified ${fn.name}`,
    },
  });

  return timerified;
}

module.exports = timerify;