Skip to content

Commit

Permalink
Decouple event logger from PerformanceEntryReporter and remove singleton
Browse files Browse the repository at this point in the history
Summary:
Changelog: [internal]

## Context

This is part of a refactor to decouple the performance entry reporter from the rendering infra and from the native module that uses it.

## Changes

This moves the logic to report the timing of events to a separate class (outside `PerformanceEntryReporter` that now is agnostic to the rendering infra).

Differential Revision: D55646392
  • Loading branch information
rubennorte authored and facebook-github-bot committed Apr 4, 2024
1 parent 7e5dcba commit 6f4eaa0
Show file tree
Hide file tree
Showing 17 changed files with 441 additions and 384 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ void NativePerformance::mark(
jsi::Runtime& rt,
std::string name,
double startTime) {
PerformanceEntryReporter::getInstance().mark(name, startTime);
PerformanceEntryReporter::getInstance()->mark(name, startTime);
}

void NativePerformance::measure(
Expand All @@ -45,7 +45,7 @@ void NativePerformance::measure(
std::optional<double> duration,
std::optional<std::string> startMark,
std::optional<std::string> endMark) {
PerformanceEntryReporter::getInstance().measure(
PerformanceEntryReporter::getInstance()->measure(
name, startTime, endTime, duration, startMark, endMark);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,80 +26,62 @@ namespace facebook::react {

NativePerformanceObserver::NativePerformanceObserver(
std::shared_ptr<CallInvoker> jsInvoker)
: NativePerformanceObserverCxxSpec(std::move(jsInvoker)) {
setEventLogger(&PerformanceEntryReporter::getInstance());
}

NativePerformanceObserver::~NativePerformanceObserver() {
setEventLogger(nullptr);
}
: NativePerformanceObserverCxxSpec(std::move(jsInvoker)) {}

void NativePerformanceObserver::startReporting(
jsi::Runtime& rt,
jsi::Runtime& /*rt*/,
PerformanceEntryType entryType) {
PerformanceEntryReporter& reporter = PerformanceEntryReporter::getInstance();

reporter.startReporting(entryType);
auto reporter = PerformanceEntryReporter::getInstance();

if (entryType == PerformanceEntryType::EVENT &&
CoreFeatures::enableReportEventPaintTime) {
UIManagerBinding::getBinding(rt)->getUIManager().registerMountHook(
reporter);
}
reporter->startReporting(entryType);
}

void NativePerformanceObserver::stopReporting(
jsi::Runtime& rt,
jsi::Runtime& /*rt*/,
PerformanceEntryType entryType) {
PerformanceEntryReporter& reporter = PerformanceEntryReporter::getInstance();
auto reporter = PerformanceEntryReporter::getInstance();

reporter.stopReporting(entryType);

if (entryType == PerformanceEntryType::EVENT &&
CoreFeatures::enableReportEventPaintTime) {
UIManagerBinding::getBinding(rt)->getUIManager().unregisterMountHook(
reporter);
}
reporter->stopReporting(entryType);
}

void NativePerformanceObserver::setIsBuffered(
jsi::Runtime& /*rt*/,
const std::vector<PerformanceEntryType> entryTypes,
bool isBuffered) {
for (const PerformanceEntryType entryType : entryTypes) {
PerformanceEntryReporter::getInstance().setAlwaysLogged(
PerformanceEntryReporter::getInstance()->setAlwaysLogged(
entryType, isBuffered);
}
}

PerformanceEntryReporter::PopPendingEntriesResult
NativePerformanceObserver::popPendingEntries(jsi::Runtime& /*rt*/) {
return PerformanceEntryReporter::getInstance().popPendingEntries();
return PerformanceEntryReporter::getInstance()->popPendingEntries();
}

void NativePerformanceObserver::setOnPerformanceEntryCallback(
jsi::Runtime& /*rt*/,
std::optional<AsyncCallback<>> callback) {
if (callback) {
PerformanceEntryReporter::getInstance().setReportingCallback(
PerformanceEntryReporter::getInstance()->setReportingCallback(
[callback = std::move(callback)]() {
callback->callWithPriority(SchedulerPriority::IdlePriority);
});
} else {
PerformanceEntryReporter::getInstance().setReportingCallback(nullptr);
PerformanceEntryReporter::getInstance()->setReportingCallback(nullptr);
}
}

void NativePerformanceObserver::logRawEntry(
jsi::Runtime& /*rt*/,
const PerformanceEntry entry) {
PerformanceEntryReporter::getInstance().logEntry(entry);
PerformanceEntryReporter::getInstance()->logEntry(entry);
}

std::vector<std::pair<std::string, uint32_t>>
NativePerformanceObserver::getEventCounts(jsi::Runtime& /*rt*/) {
const auto& eventCounts =
PerformanceEntryReporter::getInstance().getEventCounts();
PerformanceEntryReporter::getInstance()->getEventCounts();
return std::vector<std::pair<std::string, uint32_t>>(
eventCounts.begin(), eventCounts.end());
}
Expand All @@ -108,23 +90,23 @@ void NativePerformanceObserver::setDurationThreshold(
jsi::Runtime& /*rt*/,
PerformanceEntryType entryType,
double durationThreshold) {
PerformanceEntryReporter::getInstance().setDurationThreshold(
PerformanceEntryReporter::getInstance()->setDurationThreshold(
entryType, durationThreshold);
}

void NativePerformanceObserver::clearEntries(
jsi::Runtime& /*rt*/,
PerformanceEntryType entryType,
std::optional<std::string> entryName) {
PerformanceEntryReporter::getInstance().clearEntries(
PerformanceEntryReporter::getInstance()->clearEntries(
entryType, entryName ? entryName->c_str() : std::string_view{});
}

std::vector<PerformanceEntry> NativePerformanceObserver::getEntries(
jsi::Runtime& /*rt*/,
std::optional<PerformanceEntryType> entryType,
std::optional<std::string> entryName) {
return PerformanceEntryReporter::getInstance().getEntries(
return PerformanceEntryReporter::getInstance()->getEntries(
entryType, entryName ? entryName->c_str() : std::string_view{});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ class NativePerformanceObserver
: public NativePerformanceObserverCxxSpec<NativePerformanceObserver> {
public:
NativePerformanceObserver(std::shared_ptr<CallInvoker> jsInvoker);
~NativePerformanceObserver();

void startReporting(jsi::Runtime& rt, PerformanceEntryType entryType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,14 @@
*/

#include "PerformanceEntryReporter.h"
#include <cxxreact/JSExecutor.h>
#include <react/renderer/core/EventLogger.h>
#include <react/utils/CoreFeatures.h>

#include <functional>
#include <unordered_map>

#include <glog/logging.h>
#include <cxxreact/JSExecutor.h>

namespace facebook::react {
EventTag PerformanceEntryReporter::sCurrentEventTag_{0};

PerformanceEntryReporter& PerformanceEntryReporter::getInstance() {
static PerformanceEntryReporter instance;
std::shared_ptr<PerformanceEntryReporter>
PerformanceEntryReporter::getInstance() {
static auto instance = std::make_shared<PerformanceEntryReporter>();
return instance;
}

Expand Down Expand Up @@ -299,171 +293,4 @@ void PerformanceEntryReporter::scheduleFlushBuffer() {
}
}

struct StrKey {
uint32_t key;
StrKey(std::string_view s) : key(std::hash<std::string_view>{}(s)) {}

bool operator==(const StrKey& rhs) const {
return key == rhs.key;
}
};

struct StrKeyHash {
constexpr size_t operator()(const StrKey& strKey) const {
return static_cast<size_t>(strKey.key);
}
};

// Supported events for reporting, see
// https://www.w3.org/TR/event-timing/#sec-events-exposed
// Not all of these are currently supported by RN, but we map them anyway for
// future-proofing.
using SupportedEventTypeRegistry =
std::unordered_map<StrKey, std::string_view, StrKeyHash>;

static const SupportedEventTypeRegistry& getSupportedEvents() {
static SupportedEventTypeRegistry SUPPORTED_EVENTS = {
{StrKey("topAuxClick"), "auxclick"},
{StrKey("topClick"), "click"},
{StrKey("topContextMenu"), "contextmenu"},
{StrKey("topDblClick"), "dblclick"},
{StrKey("topMouseDown"), "mousedown"},
{StrKey("topMouseEnter"), "mouseenter"},
{StrKey("topMouseLeave"), "mouseleave"},
{StrKey("topMouseOut"), "mouseout"},
{StrKey("topMouseOver"), "mouseover"},
{StrKey("topMouseUp"), "mouseup"},
{StrKey("topPointerOver"), "pointerover"},
{StrKey("topPointerEnter"), "pointerenter"},
{StrKey("topPointerDown"), "pointerdown"},
{StrKey("topPointerUp"), "pointerup"},
{StrKey("topPointerCancel"), "pointercancel"},
{StrKey("topPointerOut"), "pointerout"},
{StrKey("topPointerLeave"), "pointerleave"},
{StrKey("topGotPointerCapture"), "gotpointercapture"},
{StrKey("topLostPointerCapture"), "lostpointercapture"},
{StrKey("topTouchStart"), "touchstart"},
{StrKey("topTouchEnd"), "touchend"},
{StrKey("topTouchCancel"), "touchcancel"},
{StrKey("topKeyDown"), "keydown"},
{StrKey("topKeyPress"), "keypress"},
{StrKey("topKeyUp"), "keyup"},
{StrKey("topBeforeInput"), "beforeinput"},
{StrKey("topInput"), "input"},
{StrKey("topCompositionStart"), "compositionstart"},
{StrKey("topCompositionUpdate"), "compositionupdate"},
{StrKey("topCompositionEnd"), "compositionend"},
{StrKey("topDragStart"), "dragstart"},
{StrKey("topDragEnd"), "dragend"},
{StrKey("topDragEnter"), "dragenter"},
{StrKey("topDragLeave"), "dragleave"},
{StrKey("topDragOver"), "dragover"},
{StrKey("topDrop"), "drop"},
};
return SUPPORTED_EVENTS;
}

EventTag PerformanceEntryReporter::onEventStart(std::string_view name) {
if (!isReporting(PerformanceEntryType::EVENT)) {
return 0;
}
const auto& supportedEvents = getSupportedEvents();
auto it = supportedEvents.find(name);
if (it == supportedEvents.end()) {
return 0;
}

auto reportedName = it->second;

sCurrentEventTag_++;
if (sCurrentEventTag_ == 0) {
// The tag wrapped around (which is highly unlikely, but still)
sCurrentEventTag_ = 1;
}

auto timeStamp = getCurrentTimeStamp();
{
std::lock_guard lock(eventsInFlightMutex_);
eventsInFlight_.emplace(std::make_pair(
sCurrentEventTag_, EventEntry{reportedName, timeStamp, 0.0}));
}
return sCurrentEventTag_;
}

void PerformanceEntryReporter::onEventProcessingStart(EventTag tag) {
if (!isReporting(PerformanceEntryType::EVENT) || tag == 0) {
return;
}
auto timeStamp = getCurrentTimeStamp();
{
std::lock_guard lock(eventsInFlightMutex_);
auto it = eventsInFlight_.find(tag);
if (it != eventsInFlight_.end()) {
it->second.processingStartTime = timeStamp;
}
}
}

void PerformanceEntryReporter::onEventProcessingEnd(EventTag tag) {
if (!isReporting(PerformanceEntryType::EVENT) || tag == 0) {
return;
}
auto timeStamp = getCurrentTimeStamp();
{
std::lock_guard lock(eventsInFlightMutex_);
auto it = eventsInFlight_.find(tag);
if (it == eventsInFlight_.end()) {
return;
}
auto& entry = it->second;
entry.processingEndTime = timeStamp;

if (CoreFeatures::enableReportEventPaintTime) {
// If reporting paint time, don't send the entry just yet and wait for the
// mount hook callback to be called
return;
}

const auto& name = entry.name;

logEventEntry(
std::string(name),
entry.startTime,
timeStamp - entry.startTime,
entry.processingStartTime,
entry.processingEndTime,
entry.interactionId);
eventsInFlight_.erase(it);
}
}

void PerformanceEntryReporter::shadowTreeDidMount(
const RootShadowNode::Shared& /*rootShadowNode*/,
double mountTime) noexcept {
if (!isReporting(PerformanceEntryType::EVENT) ||
!CoreFeatures::enableReportEventPaintTime) {
return;
}

std::lock_guard lock(eventsInFlightMutex_);
auto it = eventsInFlight_.begin();
while (it != eventsInFlight_.end()) {
const auto& entry = it->second;
if (entry.processingEndTime == 0.0 || entry.processingEndTime > mountTime) {
// This mount doesn't correspond to the event
++it;
continue;
}

logEventEntry(
std::string(entry.name),
entry.startTime,
mountTime - entry.startTime,
entry.processingStartTime,
entry.processingEndTime,
entry.interactionId);
it = eventsInFlight_.erase(it);
}
}

} // namespace facebook::react
Loading

0 comments on commit 6f4eaa0

Please sign in to comment.