diff options
author | RafaelGSS <rafael.nunu@hotmail.com> | 2022-04-10 05:37:03 +0300 |
---|---|---|
committer | Beth Griggs <bgriggs@redhat.com> | 2022-05-16 13:12:16 +0300 |
commit | c92e291beb9dc832af5510bf7fb1e8e8a4923124 (patch) | |
tree | 4b036de25aeb0c9acd27ab17eeb4f19b4efdcd64 /lib | |
parent | 76096c2d4afac1cedd114d188ce09007b3da8282 (diff) |
perf_hooks: add PerformanceResourceTiming
perf_hooks: create clearResourceTimings
perf_hooks: add resourcetiming test parallel
perf_hooks: add markResourceTiming
perf_hooks: fix observable when using resource
perf_hooks: fix observable when using resource
perf_hooks: add class comments
perf_hooks: add PerformanceResourceTiming
perf_hooks: create clearResourceTimings
perf_hooks: add resourcetiming test parallel
perf_hooks: add markResourceTiming
perf_hooks: fix observable when using resource
perf_hooks: fix observable when using resource
perf_hooks: add class comments
perf_hooks: add Resource Timing documentation
benchmark: measure resource timing module
perf_hooks: add check avoiding new PerformanceResourceTiming
perf_hooks: adjust doc
PR-URL: https://github.com/nodejs/node/pull/42725
Fixes: https://github.com/nodejs/undici/issues/952
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/internal/perf/observe.js | 16 | ||||
-rw-r--r-- | lib/internal/perf/performance.js | 21 | ||||
-rw-r--r-- | lib/internal/perf/resource_timing.js | 179 | ||||
-rw-r--r-- | lib/perf_hooks.js | 2 |
4 files changed, 215 insertions, 3 deletions
diff --git a/lib/internal/perf/observe.js b/lib/internal/perf/observe.js index 18fc10eb789..eedf84d1e7e 100644 --- a/lib/internal/perf/observe.js +++ b/lib/internal/perf/observe.js @@ -85,15 +85,18 @@ const kSupportedEntryTypes = ObjectFreeze([ 'mark', 'measure', 'net', + 'resource', ]); // Performance timeline entry Buffers let markEntryBuffer = []; let measureEntryBuffer = []; +let resourceTimingBuffer = []; const kMaxPerformanceEntryBuffers = 1e6; const kClearPerformanceEntryBuffers = ObjectFreeze({ 'mark': 'performance.clearMarks', 'measure': 'performance.clearMeasures', + 'resource': 'performance.clearResourceTimings', }); const kWarnedEntryTypes = new SafeMap(); @@ -340,6 +343,8 @@ function enqueue(entry) { buffer = markEntryBuffer; } else if (entryType === 'measure') { buffer = measureEntryBuffer; + } else if (entryType === 'resource') { + buffer = resourceTimingBuffer; } else { return; } @@ -365,16 +370,19 @@ function enqueue(entry) { } function clearEntriesFromBuffer(type, name) { - if (type !== 'mark' && type !== 'measure') { + if (type !== 'mark' && type !== 'measure' && type !== 'resource') { return; } if (type === 'mark') { markEntryBuffer = name === undefined ? [] : ArrayPrototypeFilter(markEntryBuffer, (entry) => entry.name !== name); - } else { + } else if (type === 'measure') { measureEntryBuffer = name === undefined ? [] : ArrayPrototypeFilter(measureEntryBuffer, (entry) => entry.name !== name); + } else { + resourceTimingBuffer = name === undefined ? + [] : ArrayPrototypeFilter(resourceTimingBuffer, (entry) => entry.name !== name); } } @@ -384,11 +392,13 @@ function filterBufferMapByNameAndType(name, type) { bufferList = markEntryBuffer; } else if (type === 'measure') { bufferList = measureEntryBuffer; + } else if (type === 'resource') { + bufferList = resourceTimingBuffer; } else if (type !== undefined) { // Unrecognized type; return []; } else { - bufferList = ArrayPrototypeConcat(markEntryBuffer, measureEntryBuffer); + bufferList = ArrayPrototypeConcat(markEntryBuffer, measureEntryBuffer, resourceTimingBuffer); } if (name !== undefined) { bufferList = ArrayPrototypeFilter(bufferList, (buffer) => buffer.name === name); diff --git a/lib/internal/perf/performance.js b/lib/internal/perf/performance.js index 20603fa382e..5c7c008ee14 100644 --- a/lib/internal/perf/performance.js +++ b/lib/internal/perf/performance.js @@ -19,6 +19,8 @@ const { const { now } = require('internal/perf/utils'); +const { markResourceTiming } = require('internal/perf/resource_timing'); + const { mark, measure, @@ -82,6 +84,13 @@ function clearMeasures(name) { clearEntriesFromBuffer('measure', name); } +function clearResourceTimings(name) { + if (name !== undefined) { + name = `${name}`; + } + clearEntriesFromBuffer('resource', name); +} + function getEntries() { return filterBufferMapByNameAndType(); } @@ -117,6 +126,11 @@ ObjectDefineProperties(Performance.prototype, { enumerable: false, value: clearMeasures, }, + clearResourceTimings: { + configurable: true, + enumerable: false, + value: clearResourceTimings, + }, eventLoopUtilization: { configurable: true, enumerable: false, @@ -152,6 +166,13 @@ ObjectDefineProperties(Performance.prototype, { enumerable: false, value: nodeTiming, }, + // In the browser, this function is not public. However, it must be used inside fetch + // which is a Node.js dependency, not a internal module + markResourceTiming: { + configurable: true, + enumerable: false, + value: markResourceTiming, + }, now: { configurable: true, enumerable: false, diff --git a/lib/internal/perf/resource_timing.js b/lib/internal/perf/resource_timing.js new file mode 100644 index 00000000000..ff0728c9f0f --- /dev/null +++ b/lib/internal/perf/resource_timing.js @@ -0,0 +1,179 @@ +'use strict'; +// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming + +const { InternalPerformanceEntry } = require('internal/perf/performance_entry'); +const { SymbolToStringTag } = primordials; +const assert = require('internal/assert'); +const { enqueue } = require('internal/perf/observe'); +const { Symbol, ObjectSetPrototypeOf } = primordials; + +const kCacheMode = Symbol('kCacheMode'); +const kRequestedUrl = Symbol('kRequestedUrl'); +const kTimingInfo = Symbol('kTimingInfo'); +const kInitiatorType = Symbol('kInitiatorType'); + +const { + codes: { + ERR_ILLEGAL_CONSTRUCTOR, + } +} = require('internal/errors'); + +class InternalPerformanceResourceTiming extends InternalPerformanceEntry { + constructor(requestedUrl, initiatorType, timingInfo, cacheMode = '') { + super(requestedUrl, 'resource'); + this[kInitiatorType] = initiatorType; + this[kRequestedUrl] = requestedUrl; + // https://fetch.spec.whatwg.org/#fetch-timing-info + // This class is using timingInfo assuming it's already validated. + // The spec doesn't say to validate it in the class construction. + this[kTimingInfo] = timingInfo; + this[kCacheMode] = cacheMode; + } + + get [SymbolToStringTag]() { + return 'PerformanceResourceTiming'; + } + + get name() { + return this[kRequestedUrl]; + } + + get startTime() { + return this[kTimingInfo].startTime; + } + + get duration() { + return this[kTimingInfo].endTime - this[kTimingInfo].startTime; + } + + get workerStart() { + return this[kTimingInfo].finalServiceWorkerStartTime; + } + + get redirectStart() { + return this[kTimingInfo].redirectStartTime; + } + + get redirectEnd() { + return this[kTimingInfo].redirectEndTime; + } + + get fetchStart() { + return this[kTimingInfo].postRedirectStartTime; + } + + get domainLookupStart() { + return this[kTimingInfo].finalConnectionTimingInfo?.domainLookupStartTime; + } + + get domainLookupEnd() { + return this[kTimingInfo].finalConnectionTimingInfo?.domainLookupEndTime; + } + + get connectStart() { + return this[kTimingInfo].finalConnectionTimingInfo?.connectionStartTime; + } + + get connectEnd() { + return this[kTimingInfo].finalConnectionTimingInfo?.connectionEndTime; + } + + get secureConnectionStart() { + return this[kTimingInfo] + .finalConnectionTimingInfo?.secureConnectionStartTime; + } + + get nextHopProtocol() { + return this[kTimingInfo] + .finalConnectionTimingInfo?.ALPNNegotiatedProtocol; + } + + get requestStart() { + return this[kTimingInfo].finalNetworkRequestStartTime; + } + + get responseStart() { + return this[kTimingInfo].finalNetworkResponseStartTime; + } + + get responseEnd() { + return this[kTimingInfo].endTime; + } + + get encodedBodySize() { + return this[kTimingInfo].encodedBodySize; + } + + get decodedBodySize() { + return this[kTimingInfo].decodedBodySize; + } + + get transferSize() { + if (this[kCacheMode] === 'local') return 0; + if (this[kCacheMode] === 'validated') return 300; + + return this[kTimingInfo].encodedBodySize + 300; + } + + toJSON() { + return { + name: this.name, + entryType: this.entryType, + startTime: this.startTime, + duration: this.duration, + initiatorType: this[kInitiatorType], + nextHopProtocol: this.nextHopProtocol, + workerStart: this.workerStart, + redirectStart: this.redirectStart, + redirectEnd: this.redirectEnd, + fetchStart: this.fetchStart, + domainLookupStart: this.domainLookupStart, + domainLookupEnd: this.domainLookupEnd, + connectStart: this.connectStart, + connectEnd: this.connectEnd, + secureConnectionStart: this.secureConnectionStart, + requestStart: this.requestStart, + responseStart: this.responseStart, + responseEnd: this.responseEnd, + transferSize: this.transferSize, + encodedBodySize: this.encodedBodySize, + decodedBodySize: this.decodedBodySize, + }; + } +} + +class PerformanceResourceTiming extends InternalPerformanceResourceTiming { + constructor() { + throw new ERR_ILLEGAL_CONSTRUCTOR(); + } +} + +// https://w3c.github.io/resource-timing/#dfn-mark-resource-timing +function markResourceTiming( + timingInfo, + requestedUrl, + initiatorType, + global, + cacheMode, +) { + // https://w3c.github.io/resource-timing/#dfn-setup-the-resource-timing-entry + assert( + cacheMode === '' || cacheMode === 'local', + 'cache must be an empty string or \'local\'', + ); + const resource = new InternalPerformanceResourceTiming( + requestedUrl, + initiatorType, + timingInfo, + cacheMode, + ); + + ObjectSetPrototypeOf(resource, PerformanceResourceTiming.prototype); + enqueue(resource); + return resource; +} + +module.exports = { + PerformanceResourceTiming, + markResourceTiming, +}; diff --git a/lib/perf_hooks.js b/lib/perf_hooks.js index 74ba890f537..2456bcb7e7e 100644 --- a/lib/perf_hooks.js +++ b/lib/perf_hooks.js @@ -9,6 +9,7 @@ const { } = internalBinding('performance'); const { PerformanceEntry } = require('internal/perf/performance_entry'); +const { PerformanceResourceTiming } = require('internal/perf/resource_timing'); const { PerformanceObserver, PerformanceObserverEntryList, @@ -31,6 +32,7 @@ module.exports = { PerformanceMeasure, PerformanceObserver, PerformanceObserverEntryList, + PerformanceResourceTiming, monitorEventLoopDelay, createHistogram, performance: new InternalPerformance(), |