perf_hooks: add warning when too many entries in the timeline
PR-URL: https://github.com/nodejs/node/pull/18087 Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
parent
6e312c5cc7
commit
6bcd31f2f4
@ -125,6 +125,20 @@ Creates a new `PerformanceMark` entry in the Performance Timeline. A
|
|||||||
`performanceEntry.duration` is always `0`. Performance marks are used
|
`performanceEntry.duration` is always `0`. Performance marks are used
|
||||||
to mark specific significant moments in the Performance Timeline.
|
to mark specific significant moments in the Performance Timeline.
|
||||||
|
|
||||||
|
### performance.maxEntries
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
Value: {number}
|
||||||
|
|
||||||
|
The maximum number of Performance Entry items that should be added to the
|
||||||
|
Performance Timeline. This limit is not strictly enforced, but a process
|
||||||
|
warning will be emitted if the number of entries in the timeline exceeds
|
||||||
|
this limit.
|
||||||
|
|
||||||
|
Defaults to 150.
|
||||||
|
|
||||||
### performance.measure(name, startMark, endMark)
|
### performance.measure(name, startMark, endMark)
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.5.0
|
added: v8.5.0
|
||||||
|
@ -53,6 +53,9 @@ const kClearEntry = Symbol('clear-entry');
|
|||||||
const kGetEntries = Symbol('get-entries');
|
const kGetEntries = Symbol('get-entries');
|
||||||
const kIndex = Symbol('index');
|
const kIndex = Symbol('index');
|
||||||
const kMarks = Symbol('marks');
|
const kMarks = Symbol('marks');
|
||||||
|
const kCount = Symbol('count');
|
||||||
|
const kMaxCount = Symbol('max-count');
|
||||||
|
const kDefaultMaxCount = 150;
|
||||||
|
|
||||||
observerCounts[NODE_PERFORMANCE_ENTRY_TYPE_MARK] = 1;
|
observerCounts[NODE_PERFORMANCE_ENTRY_TYPE_MARK] = 1;
|
||||||
observerCounts[NODE_PERFORMANCE_ENTRY_TYPE_MEASURE] = 1;
|
observerCounts[NODE_PERFORMANCE_ENTRY_TYPE_MEASURE] = 1;
|
||||||
@ -250,10 +253,17 @@ const nodeTiming = new PerformanceNodeTiming();
|
|||||||
// Maintains a list of entries as a linked list stored in insertion order.
|
// Maintains a list of entries as a linked list stored in insertion order.
|
||||||
class PerformanceObserverEntryList {
|
class PerformanceObserverEntryList {
|
||||||
constructor() {
|
constructor() {
|
||||||
Object.defineProperty(this, kEntries, {
|
Object.defineProperties(this, {
|
||||||
writable: true,
|
[kEntries]: {
|
||||||
enumerable: false,
|
writable: true,
|
||||||
value: {}
|
enumerable: false,
|
||||||
|
value: {}
|
||||||
|
},
|
||||||
|
[kCount]: {
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
});
|
});
|
||||||
L.init(this[kEntries]);
|
L.init(this[kEntries]);
|
||||||
}
|
}
|
||||||
@ -261,9 +271,14 @@ class PerformanceObserverEntryList {
|
|||||||
[kInsertEntry](entry) {
|
[kInsertEntry](entry) {
|
||||||
const item = { entry };
|
const item = { entry };
|
||||||
L.append(this[kEntries], item);
|
L.append(this[kEntries], item);
|
||||||
|
this[kCount]++;
|
||||||
this[kIndexEntry](item);
|
this[kIndexEntry](item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get length() {
|
||||||
|
return this[kCount];
|
||||||
|
}
|
||||||
|
|
||||||
[kIndexEntry](entry) {
|
[kIndexEntry](entry) {
|
||||||
// Default implementation does nothing
|
// Default implementation does nothing
|
||||||
}
|
}
|
||||||
@ -384,9 +399,22 @@ class Performance extends PerformanceObserverEntryList {
|
|||||||
this[kIndex] = {
|
this[kIndex] = {
|
||||||
[kMarks]: new Set()
|
[kMarks]: new Set()
|
||||||
};
|
};
|
||||||
|
this[kMaxCount] = kDefaultMaxCount;
|
||||||
this[kInsertEntry](nodeTiming);
|
this[kInsertEntry](nodeTiming);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set maxEntries(val) {
|
||||||
|
if (typeof val !== 'number' || val >>> 0 !== val) {
|
||||||
|
const errors = lazyErrors();
|
||||||
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'val', 'number');
|
||||||
|
}
|
||||||
|
this[kMaxCount] = Math.max(1, val >>> 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get maxEntries() {
|
||||||
|
return this[kMaxCount];
|
||||||
|
}
|
||||||
|
|
||||||
[kIndexEntry](item) {
|
[kIndexEntry](item) {
|
||||||
const index = this[kIndex];
|
const index = this[kIndex];
|
||||||
const type = item.entry.entryType;
|
const type = item.entry.entryType;
|
||||||
@ -397,6 +425,17 @@ class Performance extends PerformanceObserverEntryList {
|
|||||||
}
|
}
|
||||||
const entry = item.entry;
|
const entry = item.entry;
|
||||||
L.append(items, { entry, item });
|
L.append(items, { entry, item });
|
||||||
|
const count = this[kCount];
|
||||||
|
if (count > this[kMaxCount]) {
|
||||||
|
const text = count === 1 ? 'is 1 entry' : `are ${count} entries`;
|
||||||
|
process.emitWarning('Possible perf_hooks memory leak detected. ' +
|
||||||
|
`There ${text} in the ` +
|
||||||
|
'Performance Timeline. Use the clear methods ' +
|
||||||
|
'to remove entries that are no longer needed or ' +
|
||||||
|
'set performance.maxEntries equal to a higher ' +
|
||||||
|
'value (currently the maxEntries is ' +
|
||||||
|
`${this[kMaxCount]}).`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[kClearEntry](type, name) {
|
[kClearEntry](type, name) {
|
||||||
@ -411,10 +450,12 @@ class Performance extends PerformanceObserverEntryList {
|
|||||||
if (entry.name === `${name}`) {
|
if (entry.name === `${name}`) {
|
||||||
L.remove(item); // remove from the index
|
L.remove(item); // remove from the index
|
||||||
L.remove(item.item); // remove from the master
|
L.remove(item.item); // remove from the master
|
||||||
|
this[kCount]--;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
L.remove(item); // remove from the index
|
L.remove(item); // remove from the index
|
||||||
L.remove(item.item); // remove from the master
|
L.remove(item.item); // remove from the master
|
||||||
|
this[kCount]--;
|
||||||
}
|
}
|
||||||
item = next;
|
item = next;
|
||||||
}
|
}
|
||||||
|
29
test/parallel/test-performance-warning.js
Normal file
29
test/parallel/test-performance-warning.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Flags: --no-warnings
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const { performance } = require('perf_hooks');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
assert.strictEqual(performance.length, 1);
|
||||||
|
assert.strictEqual(performance.maxEntries, 150);
|
||||||
|
|
||||||
|
performance.maxEntries = 1;
|
||||||
|
|
||||||
|
[-1, 0xffffffff + 1, '', null, undefined, Infinity].forEach((i) => {
|
||||||
|
common.expectsError(
|
||||||
|
() => performance.maxEntries = i,
|
||||||
|
{
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
type: TypeError
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
common.expectWarning('Warning', [
|
||||||
|
'Possible perf_hooks memory leak detected. There are 2 entries in the ' +
|
||||||
|
'Performance Timeline. Use the clear methods to remove entries that are no ' +
|
||||||
|
'longer needed or set performance.maxEntries equal to a higher value ' +
|
||||||
|
'(currently the maxEntries is 1).']);
|
||||||
|
|
||||||
|
performance.mark('test');
|
Loading…
x
Reference in New Issue
Block a user