Skip to content

Commit

Permalink
perf_hooks: add PerformanceResourceTiming
Browse files Browse the repository at this point in the history
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
  • Loading branch information
RafaelGSS committed Apr 23, 2022
1 parent c4781ea commit 2173e92
Show file tree
Hide file tree
Showing 6 changed files with 455 additions and 3 deletions.
16 changes: 13 additions & 3 deletions lib/internal/perf/observe.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -340,6 +343,8 @@ function enqueue(entry) {
buffer = markEntryBuffer;
} else if (entryType === 'measure') {
buffer = measureEntryBuffer;
} else if (entryType === 'resource') {
buffer = resourceTimingBuffer;
} else {
return;
}
Expand All @@ -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);
}
}

Expand All @@ -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);
Expand Down
21 changes: 21 additions & 0 deletions lib/internal/perf/performance.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const {

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

const { markResourceTiming } = require('internal/perf/resource_timing');

const {
mark,
measure,
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -117,6 +126,11 @@ ObjectDefineProperties(Performance.prototype, {
enumerable: false,
value: clearMeasures,
},
clearResourceTimings: {
configurable: true,
enumerable: false,
value: clearResourceTimings,
},
eventLoopUtilization: {
configurable: true,
enumerable: false,
Expand Down Expand Up @@ -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,
Expand Down
162 changes: 162 additions & 0 deletions lib/internal/perf/resource_timing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
'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 } = primordials;

const kCacheMode = Symbol('kCacheMode');
const kRequestedUrl = Symbol('kRequestedUrl');
const kTimingInfo = Symbol('kTimingInfo');
const kInitiatorType = Symbol('kInitiatorType');

class PerformanceResourceTiming 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,
};
}
}

// 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');
const resource = new PerformanceResourceTiming(
requestedUrl,
initiatorType,
timingInfo,
cacheMode,
);
enqueue(resource);
return resource;
}

module.exports = {
PerformanceResourceTiming,
markResourceTiming,
};
2 changes: 2 additions & 0 deletions lib/perf_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
} = internalBinding('performance');

const { PerformanceEntry } = require('internal/perf/performance_entry');
const { PerformanceResourceTiming } = require('internal/perf/resource_timing');
const {
PerformanceObserver,
PerformanceObserverEntryList,
Expand All @@ -31,6 +32,7 @@ module.exports = {
PerformanceMeasure,
PerformanceObserver,
PerformanceObserverEntryList,
PerformanceResourceTiming,
monitorEventLoopDelay,
createHistogram,
performance: new InternalPerformance(),
Expand Down
1 change: 1 addition & 0 deletions test/parallel/test-bootstrap-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const expectedModules = new Set([
'NativeModule internal/perf/performance',
'NativeModule internal/perf/timerify',
'NativeModule internal/perf/usertiming',
'NativeModule internal/perf/resource_timing',
'NativeModule internal/perf/utils',
'NativeModule internal/priority_queue',
'NativeModule internal/process/esm_loader',
Expand Down
Loading

0 comments on commit 2173e92

Please sign in to comment.