diff --git a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp index 72ac7ef0e121..d863a5f4ec46 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp @@ -45,10 +45,8 @@ namespace { #endif NO_DESTROY const std::string TRACK_PREFIX = "Track:"; -NO_DESTROY const std::string DEFAULT_TRACK_NAME = "# Web Performance"; -NO_DESTROY const std::string CUSTOM_TRACK_NAME_PREFIX = "# Web Performance: "; -std::tuple parseTrackName( +std::tuple, std::string_view> parseTrackName( const std::string& name) { // Until there's a standard way to pass through track information, parse it // manually, e.g., "Track:Foo:Event name" @@ -58,16 +56,13 @@ std::tuple parseTrackName( if (name.starts_with(TRACK_PREFIX)) { const auto trackNameDelimiter = name.find(':', TRACK_PREFIX.length()); if (trackNameDelimiter != std::string::npos) { - trackName = CUSTOM_TRACK_NAME_PREFIX + - name.substr( - TRACK_PREFIX.length(), - trackNameDelimiter - TRACK_PREFIX.length()); + 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; - return std::make_tuple(trackNameRef, eventName); + return std::make_tuple(trackName, eventName); } class PerformanceObserverWrapper : public jsi::NativeState { diff --git a/packages/react-native/ReactCommon/reactperflogger/fusebox/FuseboxTracer.cpp b/packages/react-native/ReactCommon/reactperflogger/fusebox/FuseboxTracer.cpp index 46c136a2e9df..3d52288965ca 100644 --- a/packages/react-native/ReactCommon/reactperflogger/fusebox/FuseboxTracer.cpp +++ b/packages/react-native/ReactCommon/reactperflogger/fusebox/FuseboxTracer.cpp @@ -14,6 +14,13 @@ namespace facebook::react { +namespace { + +/** Process ID for all emitted events. */ +const uint64_t PID = 1000; + +} // namespace + bool FuseboxTracer::isTracing() { std::lock_guard lock(mutex_); return tracing_; @@ -43,34 +50,43 @@ bool FuseboxTracer::stopTracing( } auto traceEvents = folly::dynamic::array(); + + // Register "Main" process + traceEvents.push_back(folly::dynamic::object( + "args", folly::dynamic::object("name", "Main"))("cat", "__metadata")( + "name", "process_name")("ph", "M")("pid", PID)("tid", 0)("ts", 0)); + // Register "Timings" track + // NOTE: This is a hack to make the trace viewer show a "Timings" track + // adjacent to custom tracks in our current build of Chrome DevTools. + // In future, we should align events exactly. + traceEvents.push_back(folly::dynamic::object( + "args", folly::dynamic::object("name", "Timings"))("cat", "__metadata")( + "name", "thread_name")("ph", "M")("pid", PID)("tid", 1000)("ts", 0)); + auto savedBuffer = std::move(buffer_); buffer_.clear(); - std::unordered_map trackIdMap; - uint64_t nextTrack = 1000; - - // Name the main process. Only one process is supported currently. - traceEvents.push_back(folly::dynamic::object( - "args", folly::dynamic::object("name", "Main App"))("cat", "__metadata")( - "name", "process_name")("ph", "M")("pid", 1000)("tid", 0)("ts", 0)); + uint64_t nextTrack = 1001; for (auto& event : savedBuffer) { - if (!trackIdMap.contains(event.track)) { + // For events with a custom track name, register track + if (event.track.length() && !trackIdMap.contains(event.track)) { auto trackId = nextTrack++; trackIdMap[event.track] = trackId; - // New track traceEvents.push_back(folly::dynamic::object( "args", folly::dynamic::object("name", event.track))( - "cat", "__metadata")("name", "thread_name")("ph", "M")("pid", 1000)( + "cat", "__metadata")("name", "thread_name")("ph", "M")("pid", PID)( "tid", trackId)("ts", 0)); } - auto trackId = trackIdMap[event.track]; - // New event + auto trackId = + trackIdMap.contains(event.track) ? trackIdMap[event.track] : 1000; + + // Emit "blink.user_timing" trace event traceEvents.push_back(folly::dynamic::object( - "args", folly::dynamic::object())("cat", "react.native")( + "args", folly::dynamic::object())("cat", "blink.user_timing")( "dur", (event.end - event.start) * 1000)("name", event.name)("ph", "X")( - "ts", event.start * 1000)("pid", 1000)("tid", trackId)); + "ts", event.start * 1000)("pid", PID)("tid", trackId)); if (traceEvents.size() >= 1000) { resultCallback(traceEvents); @@ -88,13 +104,13 @@ void FuseboxTracer::addEvent( const std::string_view& name, uint64_t start, uint64_t end, - const std::string_view& track) { + const std::optional& track) { std::lock_guard lock(mutex_); if (!tracing_) { return; } - buffer_.push_back( - BufferEvent{start, end, std::string(name), std::string(track)}); + buffer_.push_back(BufferEvent{ + start, end, std::string(name), std::string(track.value_or(""))}); } bool FuseboxTracer::stopTracingAndWriteToFile(const std::string& path) { diff --git a/packages/react-native/ReactCommon/reactperflogger/fusebox/FuseboxTracer.h b/packages/react-native/ReactCommon/reactperflogger/fusebox/FuseboxTracer.h index 6f04db474b6d..b5956d6c2b7c 100644 --- a/packages/react-native/ReactCommon/reactperflogger/fusebox/FuseboxTracer.h +++ b/packages/react-native/ReactCommon/reactperflogger/fusebox/FuseboxTracer.h @@ -7,9 +7,11 @@ #pragma once +#include "folly/dynamic.h" + #include +#include #include -#include "folly/dynamic.h" namespace facebook::react { @@ -38,7 +40,7 @@ class FuseboxTracer { const std::string_view& name, uint64_t start, uint64_t end, - const std::string_view& track); + const std::optional& track); static FuseboxTracer& getFuseboxTracer(); diff --git a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfLogger.cpp b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfLogger.cpp index 6f1b8d0aa238..94e8eaae4ffb 100644 --- a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfLogger.cpp +++ b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfLogger.cpp @@ -14,15 +14,33 @@ #endif #include +#ifdef WITH_PERFETTO #include "ReactPerfetto.h" +#endif namespace facebook::react { +#ifdef WITH_PERFETTO +namespace { + +const std::string PERFETTO_DEFAULT_TRACK_NAME = "# Web Performance"; +const std::string PERFETTO_TRACK_NAME_PREFIX = "# Web Performance: "; + +std::string toPerfettoTrackName( + const std::optional& trackName) { + return trackName.has_value() + ? PERFETTO_TRACK_NAME_PREFIX + std::string(trackName.value()) + : PERFETTO_DEFAULT_TRACK_NAME; +} + +} // namespace +#endif + /* static */ void ReactPerfLogger::measure( const std::string_view& eventName, double startTime, double endTime, - const std::string_view& trackName) { + const std::optional& trackName) { #ifdef HAS_FUSEBOX FuseboxTracer::getFuseboxTracer().addEvent( eventName, (uint64_t)startTime, (uint64_t)endTime, trackName); @@ -30,7 +48,7 @@ namespace facebook::react { #ifdef WITH_PERFETTO if (TRACE_EVENT_CATEGORY_ENABLED("react-native")) { - auto track = getPerfettoWebPerfTrackAsync(std::string(trackName)); + auto track = getPerfettoWebPerfTrackAsync(toPerfettoTrackName(trackName)); TRACE_EVENT_BEGIN( "react-native", perfetto::DynamicString(eventName.data(), eventName.size()), @@ -45,7 +63,7 @@ namespace facebook::react { /* static */ void ReactPerfLogger::mark( const std::string_view& eventName, double startTime, - const std::string_view& trackName) { + const std::optional& trackName) { // TODO(T203046480) Support mark in FuseboxTracer #ifdef WITH_PERFETTO @@ -53,7 +71,7 @@ namespace facebook::react { TRACE_EVENT_INSTANT( "react-native", perfetto::DynamicString(eventName.data(), eventName.size()), - getPerfettoWebPerfTrackSync(std::string(trackName)), + getPerfettoWebPerfTrackSync(toPerfettoTrackName(trackName)), performanceNowToPerfettoTraceTime(startTime)); } #endif @@ -62,4 +80,5 @@ namespace facebook::react { /* static */ double ReactPerfLogger::performanceNow() { return chronoToDOMHighResTimeStamp(std::chrono::steady_clock::now()); } + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfLogger.h b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfLogger.h index 9e4e27b54a52..e2e2eff92dfb 100644 --- a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfLogger.h +++ b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfLogger.h @@ -8,26 +8,30 @@ #pragma once #include + +#include #include namespace facebook::react { +/** + * An internal interface for logging performance events to configured React + * Native performance tools, such as Perfetto or React Native DevTools. + * + * Approximates https://w3c.github.io/user-timing/. + */ class ReactPerfLogger { public: static void mark( const std::string_view& eventName, double startTime, - const std::string_view& trackName); + const std::optional& trackName); - /** - * This accepts performance events that should go to internal tracing - * frameworks like Perfetto, and should go to DevTools like Fusebox. - */ static void measure( const std::string_view& eventName, double startTime, double endTime, - const std::string_view& trackName); + const std::optional& trackName); static double performanceNow(); };