Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New implementation of PerformanceObserver and related APIs (fixes #45122) #45206

Closed
wants to merge 60 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
5129b07
refactor: extract performance observers to native layer and push entr…
robik Jun 27, 2024
df5cade
update JS code to reflect native changes
robik Jul 24, 2024
2a47fdc
chore: undo CMakeLists change
robik Aug 1, 2024
cecdd2f
fix: wrap Plugins.h include into ifdef
robik Aug 1, 2024
b4c02b3
chore: add comment on PerformanceObserver TurboModule inclusion
robik Aug 1, 2024
cc46c94
fix: use type from timing/Primitives
robik Aug 1, 2024
ae9c73b
fix reporting logic for entries of event type
robik Aug 1, 2024
b8705e4
fix: handle LongTask in getBuffer() switch
robik Aug 1, 2024
7819498
fix: use DOMHighResTimestamp
robik Aug 1, 2024
a4e404d
fix: remove checking for duration threshold in observer and move filt…
robik Aug 5, 2024
2b0f107
fix: remove const ref getEventFilter and add [[nodiscard]] attributes
robik Aug 5, 2024
284971e
feat: add [[nodiscard]] attributes
robik Aug 5, 2024
751aba1
feat: add [[nodiscard]] attributes; clean up data flow to make it sim…
robik Aug 5, 2024
30642ec
reorganize files; clean up logic; split PerformanceEntry buffers
robik Aug 8, 2024
6862527
improve `PerformanceObserver` options bridging
robik Aug 8, 2024
863d50e
chore: prettify Performance Observer public API
robik Sep 13, 2024
dbf0775
chore: moved PushStatus back to `BoundedConsumableBuffer`
robik Sep 13, 2024
6e0319c
chore: add more comments about testing only code
robik Sep 13, 2024
f3450b6
chore: add comment about divergence vs the spec
robik Sep 13, 2024
13f1200
fix: narrow down the public API and move the responsibility of filter…
robik Sep 13, 2024
573b329
chore: remove unnecessary [[nodiscard]] attirubtes
robik Sep 13, 2024
b2f039a
chore: rename `PerformanceObserverRegistry::emit` to `queuePerformanc…
robik Sep 13, 2024
f613d44
chore: add `OpaqueNativeObserverHandle` type
robik Sep 16, 2024
8e1217d
chore: remove generic `pushEntry` from PerformanceEntryReporter
robik Sep 16, 2024
254613f
chore: bring back public `getBuffer`
robik Sep 16, 2024
b7a1732
fix: multiple types buffering is not allowed
robik Sep 16, 2024
574fa98
fix: update JS layer; make observer buffer a vector; support `duratio…
robik Sep 17, 2024
6dc3b4a
fix: update native and JS layer
robik Sep 17, 2024
6a12b19
fix: make performance observer inherit from NativeState
robik Sep 17, 2024
6056aa7
fix: tests
robik Sep 17, 2024
e0aa0a2
chore: cleanup PerformanceObserver and PerformanceObserverRegistry de…
robik Sep 17, 2024
a2f54b1
chore: cleanup PerformanceObserver and PerformanceObserverRegistry de…
robik Sep 17, 2024
1452d44
chore: style fixes
robik Sep 17, 2024
11ebbef
chore: narrow down CircularBuffer API
robik Sep 17, 2024
9a59aec
chore: remove LinearBuffer and `pendingMessagesCount`
robik Sep 17, 2024
a1f7bb6
chore: fix getEventCounts position
robik Sep 17, 2024
187be80
chore: fix lock_guard initialization style
robik Sep 17, 2024
a2bf9cf
style: rename `entriesMutex_` to `buffersMutex_`
robik Sep 17, 2024
1cc3928
fix: remove concept of consuming, renamed `BoundedConsumableBuffer` t…
robik Sep 18, 2024
771537e
chore: remove allEntries cache from keyed buffer
robik Sep 18, 2024
88cc8ad
fix: refactor EntryReporter public API
robik Sep 18, 2024
a8cbb48
style: format code
robik Sep 18, 2024
40b6200
chore: make add/removeObserver part of registry public API
robik Sep 18, 2024
055ae21
chore: remove getNextOverwriteCandidate
robik Sep 18, 2024
4a9372c
fix: schedule buffer flush only when its not empty
robik Sep 18, 2024
8c987ab
chore: cleanup optionals as arguments
robik Sep 18, 2024
d22f590
chore: remove vector after clearing elements by name
robik Sep 18, 2024
dfd9413
chore: remove pointer aliases, make PerformanceObserver shared instea…
robik Sep 19, 2024
8d78ccd
style: fix
robik Sep 19, 2024
56d04ab
style: update comment
robik Sep 19, 2024
fb3197c
chore: remove default max size from circular buffer
robik Sep 19, 2024
23802a4
fix: optimize JS native call
robik Sep 19, 2024
8fe46c2
fix: tests
robik Sep 20, 2024
4f4f6b3
fix: tests
robik Sep 20, 2024
8f717c0
undo testing stuff
robik Sep 20, 2024
ecf035a
fix if condition
robik Sep 20, 2024
3b20278
remove weak ptrs from registry;
robik Sep 20, 2024
aeba178
remove clearing the observer in native module
robik Sep 20, 2024
8cfb905
rename `logEvent` to `testOnly_logEvent` in NativePerformanceObserver
robik Sep 23, 2024
f136f65
style: fix
robik Sep 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@
#include <cxxreact/ReactMarker.h>
#include <jsi/instrumentation.h>
#include <react/performance/timeline/PerformanceEntryReporter.h>
#if __has_include(<reactperflogger/fusebox/FuseboxTracer.h>)
#include <reactperflogger/fusebox/FuseboxTracer.h>
#define HAS_FUSEBOX
#endif
#include "NativePerformance.h"

#ifdef RN_DISABLE_OSS_PLUGIN_HEADER
#include "Plugins.h"
#endif

#ifdef WITH_PERFETTO
#include <reactperflogger/ReactPerfetto.h>
Expand Down Expand Up @@ -106,8 +112,11 @@ void NativePerformance::measure(
std::optional<std::string> endMark) {
auto [trackName, eventName] = parseTrackName(name);

#ifdef HAS_FUSEBOX
FuseboxTracer::getFuseboxTracer().addEvent(
eventName, (uint64_t)startTime, (uint64_t)endTime, trackName);
#endif

PerformanceEntryReporter::getInstance()->measure(
eventName, startTime, endTime, duration, startMark, endMark);

Expand All @@ -128,6 +137,18 @@ void NativePerformance::measure(
#endif
}

void NativePerformance::testOnly_logEvent(
jsi::Runtime& rt,
std::string name,
double startTime,
double duration,
double processingStart,
double processingEnd,
double interactionId) {
PerformanceEntryReporter::getInstance()->logEventEntry(
name, startTime, duration, processingStart, processingEnd, interactionId);
}

std::unordered_map<std::string, double> NativePerformance::getSimpleMemoryInfo(
jsi::Runtime& rt) {
auto heapInfo = rt.instrumentation().getHeapInfo(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@

#pragma once

#if __has_include("rncoreJSI.h") // Cmake headers on Android
#include "rncoreJSI.h"
#elif __has_include("FBReactNativeSpecJSI.h") // CocoaPod headers on Apple
#include "FBReactNativeSpecJSI.h"
#else
#include <FBReactNativeSpec/FBReactNativeSpecJSI.h>
#endif
#include <memory>
#include <string>

Expand All @@ -30,6 +36,15 @@ class NativePerformance : public NativePerformanceCxxSpec<NativePerformance> {
std::optional<std::string> startMark,
std::optional<std::string> endMark);

void testOnly_logEvent(
jsi::Runtime& rt,
std::string name,
double startTime,
double duration,
double processingStart,
double processingEnd,
double interactionId);

// To align with web API, we will make sure to return three properties
// (jsHeapSizeLimit, totalJSHeapSize, usedJSHeapSize) + anything needed from
// the VM side.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
#include <react/renderer/uimanager/UIManagerBinding.h>
#include <react/utils/CoreFeatures.h>

#ifdef RN_DISABLE_OSS_PLUGIN_HEADER
#include "Plugins.h"
robik marked this conversation as resolved.
Show resolved Hide resolved
#endif

std::shared_ptr<facebook::react::TurboModule>
NativePerformanceObserverModuleProvider(
Expand All @@ -24,75 +26,100 @@ NativePerformanceObserverModuleProvider(
}

namespace facebook::react {

NativePerformanceObserver::NativePerformanceObserver(
std::shared_ptr<CallInvoker> jsInvoker)
: NativePerformanceObserverCxxSpec(std::move(jsInvoker)) {}

void NativePerformanceObserver::startReporting(
jsi::Runtime& /*rt*/,
PerformanceEntryType entryType) {
auto reporter = PerformanceEntryReporter::getInstance();
jsi::Object NativePerformanceObserver::createObserver(
jsi::Runtime& rt,
NativePerformanceObserverCallback callback) {
// The way we dispatch performance observer callbacks is a bit different from
// the spec. The specification requires us to queue a single task that
// dispatches observer callbacks. Instead, we are queuing all callbacks as
// separate tasks in the scheduler.
PerformanceObserverCallback cb = [callback = std::move(callback)]() {
callback.callWithPriority(SchedulerPriority::IdlePriority);
rubennorte marked this conversation as resolved.
Show resolved Hide resolved
};

auto& registry =
PerformanceEntryReporter::getInstance()->getObserverRegistry();

reporter->startReporting(entryType);
auto observer = PerformanceObserver::create(registry, std::move(cb));
jsi::Object observerObj{rt};
observerObj.setNativeState(rt, observer);
return observerObj;
}

void NativePerformanceObserver::stopReporting(
jsi::Runtime& /*rt*/,
PerformanceEntryType entryType) {
auto reporter = PerformanceEntryReporter::getInstance();
double NativePerformanceObserver::getDroppedEntriesCount(
jsi::Runtime& rt,
jsi::Object observerObj) {
auto observer = std::dynamic_pointer_cast<PerformanceObserver>(
observerObj.getNativeState(rt));

if (!observer) {
return 0;
}

reporter->stopReporting(entryType);
return observer->getDroppedEntriesCount();
}

void NativePerformanceObserver::setIsBuffered(
jsi::Runtime& /*rt*/,
const std::vector<PerformanceEntryType> entryTypes,
bool isBuffered) {
for (const PerformanceEntryType entryType : entryTypes) {
PerformanceEntryReporter::getInstance()->setAlwaysLogged(
entryType, isBuffered);
void NativePerformanceObserver::observe(
jsi::Runtime& rt,
jsi::Object observerObj,
NativePerformanceObserverObserveOptions options) {
auto observer = std::dynamic_pointer_cast<PerformanceObserver>(
observerObj.getNativeState(rt));

if (!observer) {
return;
}
auto durationThreshold = options.durationThreshold.value_or(0.0);

// observer of type multiple
if (options.entryTypes.has_value()) {
std::unordered_set<PerformanceEntryType> entryTypes;
auto rawTypes = options.entryTypes.value();

for (auto i = 0; i < rawTypes.size(); ++i) {
entryTypes.insert(
Bridging<PerformanceEntryType>::fromJs(rt, rawTypes[i]));
}

observer->observe(entryTypes, {.durationThreshold = durationThreshold});
} else { // single
auto buffered = options.buffered.value_or(false);
if (options.type.has_value()) {
observer->observe(
static_cast<PerformanceEntryType>(options.type.value()),
{.buffered = buffered, .durationThreshold = durationThreshold});
}
}
}

PerformanceEntryReporter::PopPendingEntriesResult
NativePerformanceObserver::popPendingEntries(jsi::Runtime& /*rt*/) {
return PerformanceEntryReporter::getInstance()->popPendingEntries();
}
void NativePerformanceObserver::disconnect(
jsi::Runtime& rt,
jsi::Object observerObj) {
auto observer = std::dynamic_pointer_cast<PerformanceObserver>(
observerObj.getNativeState(rt));

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

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

std::vector<std::pair<std::string, uint32_t>>
NativePerformanceObserver::getEventCounts(jsi::Runtime& /*rt*/) {
const auto& eventCounts =
PerformanceEntryReporter::getInstance()->getEventCounts();
return std::vector<std::pair<std::string, uint32_t>>(
eventCounts.begin(), eventCounts.end());
}
std::vector<PerformanceEntry> NativePerformanceObserver::takeRecords(
jsi::Runtime& rt,
jsi::Object observerObj) {
auto observer = std::dynamic_pointer_cast<PerformanceObserver>(
observerObj.getNativeState(rt));

void NativePerformanceObserver::setDurationThreshold(
jsi::Runtime& /*rt*/,
PerformanceEntryType entryType,
double durationThreshold) {
PerformanceEntryReporter::getInstance()->setDurationThreshold(
entryType, durationThreshold);
if (!observer) {
return {};
}

return observer->takeRecords();
}

void NativePerformanceObserver::clearEntries(
Expand All @@ -107,8 +134,19 @@ std::vector<PerformanceEntry> NativePerformanceObserver::getEntries(
jsi::Runtime& /*rt*/,
std::optional<PerformanceEntryType> entryType,
std::optional<std::string> entryName) {
return PerformanceEntryReporter::getInstance()->getEntries(
entryType, entryName ? entryName->c_str() : std::string_view{});
const auto reporter = PerformanceEntryReporter::getInstance();

if (entryType.has_value()) {
if (entryName.has_value()) {
return reporter->getEntriesByName(entryName.value(), entryType.value());
} else {
return reporter->getEntriesByType(entryType.value());
}
} else if (entryName.has_value()) {
return reporter->getEntriesByName(entryName.value());
}

return reporter->getEntries();
}

std::vector<PerformanceEntryType>
Expand All @@ -127,4 +165,10 @@ NativePerformanceObserver::getSupportedPerformanceEntryTypes(
return supportedEntries;
}

std::vector<std::pair<std::string, uint32_t>>
NativePerformanceObserver::getEventCounts(jsi::Runtime& /*rt*/) {
const auto& eventCounts =
PerformanceEntryReporter::getInstance()->getEventCounts();
return {eventCounts.begin(), eventCounts.end()};
}
} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,34 @@

#pragma once

#if __has_include("rncoreJSI.h") // Cmake headers on Android
#include "rncoreJSI.h"
#elif __has_include("FBReactNativeSpecJSI.h") // CocoaPod headers on Apple
#include "FBReactNativeSpecJSI.h"
#else
#include <FBReactNativeSpec/FBReactNativeSpecJSI.h>
#endif

#include <react/performance/timeline/PerformanceEntryReporter.h>
#include <react/performance/timeline/PerformanceObserver.h>
#include <optional>
#include <string>
#include <vector>

namespace facebook::react {

using NativePerformanceObserverCallback = AsyncCallback<>;
using NativePerformanceObserverObserveOptions =
NativePerformanceObserverPerformanceObserverInit<
// entryTypes
std::optional<std::vector<int>>,
// type
std::optional<int>,
// buffered
std::optional<bool>,
// durationThreshold
std::optional<double>>;

#pragma mark - Structs

template <>
Expand All @@ -33,47 +53,38 @@ struct Bridging<PerformanceEntryType> {
};

template <>
struct Bridging<PerformanceEntry>
: NativePerformanceObserverRawPerformanceEntryBridging<PerformanceEntry> {};
struct Bridging<NativePerformanceObserverObserveOptions>
: NativePerformanceObserverPerformanceObserverInitBridging<
NativePerformanceObserverObserveOptions> {};

template <>
struct Bridging<PerformanceEntryReporter::PopPendingEntriesResult>
: NativePerformanceObserverGetPendingEntriesResultBridging<
PerformanceEntryReporter::PopPendingEntriesResult> {};
struct Bridging<PerformanceEntry>
: NativePerformanceObserverRawPerformanceEntryBridging<PerformanceEntry> {};

#pragma mark - implementation

class NativePerformanceObserver
: public NativePerformanceObserverCxxSpec<NativePerformanceObserver> {
public:
NativePerformanceObserver(std::shared_ptr<CallInvoker> jsInvoker);

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

void stopReporting(jsi::Runtime& rt, PerformanceEntryType entryType);
explicit NativePerformanceObserver(std::shared_ptr<CallInvoker> jsInvoker);

void setIsBuffered(
jsi::Object createObserver(
jsi::Runtime& rt,
const std::vector<PerformanceEntryType> entryTypes,
bool isBuffered);
NativePerformanceObserverCallback callback);
double getDroppedEntriesCount(jsi::Runtime& rt, jsi::Object observerObj);

PerformanceEntryReporter::PopPendingEntriesResult popPendingEntries(
jsi::Runtime& rt);

void setOnPerformanceEntryCallback(
void observe(
jsi::Runtime& rt,
std::optional<AsyncCallback<>> callback);

void logRawEntry(jsi::Runtime& rt, const PerformanceEntry entry);
jsi::Object observer,
NativePerformanceObserverObserveOptions options);
void disconnect(jsi::Runtime& rt, jsi::Object observer);
std::vector<PerformanceEntry> takeRecords(
jsi::Runtime& rt,
jsi::Object observerObj);

std::vector<std::pair<std::string, uint32_t>> getEventCounts(
jsi::Runtime& rt);

void setDurationThreshold(
jsi::Runtime& rt,
PerformanceEntryType entryType,
DOMHighResTimeStamp durationThreshold);

void clearEntries(
jsi::Runtime& rt,
PerformanceEntryType entryType,
Expand Down
Loading
Loading