2021-05-14 10:11:59 +01:00
|
|
|
/*
|
|
|
|
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
|
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { PerformanceEntryNames } from "./entry-names";
|
|
|
|
|
|
|
|
|
|
const START_PREFIX = "start:";
|
|
|
|
|
const STOP_PREFIX = "stop:";
|
|
|
|
|
|
|
|
|
|
export {
|
|
|
|
|
PerformanceEntryNames,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface GetEntriesOptions {
|
|
|
|
|
name?: string,
|
|
|
|
|
type?: string,
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:23:23 +01:00
|
|
|
type PerformanceCallbackFunction = (entry: PerformanceEntry[]) => void;
|
2021-05-14 11:04:58 +01:00
|
|
|
|
|
|
|
|
interface PerformanceDataListener {
|
2021-05-14 12:23:23 +01:00
|
|
|
entryNames?: string[],
|
2021-05-14 11:04:58 +01:00
|
|
|
callback: PerformanceCallbackFunction
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let listeners: PerformanceDataListener[] = [];
|
|
|
|
|
const entries: PerformanceEntry[] = [];
|
|
|
|
|
|
2021-05-14 10:11:59 +01:00
|
|
|
export default class PerformanceMonitor {
|
|
|
|
|
/**
|
|
|
|
|
* Starts a performance recording
|
|
|
|
|
* @param name Name of the recording
|
|
|
|
|
* @param id Specify an identifier appended to the measurement name
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
|
|
|
|
static start(name: string, id?: string): void {
|
|
|
|
|
if (!supportsPerformanceApi()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const key = buildKey(name, id);
|
|
|
|
|
|
2021-05-14 11:04:58 +01:00
|
|
|
if (performance.getEntriesByName(key).length > 0) {
|
2021-05-14 10:11:59 +01:00
|
|
|
console.warn(`Recording already started for: ${name}`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
performance.mark(START_PREFIX + key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stops a performance recording and stores delta duration
|
|
|
|
|
* with the start marker
|
|
|
|
|
* @param name Name of the recording
|
|
|
|
|
* @param id Specify an identifier appended to the measurement name
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
2021-05-14 11:04:58 +01:00
|
|
|
static stop(name: string, id?: string): PerformanceEntry {
|
2021-05-14 10:11:59 +01:00
|
|
|
if (!supportsPerformanceApi()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const key = buildKey(name, id);
|
2021-05-14 11:04:58 +01:00
|
|
|
if (performance.getEntriesByName(START_PREFIX + key).length === 0) {
|
2021-05-14 10:11:59 +01:00
|
|
|
console.warn(`No recording started for: ${name}`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
performance.mark(STOP_PREFIX + key);
|
|
|
|
|
performance.measure(
|
|
|
|
|
key,
|
|
|
|
|
START_PREFIX + key,
|
|
|
|
|
STOP_PREFIX + key,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this.clear(name, id);
|
2021-05-14 11:04:58 +01:00
|
|
|
|
|
|
|
|
const measurement = performance.getEntriesByName(key).pop();
|
|
|
|
|
|
|
|
|
|
// Keeping a reference to all PerformanceEntry created
|
|
|
|
|
// by this abstraction for historical events collection
|
|
|
|
|
// when adding a data callback
|
|
|
|
|
entries.push(measurement);
|
|
|
|
|
|
2021-05-14 12:23:23 +01:00
|
|
|
listeners.forEach(listener => {
|
|
|
|
|
if (shouldEmit(listener, measurement)) {
|
|
|
|
|
listener.callback([measurement])
|
|
|
|
|
}
|
|
|
|
|
});
|
2021-05-14 11:04:58 +01:00
|
|
|
|
|
|
|
|
return measurement;
|
2021-05-14 10:11:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static clear(name: string, id?: string): void {
|
|
|
|
|
if (!supportsPerformanceApi()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const key = buildKey(name, id);
|
|
|
|
|
performance.clearMarks(START_PREFIX + key);
|
|
|
|
|
performance.clearMarks(STOP_PREFIX + key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static getEntries({ name, type }: GetEntriesOptions = {}): PerformanceEntry[] {
|
2021-05-14 11:04:58 +01:00
|
|
|
return entries.filter(entry => {
|
|
|
|
|
const satisfiesName = !name || entry.name === name;
|
|
|
|
|
const satisfiedType = !type || entry.entryType === type;
|
|
|
|
|
return satisfiesName && satisfiedType;
|
|
|
|
|
});
|
|
|
|
|
}
|
2021-05-14 10:11:59 +01:00
|
|
|
|
2021-05-14 11:04:58 +01:00
|
|
|
static addPerformanceDataCallback(listener: PerformanceDataListener, buffer = false) {
|
|
|
|
|
listeners.push(listener);
|
|
|
|
|
if (buffer) {
|
2021-05-14 12:23:23 +01:00
|
|
|
const toEmit = entries.filter(entry => shouldEmit(listener, entry));
|
|
|
|
|
if (toEmit.length > 0) {
|
|
|
|
|
listener.callback(toEmit);
|
|
|
|
|
}
|
2021-05-14 10:11:59 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-05-14 11:04:58 +01:00
|
|
|
|
|
|
|
|
static removePerformanceDataCallback(callback?: PerformanceCallbackFunction) {
|
|
|
|
|
if (!callback) {
|
|
|
|
|
listeners = [];
|
|
|
|
|
} else {
|
|
|
|
|
listeners.splice(
|
|
|
|
|
listeners.findIndex(listener => listener.callback === callback),
|
|
|
|
|
1,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:23:23 +01:00
|
|
|
function shouldEmit(listener: PerformanceDataListener, entry: PerformanceEntry): boolean {
|
|
|
|
|
return !listener.entryNames || listener.entryNames.includes(entry.name);
|
2021-05-14 10:11:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Tor browser does not support the Performance API
|
|
|
|
|
* @returns {boolean} true if the Performance API is supported
|
|
|
|
|
*/
|
|
|
|
|
function supportsPerformanceApi(): boolean {
|
|
|
|
|
return performance !== undefined && performance.mark !== undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Internal utility to ensure consistent name for the recording
|
|
|
|
|
* @param name Name of the recording
|
|
|
|
|
* @param id Specify an identifier appended to the measurement name
|
|
|
|
|
* @returns {string} a compound of the name and identifier if present
|
|
|
|
|
*/
|
|
|
|
|
function buildKey(name: string, id?: string): string {
|
|
|
|
|
return `${name}${id ? `:${id}` : ''}`;
|
|
|
|
|
}
|
2021-05-14 12:23:23 +01:00
|
|
|
|
|
|
|
|
window.mxPerformanceMonitor = PerformanceMonitor;
|
|
|
|
|
window.mxPerformanceEntryNames = PerformanceEntryNames;
|