From a64319a2b26628fa2100136f2118258a1d2c56e3 Mon Sep 17 00:00:00 2001 From: Ruslan Shestopalyuk Date: Wed, 30 Nov 2022 09:28:53 -0800 Subject: [PATCH] Add API and scaffolding for Performance.mark implementation Summary: [Changelog][Internal] Adds API definition for [Performance.mark](https://www.w3.org/TR/user-timing/#mark-method) support. This is a bare bone implementation, that just logs events on the native side. The next step is the native logic for queuing, flushing etc. Note that here I route both JS and native marks to native for now, for simplicity sake - ultimately this may not be what we want, as it may be more efficient to process marks, logged from JS, on the JS side. Reviewed By: rubennorte Differential Revision: D41472148 fbshipit-source-id: bdf2b182b8472a71a5500235849bca5af1c2f360 --- .../NativePerformanceObserver.cpp | 9 +++ .../NativePerformanceObserver.h | 2 + .../NativePerformanceObserver.js | 4 ++ Libraries/WebPerformance/Performance.js | 66 +++++++++++++++++++ .../WebPerformance/PerformanceObserver.js | 19 +++--- 5 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 Libraries/WebPerformance/Performance.js diff --git a/Libraries/WebPerformance/NativePerformanceObserver.cpp b/Libraries/WebPerformance/NativePerformanceObserver.cpp index 32ab293e6cbf37..21bfd9feafb4e3 100644 --- a/Libraries/WebPerformance/NativePerformanceObserver.cpp +++ b/Libraries/WebPerformance/NativePerformanceObserver.cpp @@ -39,4 +39,13 @@ void NativePerformanceObserver::setOnPerformanceEntryCallback( << (callback ? "non-empty" : "empty"); } +void NativePerformanceObserver::logEntryForDebug( + jsi::Runtime &rt, + RawPerformanceEntry entry) { + LOG(INFO) << "NativePerformanceObserver::logEntry: " + << "name=" << entry.name << " type=" << entry.entryType + << " startTime=" << entry.startTime + << " duration=" << entry.duration; +} + } // namespace facebook::react diff --git a/Libraries/WebPerformance/NativePerformanceObserver.h b/Libraries/WebPerformance/NativePerformanceObserver.h index 3a3250dcbcc6e7..e001370f1e26dd 100644 --- a/Libraries/WebPerformance/NativePerformanceObserver.h +++ b/Libraries/WebPerformance/NativePerformanceObserver.h @@ -57,6 +57,8 @@ class NativePerformanceObserver jsi::Runtime &rt, std::optional> callback); + void logEntryForDebug(jsi::Runtime &rt, RawPerformanceEntry entry); + private: std::optional> callback_; }; diff --git a/Libraries/WebPerformance/NativePerformanceObserver.js b/Libraries/WebPerformance/NativePerformanceObserver.js index b3d6fdefc8b7f7..c74502eb80a31a 100644 --- a/Libraries/WebPerformance/NativePerformanceObserver.js +++ b/Libraries/WebPerformance/NativePerformanceObserver.js @@ -14,6 +14,7 @@ import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export const RawPerformanceEntryTypeValues = { UNDEFINED: 0, + MARK: 1, }; export type RawPerformanceEntryType = number; @@ -34,6 +35,9 @@ export interface Spec extends TurboModule { +stopReporting: (entryType: string) => void; +getPendingEntries: () => $ReadOnlyArray; +setOnPerformanceEntryCallback: (callback?: () => void) => void; + + // NOTE: this is for dev-only purposes (potentially is going to be moved elsewhere) + +logEntryForDebug?: (entry: RawPerformanceEntry) => void; } export default (TurboModuleRegistry.get( diff --git a/Libraries/WebPerformance/Performance.js b/Libraries/WebPerformance/Performance.js new file mode 100644 index 00000000000000..5b1de3ea225e25 --- /dev/null +++ b/Libraries/WebPerformance/Performance.js @@ -0,0 +1,66 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow strict + */ + +import type {HighResTimeStamp} from './PerformanceObserver'; + +import NativePerformanceObserver, { + RawPerformanceEntryTypeValues, +} from './NativePerformanceObserver'; +import {PerformanceEntry} from './PerformanceObserver'; + +type DetailType = mixed; + +export type PerformanceMarkOptions = { + detail?: DetailType, + startTime?: HighResTimeStamp, +}; + +function getCurrentTimeStamp(): HighResTimeStamp { + return global.nativePerformanceNow?.() ?? Date.now(); +} + +export class PerformanceMark extends PerformanceEntry { + detail: DetailType; + + constructor(markName: string, markOptions?: PerformanceMarkOptions) { + let startTime = markOptions?.startTime ?? getCurrentTimeStamp(); + super({name: markName, entryType: 'mark', startTime, duration: 0}); + if (markOptions !== undefined) { + this.detail = markOptions.detail; + } + } +} + +/** + * Partial implementation of the Performance interface for RN, + * corresponding to the standard in + * https://www.w3.org/TR/user-timing/#extensions-performance-interface + */ +export default class Performance { + mark( + markName: string, + markOptions?: PerformanceMarkOptions, + ): PerformanceMark { + const mark = new PerformanceMark(markName, markOptions); + NativePerformanceObserver?.logEntryForDebug?.({ + name: markName, + entryType: RawPerformanceEntryTypeValues.MARK, + startTime: mark.startTime, + duration: mark.duration, + }); + return mark; + } + + clearMarks(markName?: string): void {} + + now(): HighResTimeStamp { + return getCurrentTimeStamp(); + } +} diff --git a/Libraries/WebPerformance/PerformanceObserver.js b/Libraries/WebPerformance/PerformanceObserver.js index dc9a0c4aff6f47..b3cf01d9931798 100644 --- a/Libraries/WebPerformance/PerformanceObserver.js +++ b/Libraries/WebPerformance/PerformanceObserver.js @@ -18,8 +18,7 @@ import NativePerformanceObserver from './NativePerformanceObserver'; export type HighResTimeStamp = number; // TODO: Extend once new types (such as event) are supported. -// TODO: Get rid of the "undefined" once there is at least one type supported. -export type PerformanceEntryType = 'undefined'; +export type PerformanceEntryType = 'undefined' | 'mark'; export class PerformanceEntry { name: string; @@ -53,7 +52,7 @@ export class PerformanceEntry { function rawToPerformanceEntryType( type: RawPerformanceEntryType, ): PerformanceEntryType { - return 'undefined'; + return 'mark'; } function rawToPerformanceEntry(entry: RawPerformanceEntry): PerformanceEntry { @@ -166,7 +165,7 @@ export default class PerformanceObserver { } else { this._entryTypes = new Set([options.type]); } - this._entryTypes.forEach(type => { + for (const type of this._entryTypes) { if (!_observedEntryTypeRefCount.has(type)) { NativePerformanceObserver.startReporting(type); } @@ -174,7 +173,7 @@ export default class PerformanceObserver { type, (_observedEntryTypeRefCount.get(type) ?? 0) + 1, ); - }); + } _observers.add(this); } @@ -183,7 +182,7 @@ export default class PerformanceObserver { warnNoNativePerformanceObserver(); return; } - this._entryTypes.forEach(type => { + for (const type of this._entryTypes) { const entryTypeRefCount = _observedEntryTypeRefCount.get(type) ?? 0; if (entryTypeRefCount === 1) { _observedEntryTypeRefCount.delete(type); @@ -191,7 +190,7 @@ export default class PerformanceObserver { } else if (entryTypeRefCount !== 0) { _observedEntryTypeRefCount.set(type, entryTypeRefCount - 1); } - }); + } _observers.delete(this); if (_observers.size === 0) { NativePerformanceObserver.setOnPerformanceEntryCallback(undefined); @@ -201,7 +200,7 @@ export default class PerformanceObserver { static supportedEntryTypes: $ReadOnlyArray = // TODO: add types once they are fully supported - Object.freeze([]); + Object.freeze(['mark']); } // This is a callback that gets scheduled and periodically called from the native side @@ -211,7 +210,7 @@ function onPerformanceEntry() { } const rawEntries = NativePerformanceObserver.getPendingEntries(); const entries = rawEntries.map(rawToPerformanceEntry); - _observers.forEach(observer => { + for (const observer of _observers) { const entriesForObserver: PerformanceEntryList = entries.filter(entry => observer._entryTypes.has(entry.entryType), ); @@ -219,5 +218,5 @@ function onPerformanceEntry() { new PerformanceObserverEntryList(entriesForObserver), observer, ); - }); + } }