Skip to content

Commit afe6521

Browse files
rshestpull[bot]
authored andcommitted
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
1 parent 9d4d118 commit afe6521

File tree

5 files changed

+90
-10
lines changed

5 files changed

+90
-10
lines changed

Libraries/WebPerformance/NativePerformanceObserver.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,13 @@ void NativePerformanceObserver::setOnPerformanceEntryCallback(
3939
<< (callback ? "non-empty" : "empty");
4040
}
4141

42+
void NativePerformanceObserver::logEntryForDebug(
43+
jsi::Runtime &rt,
44+
RawPerformanceEntry entry) {
45+
LOG(INFO) << "NativePerformanceObserver::logEntry: "
46+
<< "name=" << entry.name << " type=" << entry.entryType
47+
<< " startTime=" << entry.startTime
48+
<< " duration=" << entry.duration;
49+
}
50+
4251
} // namespace facebook::react

Libraries/WebPerformance/NativePerformanceObserver.h

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class NativePerformanceObserver
5757
jsi::Runtime &rt,
5858
std::optional<AsyncCallback<>> callback);
5959

60+
void logEntryForDebug(jsi::Runtime &rt, RawPerformanceEntry entry);
61+
6062
private:
6163
std::optional<AsyncCallback<>> callback_;
6264
};

Libraries/WebPerformance/NativePerformanceObserver.js

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
1414

1515
export const RawPerformanceEntryTypeValues = {
1616
UNDEFINED: 0,
17+
MARK: 1,
1718
};
1819

1920
export type RawPerformanceEntryType = number;
@@ -34,6 +35,9 @@ export interface Spec extends TurboModule {
3435
+stopReporting: (entryType: string) => void;
3536
+getPendingEntries: () => $ReadOnlyArray<RawPerformanceEntry>;
3637
+setOnPerformanceEntryCallback: (callback?: () => void) => void;
38+
39+
// NOTE: this is for dev-only purposes (potentially is going to be moved elsewhere)
40+
+logEntryForDebug?: (entry: RawPerformanceEntry) => void;
3741
}
3842

3943
export default (TurboModuleRegistry.get<Spec>(
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow strict
9+
*/
10+
11+
import type {HighResTimeStamp} from './PerformanceObserver';
12+
13+
import NativePerformanceObserver, {
14+
RawPerformanceEntryTypeValues,
15+
} from './NativePerformanceObserver';
16+
import {PerformanceEntry} from './PerformanceObserver';
17+
18+
type DetailType = mixed;
19+
20+
export type PerformanceMarkOptions = {
21+
detail?: DetailType,
22+
startTime?: HighResTimeStamp,
23+
};
24+
25+
function getCurrentTimeStamp(): HighResTimeStamp {
26+
return global.nativePerformanceNow?.() ?? Date.now();
27+
}
28+
29+
export class PerformanceMark extends PerformanceEntry {
30+
detail: DetailType;
31+
32+
constructor(markName: string, markOptions?: PerformanceMarkOptions) {
33+
let startTime = markOptions?.startTime ?? getCurrentTimeStamp();
34+
super({name: markName, entryType: 'mark', startTime, duration: 0});
35+
if (markOptions !== undefined) {
36+
this.detail = markOptions.detail;
37+
}
38+
}
39+
}
40+
41+
/**
42+
* Partial implementation of the Performance interface for RN,
43+
* corresponding to the standard in
44+
* https://www.w3.org/TR/user-timing/#extensions-performance-interface
45+
*/
46+
export default class Performance {
47+
mark(
48+
markName: string,
49+
markOptions?: PerformanceMarkOptions,
50+
): PerformanceMark {
51+
const mark = new PerformanceMark(markName, markOptions);
52+
NativePerformanceObserver?.logEntryForDebug?.({
53+
name: markName,
54+
entryType: RawPerformanceEntryTypeValues.MARK,
55+
startTime: mark.startTime,
56+
duration: mark.duration,
57+
});
58+
return mark;
59+
}
60+
61+
clearMarks(markName?: string): void {}
62+
63+
now(): HighResTimeStamp {
64+
return getCurrentTimeStamp();
65+
}
66+
}

Libraries/WebPerformance/PerformanceObserver.js

+9-10
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ import NativePerformanceObserver from './NativePerformanceObserver';
1818

1919
export type HighResTimeStamp = number;
2020
// TODO: Extend once new types (such as event) are supported.
21-
// TODO: Get rid of the "undefined" once there is at least one type supported.
22-
export type PerformanceEntryType = 'undefined';
21+
export type PerformanceEntryType = 'undefined' | 'mark';
2322

2423
export class PerformanceEntry {
2524
name: string;
@@ -53,7 +52,7 @@ export class PerformanceEntry {
5352
function rawToPerformanceEntryType(
5453
type: RawPerformanceEntryType,
5554
): PerformanceEntryType {
56-
return 'undefined';
55+
return 'mark';
5756
}
5857

5958
function rawToPerformanceEntry(entry: RawPerformanceEntry): PerformanceEntry {
@@ -166,15 +165,15 @@ export default class PerformanceObserver {
166165
} else {
167166
this._entryTypes = new Set([options.type]);
168167
}
169-
this._entryTypes.forEach(type => {
168+
for (const type of this._entryTypes) {
170169
if (!_observedEntryTypeRefCount.has(type)) {
171170
NativePerformanceObserver.startReporting(type);
172171
}
173172
_observedEntryTypeRefCount.set(
174173
type,
175174
(_observedEntryTypeRefCount.get(type) ?? 0) + 1,
176175
);
177-
});
176+
}
178177
_observers.add(this);
179178
}
180179

@@ -183,15 +182,15 @@ export default class PerformanceObserver {
183182
warnNoNativePerformanceObserver();
184183
return;
185184
}
186-
this._entryTypes.forEach(type => {
185+
for (const type of this._entryTypes) {
187186
const entryTypeRefCount = _observedEntryTypeRefCount.get(type) ?? 0;
188187
if (entryTypeRefCount === 1) {
189188
_observedEntryTypeRefCount.delete(type);
190189
NativePerformanceObserver.stopReporting(type);
191190
} else if (entryTypeRefCount !== 0) {
192191
_observedEntryTypeRefCount.set(type, entryTypeRefCount - 1);
193192
}
194-
});
193+
}
195194
_observers.delete(this);
196195
if (_observers.size === 0) {
197196
NativePerformanceObserver.setOnPerformanceEntryCallback(undefined);
@@ -201,7 +200,7 @@ export default class PerformanceObserver {
201200

202201
static supportedEntryTypes: $ReadOnlyArray<PerformanceEntryType> =
203202
// TODO: add types once they are fully supported
204-
Object.freeze([]);
203+
Object.freeze(['mark']);
205204
}
206205

207206
// This is a callback that gets scheduled and periodically called from the native side
@@ -211,13 +210,13 @@ function onPerformanceEntry() {
211210
}
212211
const rawEntries = NativePerformanceObserver.getPendingEntries();
213212
const entries = rawEntries.map(rawToPerformanceEntry);
214-
_observers.forEach(observer => {
213+
for (const observer of _observers) {
215214
const entriesForObserver: PerformanceEntryList = entries.filter(entry =>
216215
observer._entryTypes.has(entry.entryType),
217216
);
218217
observer._callback(
219218
new PerformanceObserverEntryList(entriesForObserver),
220219
observer,
221220
);
222-
});
221+
}
223222
}

0 commit comments

Comments
 (0)