diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp index 8bddc62df1b601..cbba48643ded20 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp @@ -49,12 +49,11 @@ bool PerformanceTracer::stopTracingAndCollectEvents( tracing_ = false; if (buffer_.empty()) { + customTrackIdMap_.clear(); return true; } auto traceEvents = folly::dynamic::array(); - auto savedBuffer = std::move(buffer_); - buffer_.clear(); // Register "Main" process traceEvents.push_back(folly::dynamic::object( @@ -69,27 +68,29 @@ bool PerformanceTracer::stopTracingAndCollectEvents( "cat", "__metadata")("name", "thread_name")("ph", "M")("pid", PID)( "tid", USER_TIMINGS_DEFAULT_TRACK)("ts", 0)); - auto customTrackIdMap = getCustomTracks(savedBuffer); - for (const auto& [trackName, trackId] : customTrackIdMap) { + 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)); } - for (auto& event : savedBuffer) { + for (auto event : buffer_) { // Emit trace events - traceEvents.push_back(serializeTraceEvent(event, customTrackIdMap)); + traceEvents.push_back(serializeTraceEvent(event)); if (traceEvents.size() >= 1000) { resultCallback(traceEvents); traceEvents = folly::dynamic::array(); } } + customTrackIdMap_.clear(); + buffer_.clear(); if (traceEvents.size() >= 1) { resultCallback(traceEvents); } + return true; } @@ -100,8 +101,15 @@ void PerformanceTracer::reportMark( if (!tracing_) { return; } - buffer_.push_back(TraceEvent{ - .type = TraceEventType::MARK, .name = std::string(name), .start = start}); + + 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); } void PerformanceTracer::reportMeasure( @@ -114,62 +122,85 @@ void PerformanceTracer::reportMeasure( return; } - std::optional track; + uint64_t threadId = + USER_TIMINGS_DEFAULT_TRACK; // FIXME: This should be real thread ID. if (trackMetadata.has_value()) { - track = trackMetadata.value().track; + std::string trackName = trackMetadata.value().track; + + if (!customTrackIdMap_.contains(trackName)) { + uint64_t trackId = + USER_TIMINGS_DEFAULT_TRACK + customTrackIdMap_.size() + 1; + threadId = trackId; + + customTrackIdMap_.emplace(trackName, trackId); + } } - buffer_.push_back(TraceEvent{ - .type = TraceEventType::MEASURE, - .name = std::string(name), - .start = start, - .duration = duration, - .track = track}); + 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); } -std::unordered_map PerformanceTracer::getCustomTracks( - std::vector& events) { - std::unordered_map trackIdMap; - - uint32_t nextTrack = USER_TIMINGS_DEFAULT_TRACK + 1; - for (auto& event : events) { - // Custom tracks are only supported by User Timing "measure" events - if (event.type != TraceEventType::MEASURE) { - continue; +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"); } - if (event.track.has_value() && !trackIdMap.contains(event.track.value())) { - auto trackId = nextTrack++; - trackIdMap[event.track.value()] = trackId; - } + result += ","; } - return trackIdMap; + if (result.length() > 0) { + result.pop_back(); + } + + return result; } folly::dynamic PerformanceTracer::serializeTraceEvent( - TraceEvent& event, - std::unordered_map& customTrackIdMap) const { + TraceEventBase* event) const { folly::dynamic result = folly::dynamic::object; - result["args"] = folly::dynamic::object(); - result["cat"] = "blink.user_timing"; - result["name"] = event.name; - result["pid"] = PID; - result["ts"] = event.start; + 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::MARK: + switch (event->type) { + case TraceEventType::Instant: result["ph"] = "I"; - result["tid"] = USER_TIMINGS_DEFAULT_TRACK; + break; - case TraceEventType::MEASURE: - result["dur"] = event.duration; + + case TraceEventType::Complete: { result["ph"] = "X"; - result["tid"] = event.track.has_value() - ? customTrackIdMap[event.track.value()] - : USER_TIMINGS_DEFAULT_TRACK; + + auto completeEvent = static_cast(event); + result["dur"] = completeEvent->duration; + break; + } + + default: + throw std::runtime_error("Unknown trace event type"); } 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 f7b9e1e17e58e1..a52a48ae763cd1 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h @@ -22,16 +22,78 @@ namespace facebook::react::jsinspector_modern { // design is copied from earlier FuseboxTracer prototype). enum class TraceEventType { - MARK = 1, - MEASURE = 2, + Instant, + Complete, }; -struct TraceEvent { - TraceEventType type; +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. + + * https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview?pli=1&tab=t.0#heading=h.yr4qxyxotyw +*/ +struct TraceEventBase { std::string name; - uint64_t start; - uint64_t duration = 0; - std::optional track; + std::vector categories; + TraceEventType type; + uint64_t timestamp; + uint64_t processId; + uint64_t threadId; + folly::dynamic args = folly::dynamic::object(); +}; + +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} {} }; /** @@ -83,14 +145,12 @@ class PerformanceTracer { PerformanceTracer& operator=(const PerformanceTracer&) = delete; ~PerformanceTracer() = default; - std::unordered_map getCustomTracks( - std::vector& events); - folly::dynamic serializeTraceEvent( - TraceEvent& event, - std::unordered_map& customTrackIdMap) const; + std::string serializeTraceEventCategories(TraceEventBase* event) const; + folly::dynamic serializeTraceEvent(TraceEventBase* event) const; bool tracing_{false}; - std::vector buffer_; + std::unordered_map customTrackIdMap_; + std::vector buffer_; std::mutex mutex_; };