Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions Libraries/WebPerformance/EventCounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* 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.
*
* @flow strict
* @format
*/

import NativePerformanceObserver from './NativePerformanceObserver';
import {warnNoNativePerformanceObserver} from './PerformanceObserver';
import {rawToPerformanceEntryType} from './RawPerformanceEntry';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ Libraries/WebPerformance/EventCounts.js line 13 – 'rawToPerformanceEntryType' is defined but never used. (no-unused-vars)


type EventCountsForEachCallbackType =
| (() => void)
| ((value: number) => void)
| ((value: number, key: string) => void)
| ((value: number, key: string, map: Map<string, number>) => void);

/**
* Implementation of the EventCounts Web Performance API
* corresponding to the standard in
* https://www.w3.org/TR/event-timing/#eventcounts
*/
export interface EventCounts {
size: number;

+entries: () => Iterator<[string, number]>;
+forEach: (callback: EventCountsForEachCallbackType) => void;
+get: (key: string) => ?number;
+has: (key: string) => boolean;
+keys: () => Iterator<string>;
+values: () => Iterator<number>;
}

let cachedEventCounts: ?Map<string, number>;

function getCachedEventCounts(): Map<string, number> {
if (cachedEventCounts) {
return cachedEventCounts;
}
if (!NativePerformanceObserver) {
warnNoNativePerformanceObserver();
return new Map();
}

cachedEventCounts = new Map<string, number>(
NativePerformanceObserver.getEventCounts(),
);
// $FlowFixMe[incompatible-call]
global.queueMicrotask(() => {
// To be consistent with the calls to the API from the same task,
// but also not to refetch the data from native too often,
// schedule to invalidate the cache later,
// after the current task is guaranteed to have finished.
cachedEventCounts = null;
});
return cachedEventCounts ?? new Map();
}

// flowlint unsafe-getters-setters:off
export const EventCountsProxy: EventCounts = {
get size(): number {
return getCachedEventCounts().size;
},

set size(value: number) {
throw new TypeError('EventCounts.size property is read-only');
},

entries: (): Iterator<[string, number]> => {
return getCachedEventCounts().entries();
},

forEach: (callback: EventCountsForEachCallbackType) => {
return getCachedEventCounts().forEach(callback);
},

get: (key: string): ?number => {
return getCachedEventCounts().get(key);
},

has: (key: string): boolean => {
return getCachedEventCounts().has(key);
},

keys: (): Iterator<string> => {
return getCachedEventCounts().keys();
},

values: (): Iterator<number> => {
return getCachedEventCounts().values();
},
};
26 changes: 24 additions & 2 deletions Libraries/WebPerformance/NativePerformanceObserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,26 @@ NativePerformanceObserver::~NativePerformanceObserver() {

void NativePerformanceObserver::startReporting(
jsi::Runtime &rt,
int32_t entryType) {
PerformanceEntryType entryType) {
PerformanceEntryReporter::getInstance().startReporting(
static_cast<PerformanceEntryType>(entryType));
}

void NativePerformanceObserver::stopReporting(
jsi::Runtime &rt,
int32_t entryType) {
PerformanceEntryType entryType) {
PerformanceEntryReporter::getInstance().stopReporting(
static_cast<PerformanceEntryType>(entryType));
}

void NativePerformanceObserver::setDurationThreshold(
jsi::Runtime &rt,
PerformanceEntryType entryType,
double durationThreshold) {
PerformanceEntryReporter::getInstance().setDurationThreshold(
static_cast<PerformanceEntryType>(entryType), durationThreshold);
}

GetPendingEntriesResult NativePerformanceObserver::popPendingEntries(
jsi::Runtime &rt) {
return PerformanceEntryReporter::getInstance().popPendingEntries();
Expand All @@ -45,4 +53,18 @@ void NativePerformanceObserver::setOnPerformanceEntryCallback(
PerformanceEntryReporter::getInstance().setReportingCallback(callback);
}

void NativePerformanceObserver::logRawEntry(
jsi::Runtime &rt,
RawPerformanceEntry entry) {
PerformanceEntryReporter::getInstance().logEntry(entry);
}

std::vector<std::pair<std::string, uint32_t>>
NativePerformanceObserver::getEventCounts(jsi::Runtime &rt) {
const auto &eventCounts =
PerformanceEntryReporter::getInstance().getEventCounts();
return std::vector<std::pair<std::string, uint32_t>>(
eventCounts.begin(), eventCounts.end());
}

} // namespace facebook::react
21 changes: 17 additions & 4 deletions Libraries/WebPerformance/NativePerformanceObserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ class PerformanceEntryReporter;

#pragma mark - Structs

using PerformanceEntryType =
NativePerformanceObserverCxxRawPerformanceEntryType;

using RawPerformanceEntry = NativePerformanceObserverCxxBaseRawPerformanceEntry<
std::string,
int32_t,
PerformanceEntryType,
double,
double,
// For "event" entries only:
Expand All @@ -32,7 +35,7 @@ template <>
struct Bridging<RawPerformanceEntry>
: NativePerformanceObserverCxxBaseRawPerformanceEntryBridging<
std::string,
int32_t,
PerformanceEntryType,
double,
double,
std::optional<double>,
Expand All @@ -59,16 +62,26 @@ class NativePerformanceObserver
NativePerformanceObserver(std::shared_ptr<CallInvoker> jsInvoker);
~NativePerformanceObserver();

void startReporting(jsi::Runtime &rt, int32_t entryType);
void startReporting(jsi::Runtime &rt, PerformanceEntryType entryType);

void stopReporting(jsi::Runtime &rt, int32_t entryType);
void stopReporting(jsi::Runtime &rt, PerformanceEntryType entryType);

GetPendingEntriesResult popPendingEntries(jsi::Runtime &rt);

void setOnPerformanceEntryCallback(
jsi::Runtime &rt,
std::optional<AsyncCallback<>> callback);

void logRawEntry(jsi::Runtime &rt, RawPerformanceEntry entry);

void setDurationThreshold(
jsi::Runtime &rt,
PerformanceEntryType entryType,
double durationThreshold);

std::vector<std::pair<std::string, uint32_t>> getEventCounts(
jsi::Runtime &rt);

private:
};

Expand Down
21 changes: 13 additions & 8 deletions Libraries/WebPerformance/NativePerformanceObserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ import type {TurboModule} from '../TurboModule/RCTExport';

import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';

export const RawPerformanceEntryTypeValues = {
UNDEFINED: 0,
MARK: 1,
MEASURE: 2,
EVENT: 3,
};

export type RawPerformanceEntryType = number;
export enum RawPerformanceEntryType {
UNDEFINED = 0,
MARK = 1,
MEASURE = 2,
EVENT = 3,
_COUNT = 4,
}

export type RawPerformanceEntry = {|
name: string,
Expand All @@ -42,6 +41,12 @@ export interface Spec extends TurboModule {
+stopReporting: (entryType: RawPerformanceEntryType) => void;
+popPendingEntries: () => GetPendingEntriesResult;
+setOnPerformanceEntryCallback: (callback?: () => void) => void;
+logRawEntry: (entry: RawPerformanceEntry) => void;
+setDurationThreshold: (
entryType: RawPerformanceEntryType,
durationThreshold: number,
) => void;
+getEventCounts: () => $ReadOnlyArray<[string, number]>;
}

export default (TurboModuleRegistry.get<Spec>(
Expand Down
6 changes: 5 additions & 1 deletion Libraries/WebPerformance/Performance.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
* @flow strict
*/

import type {EventCounts} from './EventCounts';
import type {HighResTimeStamp} from './PerformanceEntry';

import warnOnce from '../Utilities/warnOnce';
import {EventCountsProxy} from './EventCounts';
import NativePerformance from './NativePerformance';
import {PerformanceEntry} from './PerformanceEntry';

Expand Down Expand Up @@ -83,9 +85,11 @@ function warnNoNativePerformance() {
/**
* Partial implementation of the Performance interface for RN,
* corresponding to the standard in
* https://www.w3.org/TR/user-timing/#extensions-performance-interface
* https://www.w3.org/TR/user-timing/#extensions-performance-interface
*/
export default class Performance {
eventCounts: EventCounts = EventCountsProxy;

mark(
markName: string,
markOptions?: PerformanceMarkOptions,
Expand Down
39 changes: 28 additions & 11 deletions Libraries/WebPerformance/PerformanceEntryReporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,19 @@ void PerformanceEntryReporter::setReportingCallback(
}

void PerformanceEntryReporter::startReporting(PerformanceEntryType entryType) {
reportingType_[static_cast<int>(entryType)] = true;
size_t entryTypeIdx = static_cast<size_t>(entryType);
reportingType_[entryTypeIdx] = true;
durationThreshold_[entryTypeIdx] = DEFAULT_DURATION_THRESHOLD;
}

void PerformanceEntryReporter::setDurationThreshold(
PerformanceEntryType entryType,
double durationThreshold) {
durationThreshold_[static_cast<size_t>(entryType)] = durationThreshold;
}

void PerformanceEntryReporter::stopReporting(PerformanceEntryType entryType) {
reportingType_[static_cast<int>(entryType)] = false;
reportingType_[static_cast<size_t>(entryType)] = false;
}

GetPendingEntriesResult PerformanceEntryReporter::popPendingEntries() {
Expand All @@ -47,7 +56,16 @@ GetPendingEntriesResult PerformanceEntryReporter::popPendingEntries() {
}

void PerformanceEntryReporter::logEntry(const RawPerformanceEntry &entry) {
if (!isReportingType(static_cast<PerformanceEntryType>(entry.entryType))) {
if (entry.entryType == PerformanceEntryType::EVENT) {
eventCounts_[entry.name]++;
}

if (!isReportingType(entry.entryType)) {
return;
}

if (entry.duration < durationThreshold_[entry.entryType]) {
// The entries duration is lower than the desired reporting threshold, skip
return;
}

Expand Down Expand Up @@ -94,7 +112,7 @@ void PerformanceEntryReporter::mark(

logEntry(
{name,
static_cast<int>(PerformanceEntryType::MARK),
PerformanceEntryType::MARK,
startTime,
duration,
std::nullopt,
Expand All @@ -108,13 +126,13 @@ void PerformanceEntryReporter::clearMarks(
PerformanceMark mark{{*markName, 0}};
marksRegistry_.erase(&mark);
clearEntries([&markName](const RawPerformanceEntry &entry) {
return entry.entryType == static_cast<int>(PerformanceEntryType::MARK) &&
return entry.entryType == PerformanceEntryType::MARK &&
entry.name == markName;
});
} else {
marksRegistry_.clear();
clearEntries([](const RawPerformanceEntry &entry) {
return entry.entryType == static_cast<int>(PerformanceEntryType::MARK);
return entry.entryType == PerformanceEntryType::MARK;
});
}
}
Expand All @@ -131,7 +149,7 @@ void PerformanceEntryReporter::measure(
double durationVal = duration ? *duration : endTimeVal - startTimeVal;
logEntry(
{name,
static_cast<int>(PerformanceEntryType::MEASURE),
PerformanceEntryType::MEASURE,
startTimeVal,
durationVal,
std::nullopt,
Expand All @@ -143,14 +161,13 @@ void PerformanceEntryReporter::clearMeasures(
const std::optional<std::string> &measureName) {
if (measureName) {
clearEntries([&measureName](const RawPerformanceEntry &entry) {
return entry.entryType ==
static_cast<int>(PerformanceEntryType::MEASURE) &&
return entry.entryType == PerformanceEntryType::MEASURE &&
entry.name == measureName;
});
} else {
marksRegistry_.clear();
clearEntries([](const RawPerformanceEntry &entry) {
return entry.entryType == static_cast<int>(PerformanceEntryType::MEASURE);
return entry.entryType == PerformanceEntryType::MEASURE;
});
}
}
Expand All @@ -175,7 +192,7 @@ void PerformanceEntryReporter::event(
uint32_t interactionId) {
logEntry(
{std::move(name),
static_cast<int>(PerformanceEntryType::EVENT),
PerformanceEntryType::EVENT,
startTime,
duration,
processingStart,
Expand Down
Loading