Skip to content

Commit ce43342

Browse files
hoxyqfacebook-github-bot
authored andcommitted
Validate marks presense, if specified (#51389)
Summary: Pull Request resolved: #51389 # Changelog: [Internal] This is the pre-requisite for the diff on top, which migrates performance-related classes to start using `HighResTimeStamp`. We should be throwing `SyntaxError` if the specified mark name is not buffered. Like Chromium does: {F1978032319} In this diff: - `PerformanceEntryReporter::getMarkTime` is now public, ca be called by `NativePerformance` and returns `std::optional`. - `NativePerformance` is responsible for validating that marks are present in the buffer, if their names are specified in `.measure()` call. - Mark names take precedence over `startTime` and `endTime` values, so if they are specified and not available, then we will throw Error over `jsi` that will be catched on JavaScript side in `Performance.js`. Reviewed By: rubennorte Differential Revision: D74837812 fbshipit-source-id: ca2ba198c4c9e6e2d8d37f852affea667f1c174c
1 parent 5ac46bb commit ce43342

File tree

6 files changed

+113
-75
lines changed

6 files changed

+113
-75
lines changed

packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,46 @@ double NativePerformance::markWithResult(
130130
}
131131

132132
std::tuple<double, double> NativePerformance::measureWithResult(
133-
jsi::Runtime& rt,
133+
jsi::Runtime& runtime,
134134
std::string name,
135135
double startTime,
136136
double endTime,
137137
std::optional<double> duration,
138138
std::optional<std::string> startMark,
139139
std::optional<std::string> endMark) {
140-
auto entry = PerformanceEntryReporter::getInstance()->reportMeasure(
141-
name, startTime, endTime, duration, startMark, endMark);
140+
auto reporter = PerformanceEntryReporter::getInstance();
141+
142+
DOMHighResTimeStamp startTimeValue = startTime;
143+
// If the start time mark name is specified, it takes precedence over the
144+
// startTime parameter, which can be set to 0 by default from JavaScript.
145+
if (startMark) {
146+
if (auto startMarkBufferedTime = reporter->getMarkTime(*startMark)) {
147+
startTimeValue = *startMarkBufferedTime;
148+
} else {
149+
throw jsi::JSError(
150+
runtime, "The mark '" + *startMark + "' does not exist.");
151+
}
152+
}
153+
154+
DOMHighResTimeStamp endTimeValue = endTime;
155+
// If the end time mark name is specified, it takes precedence over the
156+
// startTime parameter, which can be set to 0 by default from JavaScript.
157+
if (endMark) {
158+
if (auto endMarkBufferedTime = reporter->getMarkTime(*endMark)) {
159+
endTimeValue = *endMarkBufferedTime;
160+
} else {
161+
throw jsi::JSError(
162+
runtime, "The mark '" + *endMark + "' does not exist.");
163+
}
164+
} else if (duration) {
165+
endTimeValue = startTimeValue + *duration;
166+
} else if (endTimeValue < startTimeValue) {
167+
// The end time is not specified, take the current time, according to the
168+
// standard
169+
endTimeValue = reporter->getCurrentTimeStamp();
170+
}
171+
172+
auto entry = reporter->reportMeasure(name, startTime, endTime);
142173
return std::tuple{entry.startTime, entry.duration};
143174
}
144175

packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -199,28 +199,14 @@ PerformanceMeasure PerformanceEntryReporter::reportMeasure(
199199
const std::string& name,
200200
DOMHighResTimeStamp startTime,
201201
DOMHighResTimeStamp endTime,
202-
const std::optional<DOMHighResTimeStamp>& duration,
203-
const std::optional<std::string>& startMark,
204-
const std::optional<std::string>& endMark,
205202
const std::optional<jsinspector_modern::DevToolsTrackEntryPayload>&
206203
trackMetadata) {
207-
DOMHighResTimeStamp startTimeVal =
208-
startMark ? getMarkTime(*startMark) : startTime;
209-
DOMHighResTimeStamp endTimeVal = endMark ? getMarkTime(*endMark) : endTime;
210-
211-
if (!endMark && endTime < startTimeVal) {
212-
// The end time is not specified, take the current time, according to the
213-
// standard
214-
endTimeVal = getCurrentTimeStamp();
215-
}
216-
217-
DOMHighResTimeStamp durationVal =
218-
duration ? *duration : endTimeVal - startTimeVal;
204+
DOMHighResTimeStamp duration = endTime - startTime;
219205

220206
const auto entry = PerformanceMeasure{
221207
{.name = std::string(name),
222-
.startTime = startTimeVal,
223-
.duration = durationVal}};
208+
.startTime = startTime,
209+
.duration = duration}};
224210

225211
traceMeasure(entry);
226212

@@ -235,16 +221,16 @@ PerformanceMeasure PerformanceEntryReporter::reportMeasure(
235221
return entry;
236222
}
237223

238-
DOMHighResTimeStamp PerformanceEntryReporter::getMarkTime(
224+
std::optional<DOMHighResTimeStamp> PerformanceEntryReporter::getMarkTime(
239225
const std::string& markName) const {
240226
std::shared_lock lock(buffersMutex_);
241227

242228
if (auto it = markBuffer_.find(markName); it) {
243229
return std::visit(
244230
[](const auto& entryData) { return entryData.startTime; }, *it);
245-
} else {
246-
return 0.0;
247231
}
232+
233+
return std::nullopt;
248234
}
249235

250236
void PerformanceEntryReporter::reportEvent(

packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ class PerformanceEntryReporter {
8080
return eventCounts_;
8181
}
8282

83+
std::optional<double> getMarkTime(const std::string& markName) const;
84+
8385
PerformanceMark reportMark(
8486
const std::string& name,
8587
const std::optional<DOMHighResTimeStamp>& startTime = std::nullopt);
@@ -88,9 +90,6 @@ class PerformanceEntryReporter {
8890
const std::string& name,
8991
double startTime,
9092
double endTime,
91-
const std::optional<double>& duration = std::nullopt,
92-
const std::optional<std::string>& startMark = std::nullopt,
93-
const std::optional<std::string>& endMark = std::nullopt,
9493
const std::optional<jsinspector_modern::DevToolsTrackEntryPayload>&
9594
trackMetadata = std::nullopt);
9695

@@ -129,8 +128,6 @@ class PerformanceEntryReporter {
129128

130129
std::function<double()> timeStampProvider_ = nullptr;
131130

132-
double getMarkTime(const std::string& markName) const;
133-
134131
const inline PerformanceEntryBuffer& getBuffer(
135132
PerformanceEntryType entryType) const {
136133
switch (entryType) {

packages/react-native/ReactCommon/react/performance/timeline/tests/PerformanceEntryReporterTest.cpp

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -108,38 +108,21 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestReportMeasures) {
108108
reporter->reportMark("mark2", 2);
109109

110110
reporter->reportMeasure("measure0", 0, 2);
111-
reporter->reportMeasure("measure1", 0, 2, 4);
112-
reporter->reportMeasure("measure2", 0, 0, std::nullopt, "mark1", "mark2");
113-
reporter->reportMeasure("measure3", 0, 0, 5, "mark1");
114-
reporter->reportMeasure(
115-
"measure4", 1.5, 0, std::nullopt, std::nullopt, "mark2");
116-
117-
reporter->setTimeStampProvider([]() { return 3.5; });
118-
reporter->reportMeasure("measure5", 0, 0, std::nullopt, "mark2");
111+
reporter->reportMeasure("measure1", 0, 3);
119112

120113
reporter->reportMark("mark3", 2.5);
121-
reporter->reportMeasure("measure6", 2.0, 2.0);
122-
reporter->reportMark("mark4", 2.1);
114+
reporter->reportMeasure("measure2", 2.0, 2.0);
123115
reporter->reportMark("mark4", 3.0);
124-
// Uses the last reported time for mark4
125-
reporter->reportMeasure("measure7", 0, 0, std::nullopt, "mark1", "mark4");
126116

127117
const auto entries = toSorted(reporter->getEntries());
128118

129119
const std::vector<PerformanceEntry> expected = {
130120
PerformanceMark{{.name = "mark0", .startTime = 0, .duration = 0}},
131121
PerformanceMeasure{{.name = "measure0", .startTime = 0, .duration = 2}},
132-
PerformanceMeasure{{.name = "measure1", .startTime = 0, .duration = 4}},
122+
PerformanceMeasure{{.name = "measure1", .startTime = 0, .duration = 3}},
133123
PerformanceMark{{.name = "mark1", .startTime = 1, .duration = 0}},
134-
PerformanceMeasure{{.name = "measure2", .startTime = 1, .duration = 1}},
135-
PerformanceMeasure{{.name = "measure7", .startTime = 1, .duration = 2}},
136-
PerformanceMeasure{{.name = "measure3", .startTime = 1, .duration = 5}},
137-
PerformanceMeasure{
138-
{.name = "measure4", .startTime = 1.5, .duration = 0.5}},
139124
PerformanceMark{{.name = "mark2", .startTime = 2, .duration = 0}},
140-
PerformanceMeasure{{.name = "measure6", .startTime = 2, .duration = 0}},
141-
PerformanceMeasure{{.name = "measure5", .startTime = 2, .duration = 1.5}},
142-
PerformanceMark{{.name = "mark4", .startTime = 2.1, .duration = 0}},
125+
PerformanceMeasure{{.name = "measure2", .startTime = 2, .duration = 0}},
143126
PerformanceMark{{.name = "mark3", .startTime = 2.5, .duration = 0}},
144127
PerformanceMark{{.name = "mark4", .startTime = 3, .duration = 0}}};
145128

@@ -160,24 +143,21 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestGetEntries) {
160143
reporter->reportMark("mark2", 2);
161144

162145
reporter->reportMeasure("common_name", 0, 2);
163-
reporter->reportMeasure("measure1", 0, 2, 4);
164-
reporter->reportMeasure("measure2", 0, 0, std::nullopt, "mark1", "mark2");
165-
reporter->reportMeasure("measure3", 0, 0, 5, "mark1");
166-
reporter->reportMeasure(
167-
"measure4", 1.5, 0, std::nullopt, std::nullopt, "mark2");
146+
reporter->reportMeasure("measure1", 0, 3);
147+
reporter->reportMeasure("measure2", 1, 6);
148+
reporter->reportMeasure("measure3", 1.5, 2);
168149

169150
{
170151
const auto allEntries = toSorted(reporter->getEntries());
171152
const std::vector<PerformanceEntry> expected = {
172153
PerformanceMark{{.name = "common_name", .startTime = 0, .duration = 0}},
173154
PerformanceMeasure{
174155
{.name = "common_name", .startTime = 0, .duration = 2}},
175-
PerformanceMeasure{{.name = "measure1", .startTime = 0, .duration = 4}},
156+
PerformanceMeasure{{.name = "measure1", .startTime = 0, .duration = 3}},
176157
PerformanceMark{{.name = "mark1", .startTime = 1, .duration = 0}},
177-
PerformanceMeasure{{.name = "measure2", .startTime = 1, .duration = 1}},
178-
PerformanceMeasure{{.name = "measure3", .startTime = 1, .duration = 5}},
158+
PerformanceMeasure{{.name = "measure2", .startTime = 1, .duration = 5}},
179159
PerformanceMeasure{
180-
{.name = "measure4", .startTime = 1.5, .duration = 0.5}},
160+
{.name = "measure3", .startTime = 1.5, .duration = 0.5}},
181161
PerformanceMark{{.name = "mark2", .startTime = 2, .duration = 0}}};
182162
ASSERT_EQ(expected, allEntries);
183163
}
@@ -198,11 +178,10 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestGetEntries) {
198178
const std::vector<PerformanceEntry> expected = {
199179
PerformanceMeasure{
200180
{.name = "common_name", .startTime = 0, .duration = 2}},
201-
PerformanceMeasure{{.name = "measure1", .startTime = 0, .duration = 4}},
202-
PerformanceMeasure{{.name = "measure2", .startTime = 1, .duration = 1}},
203-
PerformanceMeasure{{.name = "measure3", .startTime = 1, .duration = 5}},
181+
PerformanceMeasure{{.name = "measure1", .startTime = 0, .duration = 3}},
182+
PerformanceMeasure{{.name = "measure2", .startTime = 1, .duration = 5}},
204183
PerformanceMeasure{
205-
{.name = "measure4", .startTime = 1.5, .duration = 0.5}}};
184+
{.name = "measure3", .startTime = 1.5, .duration = 0.5}}};
206185
ASSERT_EQ(expected, measures);
207186
}
208187

@@ -233,11 +212,9 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestClearMarks) {
233212
reporter->reportMark("mark2", 2);
234213

235214
reporter->reportMeasure("common_name", 0, 2);
236-
reporter->reportMeasure("measure1", 0, 2, 4);
237-
reporter->reportMeasure("measure2", 0, 0, std::nullopt, "mark1", "mark2");
238-
reporter->reportMeasure("measure3", 0, 0, 5, "mark1");
239-
reporter->reportMeasure(
240-
"measure4", 1.5, 0, std::nullopt, std::nullopt, "mark2");
215+
reporter->reportMeasure("measure1", 0, 3);
216+
reporter->reportMeasure("measure2", 1, 6);
217+
reporter->reportMeasure("measure3", 1.5, 2);
241218

242219
reporter->clearEntries(PerformanceEntryType::MARK, "common_name");
243220

packages/react-native/src/private/webapis/performance/Performance.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type {
1717
} from './PerformanceEntry';
1818
import type {DetailType, PerformanceMarkOptions} from './UserTiming';
1919

20+
import DOMException from '../errors/DOMException';
2021
import {setPlatformObject} from '../webidl/PlatformObjects';
2122
import {EventCounts} from './EventTiming';
2223
import {
@@ -192,15 +193,22 @@ export default class Performance {
192193
let computedDuration = duration;
193194

194195
if (NativePerformance?.measureWithResult) {
195-
[computedStartTime, computedDuration] =
196-
NativePerformance.measureWithResult(
197-
measureName,
198-
startTime,
199-
endTime,
200-
duration,
201-
startMarkName,
202-
endMarkName,
196+
try {
197+
[computedStartTime, computedDuration] =
198+
NativePerformance.measureWithResult(
199+
measureName,
200+
startTime,
201+
endTime,
202+
duration,
203+
startMarkName,
204+
endMarkName,
205+
);
206+
} catch (error) {
207+
throw new DOMException(
208+
"Failed to execute 'measure' on 'Performance': " + error.message,
209+
'SyntaxError',
203210
);
211+
}
204212
} else {
205213
warnNoNativePerformance();
206214
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
* @flow strict-local
8+
* @format
9+
*/
10+
11+
import type Performance from '../Performance';
12+
13+
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
14+
15+
declare var performance: Performance;
16+
17+
describe('Performance', () => {
18+
it('measure validates mark names presence in the buffer, if specified', () => {
19+
expect(() => {
20+
performance.measure('measure', 'start', 'end');
21+
}).toThrow(
22+
"Failed to execute 'measure' on 'Performance': The mark 'start' does not exist.",
23+
); // This should also check that Error is an instance of DOMException and is SyntaxError,
24+
// but toThrow checked currently only supports string argument.
25+
26+
performance.mark('start');
27+
expect(() => {
28+
performance.measure('measure', 'start', 'end');
29+
}).toThrow(
30+
"Failed to execute 'measure' on 'Performance': The mark 'end' does not exist.",
31+
); // This should also check that Error is an instance of DOMException and is SyntaxError,
32+
// but toThrow checked currently only supports string argument.
33+
34+
performance.mark('end');
35+
expect(() => {
36+
performance.measure('measure', 'start', 'end');
37+
}).not.toThrow();
38+
});
39+
});

0 commit comments

Comments
 (0)