Source: cpuWatcher.js

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2020 Campbell Crowley. All rights reserved.
// Author: Campbell Crowley (dev@campbellcrowley.com)
const SubModule = require('./subModule.js');

/**
 * @description Manages watching loop timings to help find causes of
 * inconsistent event loops.
 * @augments SubModule
 */
class CpuWatcher extends SubModule {
  /**
   * @description Manages watching loop timings to help find causes of
   * inconsistent event loops.
   */
  constructor() {
    super();
    /** @inheritdoc */
    this.myName = 'CpuWatcher';

    /**
     * @description If a single event loop takes longer than this time, log that
     * something went wrong.
     * @private
     * @type {number}
     * @default 1000000 nanoseconds
     * @constant
     */
    this._loopWarnThresh = 1000000;
    /**
     * @description Flag of whether to keep monitoring the event loop timing.
     * @private
     * @type {boolean}
     * @default
     */
    this._watchLoop = false;
    /**
     * @description The timestamp of the last event loop start.
     * @private
     * @type {number}
     * @default
     */
    this._lastLoopTime = 0;
    /**
     * @description How often to log periodic event loop statistics.
     * @private
     * @type {number}
     * @default 60 seconds
     * @constant
     */
    this._loopLogFrequency = 60000000;
    /**
     * @description Timestamp of the last time loop timings were logged.
     * @private
     * @type {number}
     * @default
     */
    this._loopLogTime = 0;
    /**
     * @description The amount of time the longest event loop took during the
     * last interval.
     * @private
     * @type {number}
     * @default
     */
    this._maxLoopTime = 0;
    /**
     * @description The sum of all event loop delays during the last interval
     * for averaging.
     * @private
     * @type {bigint}
     * @default
     */
    this._totalLoopTime = BigInt(0);
    /**
     * @description The number of event loops that took place during the last
     * interval.
     * @private
     * @type {bigint}
     * @default
     */
    this._numLoops = BigInt(0);

    this._queueLoopCheck = this._queueLoopCheck.bind(this);
  }

  /** @inheritdoc */
  initialize() {
    this._watchLoop = true;
    setImmediate(this._queueLoopCheck);
  }
  /** @inheritdoc */
  shutdown() {
    this._watchLoop = false;
  }

  /**
   * @description Time how long the next event loop will take. Calls itself
   * every event loop until {@see _watchLoop} is set to false.
   * @private
   */
  _queueLoopCheck() {
    const now = process.hrtime.bigint();
    if (this._lastLoopTime != 0) {
      const delta = now - this._lastLoopTime;
      if (delta > this._loopWarnThresh) {
        this.warn(`Previous event loop took: ${delta} nano seconds!`);
      }
      this._maxLoopTime =
          (delta > this._maxLoopTime && this._maxLoopTime) || delta;
      this._totalLoopTime += delta;
      this._numLoops++;
    } else {
      this._loopLogTime = now;
    }
    if (now - this._loopLogTime > this._loopLogFrequency) {
      this.debug(
          'Loop Timings: Max: ' + this._maxLoopTime + ' Avg: ' +
          (this._totalLoopTime / this._numLoops) + ' Sum: ' +
          this._totalLoopTime + ' Duration: ' + this._loopLogFrequency);

      this._maxLoopTime = 0;
      this._totalLoopTime = BigInt(0);
      this._numLoops = BigInt(0);
      this._loopLogTime = now;
    }
    if (!this._watchLoop) return;
    this._lastLoopTime = now;
    setImmediate(this._queueLoopCheck);
  }
}
module.exports = new CpuWatcher();