Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -63,7 +63,6 @@ module.exports = class ReactNativeEnvironment extends NodeEnv {
Networking: {},
ImageLoader: {},
NativePerformanceCxx: {},
NativePerformanceObserverCxx: {},
LogBox: {},
SettingsManager: {
getConstants: () => ({settings: {}}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <cxxreact/ReactMarker.h>
#include <jsi/instrumentation.h>
#include <react/performance/timeline/PerformanceEntryReporter.h>
#include <react/performance/timeline/PerformanceObserver.h>

#if __has_include(<reactperflogger/fusebox/FuseboxTracer.h>)
#include <reactperflogger/fusebox/FuseboxTracer.h>
#define HAS_FUSEBOX
Expand Down Expand Up @@ -71,6 +73,40 @@ std::tuple<std::string, std::string_view> parseTrackName(
return std::make_tuple(trackNameRef, eventName);
}

class PerformanceObserverWrapper : public jsi::NativeState {
public:
explicit PerformanceObserverWrapper(
const std::shared_ptr<PerformanceObserver> observer)
: observer(observer) {}

const std::shared_ptr<PerformanceObserver> observer;
};

void sortEntries(std::vector<PerformanceEntry>& entries) {
return std::stable_sort(
entries.begin(), entries.end(), PerformanceEntrySorter{});
}

const std::array<PerformanceEntryType, 2> ENTRY_TYPES_AVAILABLE_FROM_TIMELINE{
{PerformanceEntryType::MARK, PerformanceEntryType::MEASURE}};

bool isAvailableFromTimeline(PerformanceEntryType entryType) {
return entryType == PerformanceEntryType::MARK ||
entryType == PerformanceEntryType::MEASURE;
}

std::shared_ptr<PerformanceObserver> tryGetObserver(
jsi::Runtime& rt,
jsi::Object& observerObj) {
if (!observerObj.hasNativeState(rt)) {
return nullptr;
}

auto observerWrapper = std::dynamic_pointer_cast<PerformanceObserverWrapper>(
observerObj.getNativeState(rt));
return observerWrapper ? observerWrapper->observer : nullptr;
}

} // namespace

NativePerformance::NativePerformance(std::shared_ptr<CallInvoker> jsInvoker)
Expand All @@ -88,7 +124,7 @@ void NativePerformance::mark(
jsi::Runtime& rt,
std::string name,
double startTime) {
PerformanceEntryReporter::getInstance()->mark(name, startTime);
PerformanceEntryReporter::getInstance()->reportMark(name, startTime);

#ifdef WITH_PERFETTO
if (TRACE_EVENT_CATEGORY_ENABLED("react-native")) {
Expand Down Expand Up @@ -117,7 +153,7 @@ void NativePerformance::measure(
eventName, (uint64_t)startTime, (uint64_t)endTime, trackName);
#endif

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

#ifdef WITH_PERFETTO
Expand All @@ -137,6 +173,87 @@ void NativePerformance::measure(
#endif
}

void NativePerformance::clearMarks(
jsi::Runtime& /*rt*/,
std::optional<std::string> entryName) {
if (entryName) {
PerformanceEntryReporter::getInstance()->clearEntries(
PerformanceEntryType::MARK, *entryName);
} else {
PerformanceEntryReporter::getInstance()->clearEntries(
PerformanceEntryType::MARK);
}
}

void NativePerformance::clearMeasures(
jsi::Runtime& /*rt*/,
std::optional<std::string> entryName) {
if (entryName) {
PerformanceEntryReporter::getInstance()->clearEntries(
PerformanceEntryType::MEASURE, *entryName);
} else {
PerformanceEntryReporter::getInstance()->clearEntries(
PerformanceEntryType::MEASURE);
}
}

std::vector<PerformanceEntry> NativePerformance::getEntries(
jsi::Runtime& /*rt*/) {
std::vector<PerformanceEntry> entries;

for (auto entryType : ENTRY_TYPES_AVAILABLE_FROM_TIMELINE) {
PerformanceEntryReporter::getInstance()->getEntries(entries, entryType);
}

sortEntries(entries);

return entries;
}

std::vector<PerformanceEntry> NativePerformance::getEntriesByName(
jsi::Runtime& /*rt*/,
std::string entryName,
std::optional<PerformanceEntryType> entryType) {
std::vector<PerformanceEntry> entries;

if (entryType) {
if (isAvailableFromTimeline(*entryType)) {
PerformanceEntryReporter::getInstance()->getEntries(
entries, *entryType, entryName);
}
} else {
for (auto type : ENTRY_TYPES_AVAILABLE_FROM_TIMELINE) {
PerformanceEntryReporter::getInstance()->getEntries(
entries, type, entryName);
}
}

sortEntries(entries);

return entries;
}

std::vector<PerformanceEntry> NativePerformance::getEntriesByType(
jsi::Runtime& /*rt*/,
PerformanceEntryType entryType) {
std::vector<PerformanceEntry> entries;

if (isAvailableFromTimeline(entryType)) {
PerformanceEntryReporter::getInstance()->getEntries(entries, entryType);
}

sortEntries(entries);

return entries;
}

std::vector<std::pair<std::string, uint32_t>> NativePerformance::getEventCounts(
jsi::Runtime& /*rt*/) {
const auto& eventCounts =
PerformanceEntryReporter::getInstance()->getEventCounts();
return {eventCounts.begin(), eventCounts.end()};
}

std::unordered_map<std::string, double> NativePerformance::getSimpleMemoryInfo(
jsi::Runtime& rt) {
auto heapInfo = rt.instrumentation().getHeapInfo(false);
Expand Down Expand Up @@ -185,4 +302,106 @@ NativePerformance::getReactNativeStartupTiming(jsi::Runtime& rt) {
return result;
}

jsi::Object NativePerformance::createObserver(
jsi::Runtime& rt,
NativePerformancePerformanceObserverCallback 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);
};

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

auto observer = PerformanceObserver::create(registry, std::move(cb));
auto observerWrapper = std::make_shared<PerformanceObserverWrapper>(observer);
jsi::Object observerObj{rt};
observerObj.setNativeState(rt, observerWrapper);
return observerObj;
}

double NativePerformance::getDroppedEntriesCount(
jsi::Runtime& rt,
jsi::Object observerObj) {
auto observer = tryGetObserver(rt, observerObj);

if (!observer) {
return 0;
}

return observer->getDroppedEntriesCount();
}

void NativePerformance::observe(
jsi::Runtime& rt,
jsi::Object observerObj,
NativePerformancePerformanceObserverObserveOptions options) {
auto observer = tryGetObserver(rt, observerObj);

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 rawType : rawTypes) {
entryTypes.insert(Bridging<PerformanceEntryType>::fromJs(rt, rawType));
}

observer->observe(entryTypes);
} 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});
}
}
}

void NativePerformance::disconnect(jsi::Runtime& rt, jsi::Object observerObj) {
auto observerWrapper = std::dynamic_pointer_cast<PerformanceObserverWrapper>(
observerObj.getNativeState(rt));

if (!observerWrapper) {
return;
}

auto observer = observerWrapper->observer;
observer->disconnect();
}

std::vector<PerformanceEntry> NativePerformance::takeRecords(
jsi::Runtime& rt,
jsi::Object observerObj,
bool sort) {
auto observerWrapper = std::dynamic_pointer_cast<PerformanceObserverWrapper>(
observerObj.getNativeState(rt));

if (!observerWrapper) {
return {};
}

auto observer = observerWrapper->observer;
auto records = observer->takeRecords();
if (sort) {
sortEntries(records);
}
return records;
}

std::vector<PerformanceEntryType>
NativePerformance::getSupportedPerformanceEntryTypes(jsi::Runtime& /*rt*/) {
auto supportedEntryTypes = PerformanceEntryReporter::getSupportedEntryTypes();
return {supportedEntryTypes.begin(), supportedEntryTypes.end()};
}

} // namespace facebook::react
Loading