diff --git a/Libraries/WebPerformance/NativePerformance.cpp b/Libraries/WebPerformance/NativePerformance.cpp index c66f060d6eb..355f3f2f531 100644 --- a/Libraries/WebPerformance/NativePerformance.cpp +++ b/Libraries/WebPerformance/NativePerformance.cpp @@ -22,4 +22,21 @@ void NativePerformance::mark( PerformanceEntryReporter::getInstance().mark(name, startTime, duration); } +void NativePerformance::clearMarks( + jsi::Runtime &rt, + std::optional markName) {} + +void NativePerformance::measure( + jsi::Runtime &rt, + std::string name, + double startTime, + double endTime, + std::optional duration, + std::optional startMark, + std::optional endMark) {} + +void NativePerformance::clearMeasures( + jsi::Runtime &rt, + std::optional measureName) {} + } // namespace facebook::react diff --git a/Libraries/WebPerformance/NativePerformance.h b/Libraries/WebPerformance/NativePerformance.h index 2d59d5901c1..2c23b959d0a 100644 --- a/Libraries/WebPerformance/NativePerformance.h +++ b/Libraries/WebPerformance/NativePerformance.h @@ -27,6 +27,18 @@ class NativePerformance : public NativePerformanceCxxSpec, void mark(jsi::Runtime &rt, std::string name, double startTime, double duration); + void clearMarks(jsi::Runtime &rt, std::optional markName); + + void measure( + jsi::Runtime &rt, + std::string name, + double startTime, + double endTime, + std::optional duration, + std::optional startMark, + std::optional endMark); + + void clearMeasures(jsi::Runtime &rt, std::optional measureName); private: }; diff --git a/Libraries/WebPerformance/NativePerformance.js b/Libraries/WebPerformance/NativePerformance.js index 443d4ef2430..369dd23f7cf 100644 --- a/Libraries/WebPerformance/NativePerformance.js +++ b/Libraries/WebPerformance/NativePerformance.js @@ -14,6 +14,17 @@ import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { +mark?: (name: string, startTime: number, duration: number) => void; + +clearMarks?: (markName?: string) => void; + + +measure?: ( + name: string, + startTime: number, + endTime: number, + duration?: number, + startMark?: string, + endMark?: string, + ) => void; + +clearMeasures?: (measureName?: string) => void; } export default (TurboModuleRegistry.get('NativePerformanceCxx'): ?Spec); diff --git a/Libraries/WebPerformance/NativePerformanceObserver.js b/Libraries/WebPerformance/NativePerformanceObserver.js index 39cf0c1f247..34083c4fc4d 100644 --- a/Libraries/WebPerformance/NativePerformanceObserver.js +++ b/Libraries/WebPerformance/NativePerformanceObserver.js @@ -15,6 +15,7 @@ import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export const RawPerformanceEntryTypeValues = { UNDEFINED: 0, MARK: 1, + MEASURE: 2, }; export type RawPerformanceEntryType = number; diff --git a/Libraries/WebPerformance/Performance.js b/Libraries/WebPerformance/Performance.js index a79d2e4541f..a64a0a14e8d 100644 --- a/Libraries/WebPerformance/Performance.js +++ b/Libraries/WebPerformance/Performance.js @@ -36,6 +36,30 @@ export class PerformanceMark extends PerformanceEntry { } } +export type TimeStampOrName = HighResTimeStamp | string; + +export type PerformanceMeasureOptions = { + detail?: DetailType, + start?: TimeStampOrName, + end?: TimeStampOrName, + duration?: HighResTimeStamp, +}; + +export class PerformanceMeasure extends PerformanceEntry { + detail: DetailType; + constructor(measureName: string, measureOptions?: PerformanceMeasureOptions) { + super({ + name: measureName, + entryType: 'measure', + startTime: 0, + duration: measureOptions?.duration ?? 0, + }); + if (measureOptions !== undefined) { + this.detail = measureOptions.detail; + } + } +} + /** * Partial implementation of the Performance interface for RN, * corresponding to the standard in @@ -50,8 +74,73 @@ export default class Performance { NativePerformance?.mark?.(markName, mark.startTime, mark.duration); return mark; } + clearMarks(markName?: string): void { + NativePerformance?.clearMarks?.(markName); + } - clearMarks(markName?: string): void {} + measure( + measureName: string, + startMarkOrOptions?: string | PerformanceMeasureOptions, + endMark?: string, + ): PerformanceMeasure { + let options; + let startMarkName, + endMarkName = endMark, + duration, + startTime = 0, + endTime = 0; + if (typeof startMarkOrOptions === 'string') { + startMarkName = startMarkOrOptions; + } else if (startMarkOrOptions !== undefined) { + options = startMarkOrOptions; + if (endMark !== undefined) { + throw new TypeError( + "Performance.measure: Can't have both options and endMark", + ); + } + if (options.start === undefined && options.end === undefined) { + throw new TypeError( + 'Performance.measure: Must have at least one of start/end specified in options', + ); + } + if ( + options.start !== undefined && + options.end !== undefined && + options.duration !== undefined + ) { + throw new TypeError( + "Performance.measure: Can't have both start/end and duration explicitly in options", + ); + } + + if (typeof options.start === 'number') { + startTime = options.start; + } else { + startMarkName = options.start; + } + + if (typeof options.end === 'number') { + endTime = options.end; + } else { + endMarkName = options.end; + } + + duration = options.duration ?? duration; + } + const measure = new PerformanceMeasure(measureName, options); + NativePerformance?.measure?.( + measureName, + startTime, + endTime, + duration, + startMarkName, + endMarkName, + ); + return measure; + } + clearMeasures(measureName?: string): void { + NativePerformance?.clearMeasures?.(measureName); + } now(): HighResTimeStamp { return getCurrentTimeStamp(); diff --git a/Libraries/WebPerformance/PerformanceObserver.js b/Libraries/WebPerformance/PerformanceObserver.js index 1b935bd70a2..f22b253e298 100644 --- a/Libraries/WebPerformance/PerformanceObserver.js +++ b/Libraries/WebPerformance/PerformanceObserver.js @@ -14,11 +14,12 @@ import type { } from './NativePerformanceObserver'; import warnOnce from '../Utilities/warnOnce'; -import NativePerformanceObserver from './NativePerformanceObserver'; +import NativePerformanceObserver, { + RawPerformanceEntryTypeValues, +} from './NativePerformanceObserver'; export type HighResTimeStamp = number; -// TODO: Extend once new types (such as event) are supported. -export type PerformanceEntryType = 'mark'; +export type PerformanceEntryType = 'mark' | 'measure'; export class PerformanceEntry { name: string; @@ -52,7 +53,11 @@ export class PerformanceEntry { function rawToPerformanceEntryType( type: RawPerformanceEntryType, ): PerformanceEntryType { - return 'mark'; + if (type === RawPerformanceEntryTypeValues.MARK) { + return 'mark'; + } else { + return 'measure'; + } } function rawToPerformanceEntry(entry: RawPerformanceEntry): PerformanceEntry { @@ -232,5 +237,5 @@ export default class PerformanceObserver { static supportedEntryTypes: $ReadOnlyArray = // TODO: add types once they are fully supported - Object.freeze(['mark']); + Object.freeze(['mark', 'measure']); }