Performance.measure: handle the case with a single startMark argument correctly (#37888)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/37888

# Changelog:
[Internal] -

There was one particular permutation of input arguments to `Performance.measure` that wasn't handled correctly on the native side, namely when there is only the start mark argument present, but not the end time/mark, e.g.:
```
Performance.measure('myMeasure', 'someStartMark');
```

In this case, [according to the standard](https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure), the end time should be taken as the current one:
> The end timestamp is one of:
> ...
>  -the value returned by Performance.now(), if no end mark is specified or can be determined from other values.

It was taken as 0 instead, making the total duration negative and consequently getting it filtered out by the default `durationThreshold` of 0.

I've added a corresponding missing clause in the native unit tests. This also required a slight extension to the `PerformanceObserver` API to allow for mocking the current timestamp provider.

Reviewed By: rubennorte

Differential Revision: D46728261

fbshipit-source-id: bd904d9c93707fa04c1a0ddb30802691e253c106
This commit is contained in:
Ruslan Shestopalyuk
2023-06-14 11:20:10 -07:00
committed by Facebook GitHub Bot
parent 377a8b70ee
commit 44755726ed
3 changed files with 34 additions and 6 deletions
@@ -15,10 +15,6 @@
namespace facebook::react {
EventTag PerformanceEntryReporter::sCurrentEventTag_{0};
static inline double getCurrentTimeStamp() {
return JSExecutor::performanceNow();
}
PerformanceEntryReporter &PerformanceEntryReporter::getInstance() {
static PerformanceEntryReporter instance;
return instance;
@@ -29,11 +25,17 @@ PerformanceEntryReporter::PerformanceEntryReporter() {
// sure that marks can be referenced by measures
getBuffer(PerformanceEntryType::MARK).hasNameLookup = true;
}
void PerformanceEntryReporter::setReportingCallback(
std::optional<AsyncCallback<>> callback) {
callback_ = callback;
}
double PerformanceEntryReporter::getCurrentTimeStamp() const {
return timeStampProvider_ != nullptr ? timeStampProvider_()
: JSExecutor::performanceNow();
}
void PerformanceEntryReporter::startReporting(PerformanceEntryType entryType) {
auto &buffer = getBuffer(entryType);
buffer.isReporting = true;
@@ -219,7 +221,15 @@ void PerformanceEntryReporter::measure(
const std::optional<std::string> &endMark) {
double startTimeVal = startMark ? getMarkTime(*startMark) : startTime;
double endTimeVal = endMark ? getMarkTime(*endMark) : endTime;
if (!endMark && endTime < startTimeVal) {
// The end time is not specified, take the current time, according to the
// standard
endTimeVal = getCurrentTimeStamp();
}
double durationVal = duration ? *duration : endTimeVal - startTimeVal;
logEntry(
{name,
static_cast<int>(PerformanceEntryType::MEASURE),
@@ -147,6 +147,10 @@ class PerformanceEntryReporter : public EventLogger {
return eventCounts_;
}
void setTimeStampProvider(std::function<double()> provider) {
timeStampProvider_ = provider;
}
private:
std::optional<AsyncCallback<>> callback_;
@@ -171,6 +175,8 @@ class PerformanceEntryReporter : public EventLogger {
std::unordered_map<EventTag, EventEntry> eventsInFlight_;
std::mutex eventsInFlightMutex_;
std::function<double()> timeStampProvider_ = nullptr;
static EventTag sCurrentEventTag_;
PerformanceEntryReporter();
@@ -182,6 +188,8 @@ class PerformanceEntryReporter : public EventLogger {
PerformanceEntryType entryType,
const char *entryName,
std::vector<RawPerformanceEntry> &res) const;
double getCurrentTimeStamp() const;
};
} // namespace facebook::react
@@ -146,8 +146,11 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestReportMeasures) {
reporter.measure("measure3", 0.0, 0.0, 5.0, "mark1");
reporter.measure("measure4", 1.5, 0.0, std::nullopt, std::nullopt, "mark2");
reporter.setTimeStampProvider([]() { return 3.5; });
reporter.measure("measure5", 0.0, 0.0, std::nullopt, "mark2");
reporter.mark("mark3", 2.0);
reporter.measure("measure5", 2.0, 2.0);
reporter.measure("measure6", 2.0, 2.0);
reporter.mark("mark4", 2.0);
auto res = reporter.popPendingEntries();
@@ -226,13 +229,20 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestReportMeasures) {
std::nullopt,
std::nullopt,
std::nullopt},
{"measure5",
{"measure6",
static_cast<int>(PerformanceEntryType::MEASURE),
2.0,
0.0,
std::nullopt,
std::nullopt,
std::nullopt},
{"measure5",
static_cast<int>(PerformanceEntryType::MEASURE),
2.0,
1.5,
std::nullopt,
std::nullopt,
std::nullopt},
};
ASSERT_EQ(expected, entries);