diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/CdpTracing.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/CdpTracing.h deleted file mode 100644 index 58b91d630ba0c0..00000000000000 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/CdpTracing.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 - -#include - -namespace facebook::react::jsinspector_modern { - -/** - * https://developer.chrome.com/docs/devtools/performance/extension?utm_source=devtools#devtools_object - */ -struct DevToolsTrackEntryPayload { - std::string track; -}; - -} // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp index ff75da2e068bd7..3b848f20b31809 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp @@ -113,7 +113,8 @@ void PerformanceTracer::collectEvents( void PerformanceTracer::reportMark( const std::string_view& name, - HighResTimeStamp start) { + HighResTimeStamp start, + folly::dynamic&& detail) { if (!tracingAtomic_) { return; } @@ -123,6 +124,13 @@ void PerformanceTracer::reportMark( return; } + folly::dynamic eventArgs = folly::dynamic::object(); + if (detail != nullptr) { + eventArgs = folly::dynamic::object( + "data", + folly::dynamic::object("detail", folly::toJson(std::move(detail)))); + } + buffer_.emplace_back(TraceEvent{ .name = std::string(name), .cat = "blink.user_timing", @@ -130,6 +138,7 @@ void PerformanceTracer::reportMark( .ts = start, .pid = processId_, .tid = oscompat::getCurrentThreadId(), + .args = eventArgs, }); } @@ -137,18 +146,15 @@ void PerformanceTracer::reportMeasure( const std::string_view& name, HighResTimeStamp start, HighResDuration duration, - const std::optional& trackMetadata) { + folly::dynamic&& detail) { if (!tracingAtomic_) { return; } folly::dynamic beginEventArgs = folly::dynamic::object(); - if (trackMetadata.has_value()) { - folly::dynamic devtoolsObject = folly::dynamic::object( - "devtools", - folly::dynamic::object("track", trackMetadata.value().track)); + if (detail != nullptr) { beginEventArgs = - folly::dynamic::object("detail", folly::toJson(devtoolsObject)); + folly::dynamic::object("detail", folly::toJson(std::move(detail))); } auto currentThreadId = oscompat::getCurrentThreadId(); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h index a91de55fe07d8b..7623b773a25290 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h @@ -7,7 +7,6 @@ #pragma once -#include "CdpTracing.h" #include "ConsoleTimeStamp.h" #include "TraceEvent.h" #include "TraceEventProfile.h" @@ -60,13 +59,17 @@ class PerformanceTracer { const std::function& resultCallback, uint16_t chunkSize); + /** * Record a `Performance.mark()` event - a labelled timestamp. If not * currently tracing, this is a no-op. * * See https://w3c.github.io/user-timing/#mark-method. */ - void reportMark(const std::string_view& name, HighResTimeStamp start); + void reportMark( + const std::string_view& name, + HighResTimeStamp start, + folly::dynamic&& detail = nullptr); /** * Record a `Performance.measure()` event - a labelled duration. If not @@ -78,7 +81,7 @@ class PerformanceTracer { const std::string_view& name, HighResTimeStamp start, HighResDuration duration, - const std::optional& trackMetadata); + folly::dynamic&& detail = nullptr); /** * Record a "TimeStamp" Trace Event - a labelled entry on Performance diff --git a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp index cb833ab134c124..9766b8d05c1180 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -111,6 +112,19 @@ std::shared_ptr tryGetObserver( return observerWrapper ? observerWrapper->observer : nullptr; } +PerformanceEntryReporter::UserTimingDetailProvider getDetailProviderFromEntry( + jsi::Runtime& rt, + jsi::Value& entry) { + return [&rt, &entry]() -> folly::dynamic { + try { + auto detail = entry.asObject(rt).getProperty(rt, "detail"); + return jsi::dynamicFromValue(rt, detail); + } catch (jsi::JSIException& ex) { + return nullptr; + } + }; +} + } // namespace NativePerformance::NativePerformance(std::shared_ptr jsInvoker) @@ -124,8 +138,9 @@ void NativePerformance::reportMark( jsi::Runtime& rt, std::string name, HighResTimeStamp startTime, - jsi::Value /*entry*/) { - PerformanceEntryReporter::getInstance()->reportMark(name, startTime); + jsi::Value entry) { + PerformanceEntryReporter::getInstance()->reportMark( + name, startTime, getDetailProviderFromEntry(rt, entry)); } void NativePerformance::reportMeasure( @@ -133,9 +148,9 @@ void NativePerformance::reportMeasure( std::string name, HighResTimeStamp startTime, HighResDuration duration, - jsi::Value /*entry*/) { + jsi::Value entry) { PerformanceEntryReporter::getInstance()->reportMeasure( - name, startTime, duration); + name, startTime, duration, getDetailProviderFromEntry(rt, entry)); } std::optional NativePerformance::getMarkTime( diff --git a/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt b/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt index 4250fb241c32d8..742f6f9fefe3e4 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt @@ -19,4 +19,5 @@ target_link_libraries(react_performance_timeline jsinspector_tracing reactperflogger react_featureflags - react_timing) + react_timing + folly_runtime) diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp index 2aa284f3eb9165..2f429e27d62df0 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp @@ -37,31 +37,40 @@ std::vector getSupportedEntryTypesInternal() { return supportedEntryTypes; } -#if defined(__clang__) -#define NO_DESTROY [[clang::no_destroy]] -#else -#define NO_DESTROY -#endif +std::optional getTrackFromDetail(folly::dynamic& detail) { + if (!detail.isObject()) { + return std::nullopt; + } -NO_DESTROY const std::string TRACK_PREFIX = "Track:"; - -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" - // https://github.com/w3c/user-timing/issues/109 - std::optional 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 maybeDevtools = detail["devtools"]; + if (!maybeDevtools.isObject()) { + return std::nullopt; + } + + auto maybeTrack = maybeDevtools["track"]; + if (!maybeTrack.isString()) { + return std::nullopt; + } + + return maybeTrack.asString(); +} + +std::optional getTrackGroupFromDetail(folly::dynamic& detail) { + if (!detail.isObject()) { + return std::nullopt; + } + + auto maybeDevtools = detail["devtools"]; + if (!maybeDevtools.isObject()) { + return std::nullopt; } - return std::make_tuple(trackName, eventName); + auto maybeTrackGroup = maybeDevtools["trackGroup"]; + if (!maybeTrackGroup.isString()) { + return std::nullopt; + } + + return maybeTrackGroup.asString(); } } // namespace @@ -169,10 +178,11 @@ void PerformanceEntryReporter::clearEntries( void PerformanceEntryReporter::reportMark( const std::string& name, - const HighResTimeStamp startTime) { + const HighResTimeStamp startTime, + UserTimingDetailProvider&& detailProvider) { const auto entry = PerformanceMark{{.name = name, .startTime = startTime}}; - traceMark(entry); + traceMark(entry, std::move(detailProvider)); // Add to buffers & notify observers { @@ -187,14 +197,13 @@ void PerformanceEntryReporter::reportMeasure( const std::string& name, HighResTimeStamp startTime, HighResDuration duration, - const std::optional& - trackMetadata) { + UserTimingDetailProvider&& detailProvider) { const auto entry = PerformanceMeasure{ {.name = std::string(name), .startTime = startTime, .duration = duration}}; - traceMeasure(entry); + traceMeasure(entry, std::move(detailProvider)); // Add to buffers & notify observers { @@ -296,46 +305,45 @@ void PerformanceEntryReporter::reportResourceTiming( observerRegistry_->queuePerformanceEntry(entry); } -void PerformanceEntryReporter::traceMark(const PerformanceMark& entry) const { +void PerformanceEntryReporter::traceMark( + const PerformanceMark& entry, + UserTimingDetailProvider&& detailProvider) const { auto& performanceTracer = jsinspector_modern::tracing::PerformanceTracer::getInstance(); if (ReactPerfettoLogger::isTracing() || performanceTracer.isTracing()) { - auto [trackName, eventName] = parseTrackName(entry.name); - - if (performanceTracer.isTracing()) { - performanceTracer.reportMark(entry.name, entry.startTime); + if (ReactPerfettoLogger::isTracing()) { + ReactPerfettoLogger::mark(entry.name, entry.startTime); } - if (ReactPerfettoLogger::isTracing()) { - ReactPerfettoLogger::mark(eventName, entry.startTime, trackName); + if (performanceTracer.isTracing()) { + performanceTracer.reportMark( + entry.name, + entry.startTime, + detailProvider != nullptr ? detailProvider() : nullptr); } } } void PerformanceEntryReporter::traceMeasure( - const PerformanceMeasure& entry) const { + const PerformanceMeasure& entry, + UserTimingDetailProvider&& detailProvider) const { auto& performanceTracer = jsinspector_modern::tracing::PerformanceTracer::getInstance(); if (performanceTracer.isTracing() || ReactPerfettoLogger::isTracing()) { - auto [trackName, eventName] = parseTrackName(entry.name); - - if (performanceTracer.isTracing()) { - std::optional - trackMetadata; - - if (trackName.has_value()) { - trackMetadata = {.track = trackName.value()}; - } - performanceTracer.reportMeasure( - eventName, entry.startTime, entry.duration, trackMetadata); - } + auto detail = detailProvider != nullptr ? detailProvider() : nullptr; if (ReactPerfettoLogger::isTracing()) { ReactPerfettoLogger::measure( - eventName, + entry.name, entry.startTime, entry.startTime + entry.duration, - trackName); + detail != nullptr ? getTrackFromDetail(detail) : std::nullopt, + detail != nullptr ? getTrackGroupFromDetail(detail) : std::nullopt); + } + + if (performanceTracer.isTracing()) { + performanceTracer.reportMeasure( + entry.name, entry.startTime, entry.duration, std::move(detail)); } } } diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h index 434625bdf8f924..d66427b230fc7e 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h @@ -11,7 +11,7 @@ #include "PerformanceEntryKeyedBuffer.h" #include "PerformanceObserverRegistry.h" -#include +#include #include #include @@ -86,14 +86,18 @@ class PerformanceEntryReporter { std::optional getMarkTime( const std::string& markName) const; - void reportMark(const std::string& name, HighResTimeStamp startTime); + using UserTimingDetailProvider = std::function; + + void reportMark( + const std::string& name, + HighResTimeStamp startTime, + UserTimingDetailProvider&& detailProvider = nullptr); void reportMeasure( const std::string& name, HighResTimeStamp startTime, HighResDuration duration, - const std::optional& - trackMetadata = std::nullopt); + UserTimingDetailProvider&& detailProvider = nullptr); void reportEvent( std::string name, @@ -167,8 +171,12 @@ class PerformanceEntryReporter { throw std::logic_error("Unhandled PerformanceEntryType"); } - void traceMark(const PerformanceMark& entry) const; - void traceMeasure(const PerformanceMeasure& entry) const; + void traceMark( + const PerformanceMark& entry, + UserTimingDetailProvider&& detailProvider) const; + void traceMeasure( + const PerformanceMeasure& entry, + UserTimingDetailProvider&& detailProvider) const; }; } // namespace facebook::react