From dfdacb84ce2371c5642b4bd875040f6974b63818 Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Tue, 17 Dec 2024 10:57:39 -0800 Subject: [PATCH] Refactor PerformanceTracer buffer type to use output event format (#48310) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48310 Refactors the internal storage format of trace events buffered by `PerformanceTracer`. Aligning with the emitted [Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview?pli=1&tab=t.0#heading=h.yr4qxyxotyw) enables us to simplify away the issue of defining and converting from any intermediate formats. This becomes desirable as we generalise to more event types and forthcoming browser-emulating `__metadata` events. Changelog: [Internal] Reviewed By: hoxyq Differential Revision: D67337442 fbshipit-source-id: 580928dfb4fcf4ac5efc82f93ab85a0d1d6dfb5c --- .../tracing/PerformanceTracer.cpp | 146 ++++++++---------- .../tracing/PerformanceTracer.h | 111 +++++-------- 2 files changed, 102 insertions(+), 155 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp index cbba48643de..dad7b7cd558 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp @@ -53,28 +53,45 @@ bool PerformanceTracer::stopTracingAndCollectEvents( return true; } - 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)); + buffer_.push_back(TraceEvent{ + .name = "process_name", + .cat = "__metadata", + .ph = 'M', + .ts = 0, + .pid = PID, + .tid = 0, + .args = folly::dynamic::object("name", "Main"), + }); // Register "Timings" track - // NOTE: This is a hack to make the trace viewer show a "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", USER_TIMINGS_DEFAULT_TRACK)("ts", 0)); + buffer_.push_back(TraceEvent{ + .name = "thread_name", + .cat = "__metadata", + .ph = 'M', + .ts = 0, + .pid = PID, + .tid = USER_TIMINGS_DEFAULT_TRACK, + .args = folly::dynamic::object("name", "Timings"), + }); for (const auto& [trackName, trackId] : customTrackIdMap_) { // Register custom tracks - traceEvents.push_back(folly::dynamic::object( - "args", folly::dynamic::object("name", trackName))("cat", "__metadata")( - "name", "thread_name")("ph", "M")("pid", PID)("tid", trackId)("ts", 0)); + buffer_.push_back(TraceEvent{ + .name = "thread_name", + .cat = "__metadata", + .ph = 'M', + .ts = 0, + .pid = PID, + .tid = trackId, + .args = folly::dynamic::object("name", trackName), + }); } + auto traceEvents = folly::dynamic::array(); + for (auto event : buffer_) { // Emit trace events traceEvents.push_back(serializeTraceEvent(event)); @@ -102,14 +119,15 @@ void PerformanceTracer::reportMark( return; } - TraceEventBase* event = new InstantTraceEvent{ - std::string(name), - std::vector{TraceEventCategory::UserTiming}, - start, - PID, // FIXME: This should be real process ID. - USER_TIMINGS_DEFAULT_TRACK, // FIXME: This should be real thread ID. - }; - buffer_.push_back(event); + buffer_.push_back(TraceEvent{ + .name = std::string(name), + .cat = "blink.user_timing", + .ph = 'I', + .ts = start, + .pid = PID, // FIXME: This should be the real process ID. + .tid = USER_TIMINGS_DEFAULT_TRACK, // FIXME: This should be the real + // thread ID. + }); } void PerformanceTracer::reportMeasure( @@ -122,8 +140,10 @@ void PerformanceTracer::reportMeasure( return; } - uint64_t threadId = - USER_TIMINGS_DEFAULT_TRACK; // FIXME: This should be real thread ID. + // NOTE: We synthetically create custom tracks as a hack to render them in + // our current build of Chrome DevTools frontend. + // TODO: Remove and align with web. + uint64_t threadId = USER_TIMINGS_DEFAULT_TRACK; if (trackMetadata.has_value()) { std::string trackName = trackMetadata.value().track; @@ -136,71 +156,29 @@ void PerformanceTracer::reportMeasure( } } - TraceEventBase* event = new CompleteTraceEvent{ - std::string(name), - std::vector{TraceEventCategory::UserTiming}, - start, - PID, // FIXME: This should be real process ID. - threadId, // FIXME: This should be real thread ID. - duration}; - buffer_.push_back(event); + buffer_.push_back(TraceEvent{ + .name = std::string(name), + .cat = "blink.user_timing", + .ph = 'X', + .ts = start, + .pid = PID, // FIXME: This should be the real process ID. + .tid = threadId, // FIXME: This should be the real thread ID. + .dur = duration, + }); } -std::string PerformanceTracer::serializeTraceEventCategories( - TraceEventBase* event) const { - std::string result; - - for (const auto& category : event->categories) { - switch (category) { - case TraceEventCategory::UserTiming: - result += "blink.user_timing"; - break; - case TraceEventCategory::TimelineEvent: - result += "disabled-by-default-devtools.timeline"; - break; - - default: - throw std::runtime_error("Unknown trace event category"); - } - - result += ","; - } - - if (result.length() > 0) { - result.pop_back(); - } - - return result; -} - -folly::dynamic PerformanceTracer::serializeTraceEvent( - TraceEventBase* event) const { +folly::dynamic PerformanceTracer::serializeTraceEvent(TraceEvent event) const { folly::dynamic result = folly::dynamic::object; - result["name"] = event->name; - result["cat"] = serializeTraceEventCategories(event); - result["args"] = event->args; - result["ts"] = event->timestamp; - result["pid"] = event->processId; - result["tid"] = event->threadId; - - switch (event->type) { - case TraceEventType::Instant: - result["ph"] = "I"; - - break; - - case TraceEventType::Complete: { - result["ph"] = "X"; - - auto completeEvent = static_cast(event); - result["dur"] = completeEvent->duration; - - break; - } - - default: - throw std::runtime_error("Unknown trace event type"); + result["name"] = event.name; + result["cat"] = event.cat; + result["ph"] = std::string(1, event.ph); + result["ts"] = event.ts; + result["pid"] = event.pid; + result["tid"] = event.tid; + result["args"] = event.args; + if (event.dur.has_value()) { + result["dur"] = event.dur.value(); } return result; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h index a52a48ae763..2a8ccb31967 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h @@ -21,80 +21,50 @@ namespace facebook::react::jsinspector_modern { // TODO: Review how this API is integrated into jsinspector_modern (singleton // design is copied from earlier FuseboxTracer prototype). -enum class TraceEventType { - Instant, - Complete, -}; - -enum class TraceEventCategory { - UserTiming, - TimelineEvent, -}; - -/* - * Based on the out-of-date "Trace Event Format" document from Google and our - * findings while reverse engineering the contract between Chrome and Chrome - * DevTools. +namespace { +/** + * A trace event to send to the debugger frontend, as defined by the Trace Event + * Format. * https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview?pli=1&tab=t.0#heading=h.yr4qxyxotyw -*/ -struct TraceEventBase { + */ +struct TraceEvent { + /** The name of the event, as displayed in the Trace Viewer. */ std::string name; - std::vector categories; - TraceEventType type; - uint64_t timestamp; - uint64_t processId; - uint64_t threadId; + + /** + * A comma separated list of categories for the event, configuring how + * events are shown in the Trace Viewer UI. + */ + std::string cat; + + /** + * The event type. This is a single character which changes depending on the + * type of event being output. See + * https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview?pli=1&tab=t.0#heading=h.puwqg050lyuy + */ + char ph; + + /** The tracing clock timestamp of the event, in microseconds (µs). */ + uint64_t ts; + + /** The process ID for the process that output this event. */ + uint64_t pid; + + /** The thread ID for the process that output this event. */ + uint64_t tid; + + /** Any arguments provided for the event. */ folly::dynamic args = folly::dynamic::object(); + + /** + * The duration of the event, in microseconds (µs). Only applicable to + * complete events ("ph": "X"). + */ + std::optional dur; }; -template -struct TraceEvent : public TraceEventBase { - TraceEvent( - std::string name, - std::vector categories, - uint64_t timestamp, - uint64_t processId, - uint64_t threadId) - : TraceEventBase{ - std::move(name), - std::move(categories), - T, - timestamp, - processId, - threadId} {} -}; - -struct CompleteTraceEvent : public TraceEvent { - uint64_t duration; - - CompleteTraceEvent( - std::string name, - std::vector categories, - uint64_t timestamp, - uint64_t processId, - uint64_t threadId, - uint64_t duration) - : TraceEvent< - TraceEventType:: - Complete>{std::move(name), std::move(categories), timestamp, processId, threadId}, - duration(duration) {} -}; - -struct InstantTraceEvent : public TraceEvent { - InstantTraceEvent( - std::string name, - std::vector categories, - uint64_t timestamp, - uint64_t processId, - uint64_t threadId) - : TraceEvent{ - std::move(name), - std::move(categories), - timestamp, - processId, - threadId} {} -}; +} // namespace /** * [Experimental] An interface for logging performance trace events to the @@ -145,12 +115,11 @@ class PerformanceTracer { PerformanceTracer& operator=(const PerformanceTracer&) = delete; ~PerformanceTracer() = default; - std::string serializeTraceEventCategories(TraceEventBase* event) const; - folly::dynamic serializeTraceEvent(TraceEventBase* event) const; + folly::dynamic serializeTraceEvent(TraceEvent event) const; bool tracing_{false}; std::unordered_map customTrackIdMap_; - std::vector buffer_; + std::vector buffer_; std::mutex mutex_; };