mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Integrate perfetto with UserTiming API (#44702)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44702 Based on bgirard initial changes in D53478653, this creates an initial integration of the Perfetto SDK with the User Timing API, allowing performance information to be logged from JS to Perfetto traces. We only enable this for Android right now, but may be able to leverage this on other platforms too in the future. The logic in `initializePerfetto` may need to moved to another common target (eg reactperflogger) once we want to make this usable in other components, but keeping it scoped to User Timing for now. Changelog: [Internal] Reviewed By: bgirard Differential Revision: D57881823 fbshipit-source-id: 11ba09cbc01a102a72eee65ce6d6aeca508e864a
This commit is contained in:
committed by
Facebook GitHub Bot
parent
84ff977b37
commit
9d3bac6fd5
+106
-2
@@ -5,16 +5,24 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "NativePerformance.h"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <cxxreact/JSExecutor.h>
|
||||
#include <cxxreact/ReactMarker.h>
|
||||
#include <jsi/instrumentation.h>
|
||||
#include <react/performance/timeline/PerformanceEntryReporter.h>
|
||||
#include "NativePerformance.h"
|
||||
|
||||
#include "Plugins.h"
|
||||
|
||||
#ifdef WITH_PERFETTO
|
||||
#include <perfetto.h>
|
||||
#include <reactperflogger/ReactPerfettoCategories.h>
|
||||
#endif
|
||||
|
||||
std::shared_ptr<facebook::react::TurboModule> NativePerformanceModuleProvider(
|
||||
std::shared_ptr<facebook::react::CallInvoker> jsInvoker) {
|
||||
return std::make_shared<facebook::react::NativePerformance>(
|
||||
@@ -23,8 +31,79 @@ std::shared_ptr<facebook::react::TurboModule> NativePerformanceModuleProvider(
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef WITH_PERFETTO
|
||||
|
||||
// Offset for custom perfetto tracks
|
||||
uint64_t trackId = 0x5F3759DF;
|
||||
|
||||
// Extract this once we start emitting perfetto markers from other modules
|
||||
std::once_flag perfettoInit;
|
||||
void initializePerfetto() {
|
||||
std::call_once(perfettoInit, []() {
|
||||
perfetto::TracingInitArgs args;
|
||||
args.backends |= perfetto::kSystemBackend;
|
||||
args.use_monotonic_clock = true;
|
||||
perfetto::Tracing::Initialize(args);
|
||||
perfetto::TrackEvent::Register();
|
||||
});
|
||||
}
|
||||
|
||||
const std::string TRACK_PREFIX = "Track:";
|
||||
const std::string DEFAULT_TRACK_NAME = "Web Performance";
|
||||
|
||||
std::tuple<perfetto::Track, std::string_view> parsePerfettoTrack(
|
||||
const std::string& name) {
|
||||
// Until there's a standard way to pass through track information, parse it
|
||||
// manually, e.g., "Track:Foo:Event name"
|
||||
// https://github.com/w3c/user-timing/issues/109
|
||||
std::optional<std::string> trackName;
|
||||
std::string_view eventName(name);
|
||||
if (name.starts_with(TRACK_PREFIX)) {
|
||||
const auto trackNameDelimiter = name.find(':', TRACK_PREFIX.length());
|
||||
if (trackNameDelimiter != std::string::npos) {
|
||||
trackName = name.substr(
|
||||
TRACK_PREFIX.length(), trackNameDelimiter - TRACK_PREFIX.length());
|
||||
eventName = std::string_view(name).substr(trackNameDelimiter + 1);
|
||||
}
|
||||
}
|
||||
|
||||
auto& trackNameRef = trackName.has_value() ? *trackName : DEFAULT_TRACK_NAME;
|
||||
static std::unordered_map<std::string, perfetto::Track> tracks;
|
||||
auto it = tracks.find(trackNameRef);
|
||||
if (it == tracks.end()) {
|
||||
auto track = perfetto::Track(trackId++);
|
||||
auto desc = track.Serialize();
|
||||
desc.set_name(trackNameRef);
|
||||
perfetto::TrackEvent::SetTrackDescriptor(track, desc);
|
||||
tracks.emplace(trackNameRef, track);
|
||||
return std::make_tuple(track, eventName);
|
||||
} else {
|
||||
return std::make_tuple(it->second, eventName);
|
||||
}
|
||||
}
|
||||
|
||||
// Perfetto's monotonic clock seems to match the std::chrono::steady_clock we
|
||||
// use in JSExecutor::performanceNow on Android platforms, but if that
|
||||
// assumption is incorrect we may need to manually offset perfetto timestamps.
|
||||
uint64_t performanceNowToPerfettoTraceTime(double perfNowTime) {
|
||||
if (perfNowTime == 0) {
|
||||
return perfetto::TrackEvent::GetTraceTimeNs();
|
||||
}
|
||||
return static_cast<uint64_t>(perfNowTime * 1.e6);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
NativePerformance::NativePerformance(std::shared_ptr<CallInvoker> jsInvoker)
|
||||
: NativePerformanceCxxSpec(std::move(jsInvoker)) {}
|
||||
: NativePerformanceCxxSpec(std::move(jsInvoker)) {
|
||||
#ifdef WITH_PERFETTO
|
||||
initializePerfetto();
|
||||
#endif
|
||||
}
|
||||
|
||||
double NativePerformance::now(jsi::Runtime& /*rt*/) {
|
||||
return JSExecutor::performanceNow();
|
||||
@@ -34,6 +113,16 @@ void NativePerformance::mark(
|
||||
jsi::Runtime& rt,
|
||||
std::string name,
|
||||
double startTime) {
|
||||
#ifdef WITH_PERFETTO
|
||||
if (TRACE_EVENT_CATEGORY_ENABLED("react-native")) {
|
||||
auto [track, eventName] = parsePerfettoTrack(name);
|
||||
TRACE_EVENT_INSTANT(
|
||||
"react-native",
|
||||
perfetto::DynamicString(eventName.data(), eventName.size()),
|
||||
track,
|
||||
performanceNowToPerfettoTraceTime(startTime));
|
||||
}
|
||||
#endif
|
||||
PerformanceEntryReporter::getInstance()->mark(name, startTime);
|
||||
}
|
||||
|
||||
@@ -45,6 +134,21 @@ void NativePerformance::measure(
|
||||
std::optional<double> duration,
|
||||
std::optional<std::string> startMark,
|
||||
std::optional<std::string> endMark) {
|
||||
#ifdef WITH_PERFETTO
|
||||
if (TRACE_EVENT_CATEGORY_ENABLED("react-native")) {
|
||||
// TODO T190600850 support startMark/endMark
|
||||
if (!startMark && !endMark) {
|
||||
auto [track, eventName] = parsePerfettoTrack(name);
|
||||
TRACE_EVENT_BEGIN(
|
||||
"react-native",
|
||||
perfetto::DynamicString(eventName.data(), eventName.size()),
|
||||
track,
|
||||
performanceNowToPerfettoTraceTime(startTime));
|
||||
TRACE_EVENT_END(
|
||||
"react-native", track, performanceNowToPerfettoTraceTime(endTime));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
PerformanceEntryReporter::getInstance()->measure(
|
||||
name, startTime, endTime, duration, startMark, endMark);
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
std::shared_ptr<PerformanceEntryReporter>
|
||||
std::shared_ptr<PerformanceEntryReporter>&
|
||||
PerformanceEntryReporter::getInstance() {
|
||||
static auto instance = std::make_shared<PerformanceEntryReporter>();
|
||||
return instance;
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ class PerformanceEntryReporter {
|
||||
// the same thread.
|
||||
// TODO: Consider passing it as a parameter to the corresponding modules at
|
||||
// creation time instead of having the singleton.
|
||||
static std::shared_ptr<PerformanceEntryReporter> getInstance();
|
||||
static std::shared_ptr<PerformanceEntryReporter>& getInstance();
|
||||
|
||||
struct PopPendingEntriesResult {
|
||||
std::vector<PerformanceEntry> entries;
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "ReactPerfettoCategories.h"
|
||||
|
||||
#ifdef WITH_PERFETTO
|
||||
|
||||
PERFETTO_TRACK_EVENT_STATIC_STORAGE();
|
||||
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_PERFETTO
|
||||
|
||||
#include <perfetto.h>
|
||||
|
||||
PERFETTO_DEFINE_CATEGORIES(
|
||||
perfetto::Category("react-native")
|
||||
.SetDescription("User timing events from React Native"));
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user