diff --git a/Libraries/WebPerformance/NativePerformanceObserver.cpp b/Libraries/WebPerformance/NativePerformanceObserver.cpp index 84864a69de2..16070de5c40 100644 --- a/Libraries/WebPerformance/NativePerformanceObserver.cpp +++ b/Libraries/WebPerformance/NativePerformanceObserver.cpp @@ -40,7 +40,7 @@ void NativePerformanceObserver::stopReporting( stringToPerformanceEntryType(entryType)); } -std::vector NativePerformanceObserver::popPendingEntries( +GetPendingEntriesResult NativePerformanceObserver::popPendingEntries( jsi::Runtime &rt) { return PerformanceEntryReporter::getInstance().popPendingEntries(); } diff --git a/Libraries/WebPerformance/NativePerformanceObserver.h b/Libraries/WebPerformance/NativePerformanceObserver.h index 9b8b6cd591c..37bb1462ad6 100644 --- a/Libraries/WebPerformance/NativePerformanceObserver.h +++ b/Libraries/WebPerformance/NativePerformanceObserver.h @@ -39,6 +39,17 @@ struct Bridging std::optional, std::optional> {}; +using GetPendingEntriesResult = + NativePerformanceObserverCxxBaseGetPendingEntriesResult< + std::vector, + uint32_t>; + +template <> +struct Bridging + : NativePerformanceObserverCxxBaseGetPendingEntriesResultBridging< + std::vector, + uint32_t> {}; + #pragma mark - implementation class NativePerformanceObserver @@ -51,7 +62,7 @@ class NativePerformanceObserver void stopReporting(jsi::Runtime &rt, std::string entryType); - std::vector popPendingEntries(jsi::Runtime &rt); + GetPendingEntriesResult popPendingEntries(jsi::Runtime &rt); void setOnPerformanceEntryCallback( jsi::Runtime &rt, diff --git a/Libraries/WebPerformance/NativePerformanceObserver.js b/Libraries/WebPerformance/NativePerformanceObserver.js index 91bd8fc0ab5..f2446622084 100644 --- a/Libraries/WebPerformance/NativePerformanceObserver.js +++ b/Libraries/WebPerformance/NativePerformanceObserver.js @@ -31,10 +31,15 @@ export type RawPerformanceEntry = {| interactionId?: number, |}; +export type GetPendingEntriesResult = {| + entries: $ReadOnlyArray, + droppedEntriesCount: number, +|}; + export interface Spec extends TurboModule { +startReporting: (entryType: string) => void; +stopReporting: (entryType: string) => void; - +popPendingEntries: () => $ReadOnlyArray; + +popPendingEntries: () => GetPendingEntriesResult; +setOnPerformanceEntryCallback: (callback?: () => void) => void; } diff --git a/Libraries/WebPerformance/PerformanceEntryReporter.cpp b/Libraries/WebPerformance/PerformanceEntryReporter.cpp index b23151b783b..75436ad7ed7 100644 --- a/Libraries/WebPerformance/PerformanceEntryReporter.cpp +++ b/Libraries/WebPerformance/PerformanceEntryReporter.cpp @@ -10,6 +10,11 @@ #include #include "NativePerformanceObserver.h" +// All the unflushed entries beyond this amount will get discarded, with +// the amount of discarded ones sent back to the observers' callbacks as +// "droppedEntryCount" value +static constexpr size_t MAX_ENTRY_BUFFER_SIZE = 1024; + namespace facebook::react { PerformanceEntryReporter &PerformanceEntryReporter::getInstance() { static PerformanceEntryReporter instance; @@ -33,10 +38,11 @@ const std::vector return entries_; } -std::vector PerformanceEntryReporter::popPendingEntries() { - auto entriesToReturn = std::move(entries_); +GetPendingEntriesResult PerformanceEntryReporter::popPendingEntries() { + GetPendingEntriesResult res = {std::move(entries_), droppedEntryCount_}; entries_ = {}; - return entriesToReturn; + droppedEntryCount_ = 0; + return res; } void PerformanceEntryReporter::clearPendingEntries() { @@ -48,6 +54,14 @@ void PerformanceEntryReporter::logEntry(const RawPerformanceEntry &entry) { return; } + if (entries_.size() == MAX_ENTRY_BUFFER_SIZE) { + // Start dropping entries once reached maximum buffer size. + // The number of dropped entries will be reported back to the corresponding + // PerformanceObserver callback. + droppedEntryCount_ += 1; + return; + } + entries_.emplace_back(entry); if (entries_.size() == 1) { @@ -63,21 +77,21 @@ void PerformanceEntryReporter::mark( double duration) { // Register the mark for further possible "measure" lookup, as well as add // it to a circular buffer: - PerformanceMark &mark = marks_buffer_[marks_buffer_position_]; - marks_buffer_position_ = (marks_buffer_position_ + 1) % marks_buffer_.size(); + PerformanceMark &mark = marksBuffer_[marksBufferPosition_]; + marksBufferPosition_ = (marksBufferPosition_ + 1) % marksBuffer_.size(); if (!mark.name.empty()) { // Drop off the oldest mark out of the queue, but only if that's indeed the // oldest one - auto it = marks_registry_.find(&mark); - if (it != marks_registry_.end() && *it == &mark) { - marks_registry_.erase(it); + auto it = marksRegistry_.find(&mark); + if (it != marksRegistry_.end() && *it == &mark) { + marksRegistry_.erase(it); } } mark.name = name; mark.timeStamp = startTime; - marks_registry_.insert(&mark); + marksRegistry_.insert(&mark); logEntry( {name, @@ -93,13 +107,13 @@ void PerformanceEntryReporter::clearMarks( const std::optional &markName) { if (markName) { PerformanceMark mark{{*markName, 0}}; - marks_registry_.erase(&mark); + marksRegistry_.erase(&mark); clearEntries([&markName](const RawPerformanceEntry &entry) { return entry.entryType == static_cast(PerformanceEntryType::MARK) && entry.name == markName; }); } else { - marks_registry_.clear(); + marksRegistry_.clear(); clearEntries([](const RawPerformanceEntry &entry) { return entry.entryType == static_cast(PerformanceEntryType::MARK); }); @@ -135,7 +149,7 @@ void PerformanceEntryReporter::clearMeasures( entry.name == measureName; }); } else { - marks_registry_.clear(); + marksRegistry_.clear(); clearEntries([](const RawPerformanceEntry &entry) { return entry.entryType == static_cast(PerformanceEntryType::MEASURE); }); @@ -145,8 +159,8 @@ void PerformanceEntryReporter::clearMeasures( double PerformanceEntryReporter::getMarkTime( const std::string &markName) const { PerformanceMark mark{{std::move(markName), 0}}; - auto it = marks_registry_.find(&mark); - if (it != marks_registry_.end()) { + auto it = marksRegistry_.find(&mark); + if (it != marksRegistry_.end()) { return (*it)->timeStamp; } else { return 0.0; diff --git a/Libraries/WebPerformance/PerformanceEntryReporter.h b/Libraries/WebPerformance/PerformanceEntryReporter.h index 56250660c1a..9aacbde2312 100644 --- a/Libraries/WebPerformance/PerformanceEntryReporter.h +++ b/Libraries/WebPerformance/PerformanceEntryReporter.h @@ -64,7 +64,7 @@ class PerformanceEntryReporter { void stopReporting(PerformanceEntryType entryType); const std::vector &getPendingEntries() const; - std::vector popPendingEntries(); + GetPendingEntriesResult popPendingEntries(); void clearPendingEntries(); void logEntry(const RawPerformanceEntry &entry); @@ -72,6 +72,10 @@ class PerformanceEntryReporter { return reportingType_[static_cast(entryType)]; } + uint32_t getDroppedEntryCount() const { + return droppedEntryCount_; + } + void mark(const std::string &name, double startTime, double duration); void clearMarks(const std::optional &markName); @@ -96,9 +100,10 @@ class PerformanceEntryReporter { std::array reportingType_{false}; // Mark registry for "measure" lookup - PerformanceMarkRegistryType marks_registry_; - std::array marks_buffer_; - size_t marks_buffer_position_{0}; + PerformanceMarkRegistryType marksRegistry_; + std::array marksBuffer_; + size_t marksBufferPosition_{0}; + uint32_t droppedEntryCount_{0}; }; } // namespace facebook::react diff --git a/Libraries/WebPerformance/PerformanceObserver.js b/Libraries/WebPerformance/PerformanceObserver.js index a4238be2b51..cf763e73018 100644 --- a/Libraries/WebPerformance/PerformanceObserver.js +++ b/Libraries/WebPerformance/PerformanceObserver.js @@ -112,6 +112,8 @@ export class PerformanceObserverEntryList { export type PerformanceObserverCallback = ( list: PerformanceObserverEntryList, observer: PerformanceObserver, + // The number of buffered entries which got dropped from the buffer due to the buffer being full: + droppedEntryCount?: number, ) => void; export type PerformanceObserverInit = @@ -137,7 +139,9 @@ const onPerformanceEntry = () => { if (!NativePerformanceObserver) { return; } - const rawEntries = NativePerformanceObserver.popPendingEntries() ?? []; + const entryResult = NativePerformanceObserver.popPendingEntries(); + const rawEntries = entryResult?.entries ?? []; + const droppedEntriesCount = entryResult?.droppedEntriesCount; if (rawEntries.length === 0) { return; } @@ -149,6 +153,7 @@ const onPerformanceEntry = () => { observerConfig.callback( new PerformanceObserverEntryList(entriesForObserver), observer, + droppedEntriesCount, ); } };