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 8 #pragma once 9 10 #include <ABI49_0_0React/ABI49_0_0bridging/Function.h> 11 #include <ABI49_0_0React/ABI49_0_0renderer/core/EventLogger.h> 12 #include <array> 13 #include <functional> 14 #include <mutex> 15 #include <optional> 16 #include <unordered_map> 17 #include <unordered_set> 18 #include "ABI49_0_0NativePerformanceObserver.h" 19 20 namespace ABI49_0_0facebook::ABI49_0_0React { 21 22 struct PerformanceMark { 23 std::string name; 24 double timeStamp; 25 26 RawPerformanceEntry toRawPerformanceEntry() const; 27 }; 28 29 struct PerformanceMarkHash { operatorPerformanceMarkHash30 size_t operator()(const PerformanceMark *mark) const { 31 return std::hash<std::string>()(mark->name); 32 } 33 }; 34 35 struct PerformanceMarkEqual { operatorPerformanceMarkEqual36 bool operator()(const PerformanceMark *lhs, const PerformanceMark *rhs) 37 const { 38 return lhs->name == rhs->name; 39 } 40 }; 41 42 struct PerformanceMeasure { 43 std::string name; 44 double timeStamp; 45 double duration; 46 47 RawPerformanceEntry toRawPerformanceEntry() const; 48 }; 49 50 using PerformanceMarkRegistryType = std:: 51 unordered_set<PerformanceMark *, PerformanceMarkHash, PerformanceMarkEqual>; 52 53 // Only the MARKS_BUFFER_SIZE amount of the latest marks will be kept in 54 // memory for the sake of the "Performance.measure" mark name lookup 55 constexpr size_t MARKS_BUFFER_SIZE = 1024; 56 57 // Limit buffer size for the measures kept in memory (only keep the latest ones) 58 constexpr size_t MEASURES_BUFFER_SIZE = 1024; 59 60 constexpr double DEFAULT_DURATION_THRESHOLD = 0.0; 61 62 enum class PerformanceEntryType { 63 UNDEFINED = 0, 64 MARK = 1, 65 MEASURE = 2, 66 EVENT = 3, 67 _COUNT = 4, 68 }; 69 70 class PerformanceEntryReporter : public EventLogger { 71 public: 72 PerformanceEntryReporter(PerformanceEntryReporter const &) = delete; 73 void operator=(PerformanceEntryReporter const &) = delete; 74 75 // NOTE: This class is not thread safe, make sure that the calls are made from 76 // the same thread. 77 // TODO: Consider passing it as a parameter to the corresponding modules at 78 // creation time instead of having the singleton. 79 static PerformanceEntryReporter &getInstance(); 80 81 void setReportingCallback(std::optional<AsyncCallback<>> callback); 82 void startReporting(PerformanceEntryType entryType); 83 void stopReporting(PerformanceEntryType entryType); 84 void stopReporting(); 85 void setDurationThreshold( 86 PerformanceEntryType entryType, 87 double durationThreshold); 88 89 GetPendingEntriesResult popPendingEntries(); 90 void logEntry(const RawPerformanceEntry &entry); 91 isReporting(PerformanceEntryType entryType)92 bool isReporting(PerformanceEntryType entryType) const { 93 return reportingType_[static_cast<int>(entryType)]; 94 } 95 isReportingEvents()96 bool isReportingEvents() const { 97 return isReporting(PerformanceEntryType::EVENT); 98 } 99 getDroppedEntryCount()100 uint32_t getDroppedEntryCount() const { 101 return droppedEntryCount_; 102 } 103 104 void mark(const std::string &name, double startTime, double duration); 105 106 void measure( 107 const std::string &name, 108 double startTime, 109 double endTime, 110 const std::optional<double> &duration = std::nullopt, 111 const std::optional<std::string> &startMark = std::nullopt, 112 const std::optional<std::string> &endMark = std::nullopt); 113 114 void clearEntries( 115 PerformanceEntryType entryType = PerformanceEntryType::UNDEFINED, 116 const char *entryName = nullptr); 117 118 std::vector<RawPerformanceEntry> getEntries( 119 PerformanceEntryType entryType = PerformanceEntryType::UNDEFINED, 120 const char *entryName = nullptr) const; 121 122 void event( 123 std::string name, 124 double startTime, 125 double duration, 126 double processingStart, 127 double processingEnd, 128 uint32_t interactionId); 129 130 EventTag onEventStart(const char *name) override; 131 void onEventDispatch(EventTag tag) override; 132 void onEventEnd(EventTag tag) override; 133 getEventCounts()134 const std::unordered_map<std::string, uint32_t> &getEventCounts() const { 135 return eventCounts_; 136 } 137 138 private: 139 std::optional<AsyncCallback<>> callback_; 140 std::vector<RawPerformanceEntry> entries_; 141 std::mutex entriesMutex_; 142 std::array<bool, (size_t)PerformanceEntryType::_COUNT> reportingType_{false}; 143 std::unordered_map<std::string, uint32_t> eventCounts_; 144 std::array<double, (size_t)PerformanceEntryType::_COUNT> durationThreshold_{ 145 DEFAULT_DURATION_THRESHOLD}; 146 147 // Mark registry for "measure" lookup 148 PerformanceMarkRegistryType marksRegistry_; 149 std::array<PerformanceMark, MARKS_BUFFER_SIZE> marksBuffer_; 150 size_t marksBufferPosition_{0}; 151 size_t marksCount_{0}; 152 153 std::array<PerformanceMeasure, MEASURES_BUFFER_SIZE> measuresBuffer_; 154 size_t measuresBufferPosition_{0}; 155 size_t measuresCount_{0}; 156 157 uint32_t droppedEntryCount_{0}; 158 159 struct EventEntry { 160 const char *name; 161 double startTime{0.0}; 162 double dispatchTime{0.0}; 163 }; 164 165 // Registry to store the events that are currently ongoing. 166 // Note that we could probably use a more efficient container for that, 167 // but since we only report discrete events, the volume is normally low, 168 // so a hash map should be just fine. 169 std::unordered_map<EventTag, EventEntry> eventsInFlight_; 170 std::mutex eventsInFlightMutex_; 171 172 static EventTag sCurrentEventTag_; 173 PerformanceEntryReporter()174 PerformanceEntryReporter() {} 175 176 double getMarkTime(const std::string &markName) const; 177 void scheduleFlushBuffer(); 178 179 template <class T, size_t N> 180 std::vector<RawPerformanceEntry> getCircularBufferContents( 181 const std::array<T, N> &buffer, 182 size_t entryCount, 183 size_t bufferPosition, 184 const char *entryName = nullptr) const { 185 std::vector<RawPerformanceEntry> res; 186 size_t pos = (bufferPosition - entryCount + buffer.size()) % buffer.size(); 187 for (size_t i = 0; i < entryCount; i++) { 188 if (entryName == nullptr || buffer[pos].name == entryName) { 189 res.push_back(buffer[pos].toRawPerformanceEntry()); 190 } 191 pos = (pos + 1) % buffer.size(); 192 } 193 return res; 194 } 195 196 template <class T, size_t N> clearCircularBuffer(std::array<T,N> & buffer,size_t & entryCount,size_t & bufferPosition,const char * entryName)197 void clearCircularBuffer( 198 std::array<T, N> &buffer, 199 size_t &entryCount, 200 size_t &bufferPosition, 201 const char *entryName) const { 202 std::array<T, N> newBuffer; 203 size_t newEntryCount = 0; 204 205 size_t pos = (bufferPosition - entryCount + buffer.size()) % buffer.size(); 206 for (size_t i = 0; i < entryCount; i++) { 207 if (buffer[pos].name != entryName) { 208 newBuffer[newEntryCount++] = buffer[pos]; 209 } 210 pos = (pos + 1) % buffer.size(); 211 } 212 213 buffer = newBuffer; 214 bufferPosition = newEntryCount; 215 entryCount = newEntryCount; 216 } 217 }; 218 219 } // namespace ABI49_0_0facebook::ABI49_0_0React 220