Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to always log certain performance entry types #36820

Closed
wants to merge 1 commit into from
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ void NativePerformanceObserver::stopReporting(
static_cast<PerformanceEntryType>(entryType));
}

void NativePerformanceObserver::setIsBuffered(
jsi::Runtime &rt,
std::vector<int32_t> entryTypes,
bool isBuffered) {
for (const int32_t entryType : entryTypes) {
PerformanceEntryReporter::getInstance().setAlwaysLogged(
static_cast<PerformanceEntryType>(entryType), isBuffered);
}
}

GetPendingEntriesResult NativePerformanceObserver::popPendingEntries(
jsi::Runtime &rt) {
return PerformanceEntryReporter::getInstance().popPendingEntries();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ class NativePerformanceObserver

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

void setIsBuffered(
jsi::Runtime &rt,
std::vector<int32_t> entryTypes,
bool isBuffered);

GetPendingEntriesResult popPendingEntries(jsi::Runtime &rt);

void setOnPerformanceEntryCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export type GetPendingEntriesResult = {|
export interface Spec extends TurboModule {
+startReporting: (entryType: RawPerformanceEntryType) => void;
+stopReporting: (entryType: RawPerformanceEntryType) => void;
+setIsBuffered: (
entryTypes: $ReadOnlyArray<RawPerformanceEntryType>,
isBuffered: boolean,
) => void;
+popPendingEntries: () => GetPendingEntriesResult;
+setOnPerformanceEntryCallback: (callback?: () => void) => void;
+logRawEntry: (entry: RawPerformanceEntry) => void;
Expand Down
34 changes: 23 additions & 11 deletions packages/react-native/Libraries/WebPerformance/Performance.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import EventCounts from './EventCounts';
import MemoryInfo from './MemoryInfo';
import NativePerformance from './NativePerformance';
import NativePerformanceObserver from './NativePerformanceObserver';
import {PerformanceEntry} from './PerformanceEntry';
import {ALWAYS_LOGGED_ENTRY_TYPES, PerformanceEntry} from './PerformanceEntry';
import {warnNoNativePerformanceObserver} from './PerformanceObserver';
import {
performanceEntryTypeToRaw,
Expand All @@ -43,6 +43,15 @@ const getCurrentTimeStamp: () => HighResTimeStamp = global.nativePerformanceNow
? global.nativePerformanceNow
: () => Date.now();

// We want some of the performance entry types to be always logged,
// even if they are not currently observed - this is either to be able to
// retrieve them at any time via Performance.getEntries* or to refer by other entries
// (such as when measures may refer to marks, even if the latter are not observed)
NativePerformanceObserver?.setIsBuffered(
ALWAYS_LOGGED_ENTRY_TYPES.map(performanceEntryTypeToRaw),
true,
);

export class PerformanceMark extends PerformanceEntry {
detail: DetailType;

Expand Down Expand Up @@ -261,22 +270,24 @@ export default class Performance {
* https://www.w3.org/TR/performance-timeline/#extensions-to-the-performance-interface
*/
getEntries(): PerformanceEntryList {
if (!NativePerformanceObserver?.clearEntries) {
if (!NativePerformanceObserver?.getEntries) {
warnNoNativePerformanceObserver();
return [];
}
return NativePerformanceObserver.getEntries().map(rawToPerformanceEntry);
}

getEntriesByType(entryType: PerformanceEntryType): PerformanceEntryList {
if (entryType !== 'mark' && entryType !== 'measure') {
console.log(
`Performance.getEntriesByType: Only valid for 'mark' and 'measure' entry types, got ${entryType}`,
if (!ALWAYS_LOGGED_ENTRY_TYPES.includes(entryType)) {
console.warn(
`Performance.getEntriesByType: Only valid for ${JSON.stringify(
ALWAYS_LOGGED_ENTRY_TYPES,
)} entry types, got ${entryType}`,
);
return [];
}

if (!NativePerformanceObserver?.clearEntries) {
if (!NativePerformanceObserver?.getEntries) {
warnNoNativePerformanceObserver();
return [];
}
Expand All @@ -291,16 +302,17 @@ export default class Performance {
): PerformanceEntryList {
if (
entryType !== undefined &&
entryType !== 'mark' &&
entryType !== 'measure'
!ALWAYS_LOGGED_ENTRY_TYPES.includes(entryType)
) {
console.log(
`Performance.getEntriesByName: Only valid for 'mark' and 'measure' entry types, got ${entryType}`,
console.warn(
`Performance.getEntriesByName: Only valid for ${JSON.stringify(
ALWAYS_LOGGED_ENTRY_TYPES,
)} entry types, got ${entryType}`,
);
return [];
}

if (!NativePerformanceObserver?.clearEntries) {
if (!NativePerformanceObserver?.getEntries) {
warnNoNativePerformanceObserver();
return [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
export type HighResTimeStamp = number;
export type PerformanceEntryType = 'mark' | 'measure' | 'event';

export const ALWAYS_LOGGED_ENTRY_TYPES: $ReadOnlyArray<PerformanceEntryType> = [
'mark',
'measure',
];

export class PerformanceEntry {
name: string;
entryType: PerformanceEntryType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ void PerformanceEntryReporter::startReporting(PerformanceEntryType entryType) {
buffer.durationThreshold = DEFAULT_DURATION_THRESHOLD;
}

void PerformanceEntryReporter::setAlwaysLogged(
PerformanceEntryType entryType,
bool isAlwaysLogged) {
auto &buffer = getBuffer(entryType);
buffer.isAlwaysLogged = isAlwaysLogged;
}

void PerformanceEntryReporter::setDurationThreshold(
PerformanceEntryType entryType,
double durationThreshold) {
Expand Down Expand Up @@ -82,7 +89,7 @@ void PerformanceEntryReporter::logEntry(const RawPerformanceEntry &entry) {
eventCounts_[entry.name]++;
}

if (!isReporting(entryType)) {
if (!isReporting(entryType) && !isAlwaysLogged(entryType)) {
return;
}

Expand Down Expand Up @@ -328,7 +335,7 @@ static const SupportedEventTypeRegistry &getSupportedEvents() {
}

EventTag PerformanceEntryReporter::onEventStart(const char *name) {
if (!isReportingEvents()) {
if (!isReporting(PerformanceEntryType::EVENT)) {
return 0;
}
const auto &supportedEvents = getSupportedEvents();
Expand All @@ -355,7 +362,7 @@ EventTag PerformanceEntryReporter::onEventStart(const char *name) {
}

void PerformanceEntryReporter::onEventDispatch(EventTag tag) {
if (!isReportingEvents() || tag == 0) {
if (!isReporting(PerformanceEntryType::EVENT) || tag == 0) {
return;
}
auto timeStamp = JSExecutor::performanceNow();
Expand All @@ -369,7 +376,7 @@ void PerformanceEntryReporter::onEventDispatch(EventTag tag) {
}

void PerformanceEntryReporter::onEventEnd(EventTag tag) {
if (!isReportingEvents() || tag == 0) {
if (!isReporting(PerformanceEntryType::EVENT) || tag == 0) {
return;
}
auto timeStamp = JSExecutor::performanceNow();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ constexpr size_t DEFAULT_MAX_BUFFER_SIZE = 1024;
struct PerformanceEntryBuffer {
BoundedConsumableBuffer<RawPerformanceEntry> entries{DEFAULT_MAX_BUFFER_SIZE};
bool isReporting{false};
bool isAlwaysLogged{false};
double durationThreshold{DEFAULT_DURATION_THRESHOLD};
bool hasNameLookup{false};
PerformanceEntryRegistryType nameLookup;
Expand Down Expand Up @@ -80,6 +81,7 @@ class PerformanceEntryReporter : public EventLogger {
void startReporting(PerformanceEntryType entryType);
void stopReporting(PerformanceEntryType entryType);
void stopReporting();
void setAlwaysLogged(PerformanceEntryType entryType, bool isAlwaysLogged);
void setDurationThreshold(
PerformanceEntryType entryType,
double durationThreshold);
Expand All @@ -101,8 +103,8 @@ class PerformanceEntryReporter : public EventLogger {
return getBuffer(entryType).isReporting;
}

bool isReportingEvents() const {
return isReporting(PerformanceEntryType::EVENT);
bool isAlwaysLogged(PerformanceEntryType entryType) const {
return getBuffer(entryType).isAlwaysLogged;
}

uint32_t getDroppedEntryCount() const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
import {RawPerformanceEntryTypeValues} from '../RawPerformanceEntry';

const reportingType: Set<RawPerformanceEntryType> = new Set();
const isAlwaysLogged: Set<RawPerformanceEntryType> = new Set();
const eventCounts: Map<string, number> = new Map();
const durationThresholds: Map<RawPerformanceEntryType, number> = new Map();
let entries: Array<RawPerformanceEntry> = [];
Expand All @@ -33,6 +34,19 @@ const NativePerformanceObserverMock: NativePerformanceObserver = {
durationThresholds.delete(entryType);
},

setIsBuffered: (
entryTypes: $ReadOnlyArray<RawPerformanceEntryType>,
isBuffered: boolean,
) => {
for (const entryType of entryTypes) {
if (isBuffered) {
isAlwaysLogged.add(entryType);
} else {
isAlwaysLogged.delete(entryType);
}
}
},

popPendingEntries: (): GetPendingEntriesResult => {
const res = entries;
entries = [];
Expand All @@ -47,7 +61,10 @@ const NativePerformanceObserverMock: NativePerformanceObserver = {
},

logRawEntry: (entry: RawPerformanceEntry) => {
if (reportingType.has(entry.entryType)) {
if (
reportingType.has(entry.entryType) ||
isAlwaysLogged.has(entry.entryType)
) {
const durationThreshold = durationThresholds.get(entry.entryType);
if (
durationThreshold !== undefined &&
Expand Down Expand Up @@ -81,8 +98,9 @@ const NativePerformanceObserverMock: NativePerformanceObserver = {
clearEntries: (entryType: RawPerformanceEntryType, entryName?: string) => {
entries = entries.filter(
e =>
e.entryType === entryType &&
(entryName == null || e.name === entryName),
(entryType !== RawPerformanceEntryTypeValues.UNDEFINED &&
e.entryType !== entryType) ||
(entryName != null && e.name !== entryName),
);
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,56 @@ describe('NativePerformanceObserver', () => {

NativePerformanceObserverMock.stopReporting('mark');
});

it('correctly clears/gets entries', async () => {
NativePerformanceObserverMock.logRawEntry({
name: 'mark1',
entryType: RawPerformanceEntryTypeValues.MARK,
startTime: 0,
duration: 0,
});

NativePerformanceObserverMock.logRawEntry({
name: 'event1',
entryType: RawPerformanceEntryTypeValues.EVENT,
startTime: 0,
duration: 0,
});

NativePerformanceObserverMock.clearEntries(
RawPerformanceEntryTypeValues.UNDEFINED,
);

expect(NativePerformanceObserverMock.getEntries()).toStrictEqual([]);

NativePerformanceObserverMock.logRawEntry({
name: 'entry1',
entryType: RawPerformanceEntryTypeValues.MARK,
startTime: 0,
duration: 0,
});

NativePerformanceObserverMock.logRawEntry({
name: 'entry2',
entryType: RawPerformanceEntryTypeValues.MARK,
startTime: 0,
duration: 0,
});

NativePerformanceObserverMock.logRawEntry({
name: 'entry1',
entryType: RawPerformanceEntryTypeValues.EVENT,
startTime: 0,
duration: 0,
});

NativePerformanceObserverMock.clearEntries(
RawPerformanceEntryTypeValues.UNDEFINED,
'entry1',
);

expect(
NativePerformanceObserverMock.getEntries().map(e => e.name),
).toStrictEqual(['entry2']);
});
});
Loading