Implement Performance.getEntries* W3C extension for Performance Timeline API (#36314)

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

## Changelog:

[Internal] - Added support for W3C Performance API extension (Performance,getEntries*)

See [here for the details](https://www.w3.org/TR/performance-timeline/#extensions-to-the-performance-interface).

This only supports `mark` and `measure` performance entry types (not `events`, as those are not supported by browsers either, they recommend using the `PerformanceObserver` API instead).

From the implementation perspective, we already maintained persistent buffer for `mark` entry types, which was required in order to look up measures.

The buffer is circular, limited to 1K entries by default.

I basically mimic the same behavior for `measure` entry types as well, since it's the simplest way of doing it at this point,

If we happen to want adding some other entry types that would be available via `Performance.getEntries*` API in the future, we may want to iterate on the internal data structures representation inside `PerformanceEntryReporter`, but for now I believe this approach should work just fine, and whatever changes we may want to do will be purely internal implementation detail.

Reviewed By: rubennorte

Differential Revision: D43625488

fbshipit-source-id: dd315b3f8488e910749a8e2a4158246e94d76f99
This commit is contained in:
Ruslan Shestopalyuk
2023-03-01 04:14:51 -08:00
committed by Facebook GitHub Bot
parent 3997fc1c66
commit bc56f66b8d
7 changed files with 230 additions and 14 deletions
@@ -20,6 +20,28 @@ static constexpr size_t MAX_ENTRY_BUFFER_SIZE = 1024;
namespace facebook::react {
EventTag PerformanceEntryReporter::sCurrentEventTag_{0};
RawPerformanceEntry PerformanceMark::toRawPerformanceEntry() const {
return {
name,
static_cast<int>(PerformanceEntryType::MARK),
timeStamp,
0.0,
std::nullopt,
std::nullopt,
std::nullopt};
}
RawPerformanceEntry PerformanceMeasure::toRawPerformanceEntry() const {
return {
name,
static_cast<int>(PerformanceEntryType::MEASURE),
timeStamp,
duration,
std::nullopt,
std::nullopt,
std::nullopt};
}
PerformanceEntryReporter &PerformanceEntryReporter::getInstance() {
static PerformanceEntryReporter instance;
return instance;
@@ -97,6 +119,7 @@ void PerformanceEntryReporter::mark(
// it to a circular buffer:
PerformanceMark &mark = marksBuffer_[marksBufferPosition_];
marksBufferPosition_ = (marksBufferPosition_ + 1) % marksBuffer_.size();
marksCount_ = std::min(marksBuffer_.size(), marksCount_ + 1);
if (!mark.name.empty()) {
// Drop off the oldest mark out of the queue, but only if that's indeed the
@@ -124,10 +147,24 @@ void PerformanceEntryReporter::mark(
void PerformanceEntryReporter::clearEntries(
PerformanceEntryType entryType,
const char *entryName) {
if (entryName != nullptr && entryType == PerformanceEntryType::MARK) {
// remove a named mark from the mark/measure registry
PerformanceMark mark{{entryName, 0}};
marksRegistry_.erase(&mark);
if (entryType == PerformanceEntryType::MARK) {
if (entryName != nullptr) {
// remove a named mark from the mark/measure registry
PerformanceMark mark{{entryName, 0}};
marksRegistry_.erase(&mark);
clearCircularBuffer(
marksBuffer_, marksCount_, marksBufferPosition_, entryName);
} else {
marksCount_ = 0;
}
} else if (entryType == PerformanceEntryType::MEASURE) {
if (entryName != nullptr) {
clearCircularBuffer(
measuresBuffer_, measuresCount_, measuresBufferPosition_, entryName);
} else {
measuresCount_ = 0;
}
}
int lastPos = entries_.size() - 1;
@@ -144,6 +181,26 @@ void PerformanceEntryReporter::clearEntries(
entries_.resize(lastPos + 1);
}
std::vector<RawPerformanceEntry> PerformanceEntryReporter::getEntries(
PerformanceEntryType entryType,
const char *entryName) const {
if (entryType == PerformanceEntryType::MARK) {
return getCircularBufferContents(
marksBuffer_, marksCount_, marksBufferPosition_, entryName);
} else if (entryType == PerformanceEntryType::MEASURE) {
return getCircularBufferContents(
measuresBuffer_, measuresCount_, measuresBufferPosition_, entryName);
} else if (entryType == PerformanceEntryType::UNDEFINED) {
auto marks = getCircularBufferContents(
marksBuffer_, marksCount_, marksBufferPosition_, entryName);
auto measures = getCircularBufferContents(
measuresBuffer_, measuresCount_, measuresBufferPosition_, entryName);
marks.insert(marks.end(), measures.begin(), measures.end());
return marks;
}
return {};
}
void PerformanceEntryReporter::measure(
const std::string &name,
double startTime,
@@ -154,6 +211,13 @@ void PerformanceEntryReporter::measure(
double startTimeVal = startMark ? getMarkTime(*startMark) : startTime;
double endTimeVal = endMark ? getMarkTime(*endMark) : endTime;
double durationVal = duration ? *duration : endTimeVal - startTimeVal;
measuresBuffer_[measuresBufferPosition_] =
PerformanceMeasure{name, startTime, endTime};
measuresBufferPosition_ =
(measuresBufferPosition_ + 1) % measuresBuffer_.size();
measuresCount_ = std::min(measuresBuffer_.size(), measuresCount_ + 1);
logEntry(
{name,
static_cast<int>(PerformanceEntryType::MEASURE),