diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp index 1a3415fc6a1a..e1d607c700c1 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp @@ -11,6 +11,16 @@ namespace facebook::react::jsinspector_modern { +namespace { + +/** + * Threshold for the size Trace Event chunk, that will be flushed out with + * Tracing.dataCollected event. + */ +const uint16_t TRACE_EVENT_CHUNK_SIZE = 1000; + +} // namespace + bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { if (req.method == "Tracing.start") { // @cdp Tracing.start support is experimental. @@ -26,31 +36,32 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { return true; } else if (req.method == "Tracing.end") { // @cdp Tracing.end support is experimental. - bool firstChunk = true; - auto id = req.id; - bool wasStopped = - PerformanceTracer::getInstance().stopTracingAndCollectEvents( - [this, firstChunk, id](const folly::dynamic& eventsChunk) { - if (firstChunk) { - frontendChannel_(cdp::jsonResult(id)); - } - frontendChannel_(cdp::jsonNotification( - "Tracing.dataCollected", - folly::dynamic::object("value", eventsChunk))); - }); - - if (!wasStopped) { + bool correctlyStopped = PerformanceTracer::getInstance().stopTracing(); + + if (!correctlyStopped) { frontendChannel_(cdp::jsonError( req.id, cdp::ErrorCode::InternalError, "Tracing session not started")); - } else { - frontendChannel_(cdp::jsonNotification( - "Tracing.tracingComplete", - folly::dynamic::object("dataLossOccurred", false))); + + return true; } + // Send response to Tracing.end request. frontendChannel_(cdp::jsonResult(req.id)); + + PerformanceTracer::getInstance().collectEvents( + [this](const folly::dynamic& eventsChunk) { + frontendChannel_(cdp::jsonNotification( + "Tracing.dataCollected", + folly::dynamic::object("value", eventsChunk))); + }, + TRACE_EVENT_CHUNK_SIZE); + + frontendChannel_(cdp::jsonNotification( + "Tracing.tracingComplete", + folly::dynamic::object("dataLossOccurred", false))); + return true; } diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp index dad7b7cd5582..737e40e12cf1 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp @@ -38,19 +38,24 @@ bool PerformanceTracer::startTracing() { return true; } -bool PerformanceTracer::stopTracingAndCollectEvents( - const std::function& - resultCallback) { +bool PerformanceTracer::stopTracing() { std::lock_guard lock(mutex_); - if (!tracing_) { return false; } - tracing_ = false; + return true; +} + +void PerformanceTracer::collectEvents( + const std::function& + resultCallback, + uint16_t chunkSize) { + std::lock_guard lock(mutex_); + if (buffer_.empty()) { customTrackIdMap_.clear(); - return true; + return; } // Register "Main" process @@ -91,24 +96,21 @@ bool PerformanceTracer::stopTracingAndCollectEvents( } auto traceEvents = folly::dynamic::array(); - for (auto event : buffer_) { // Emit trace events traceEvents.push_back(serializeTraceEvent(event)); - if (traceEvents.size() >= 1000) { + if (traceEvents.size() == chunkSize) { resultCallback(traceEvents); traceEvents = folly::dynamic::array(); } } - customTrackIdMap_.clear(); - buffer_.clear(); - - if (traceEvents.size() >= 1) { + if (!traceEvents.empty()) { resultCallback(traceEvents); } - return true; + customTrackIdMap_.clear(); + buffer_.clear(); } void PerformanceTracer::reportMark( diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h index 2a8ccb31967e..19af05a16aa7 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h @@ -8,6 +8,7 @@ #pragma once #include "CdpTracing.h" +#include "TraceEvent.h" #include @@ -21,51 +22,6 @@ namespace facebook::react::jsinspector_modern { // TODO: Review how this API is integrated into jsinspector_modern (singleton // design is copied from earlier FuseboxTracer prototype). -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 TraceEvent { - /** The name of the event, as displayed in the Trace Viewer. */ - std::string name; - - /** - * 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; -}; - -} // namespace - /** * [Experimental] An interface for logging performance trace events to the * modern debugger server. @@ -80,15 +36,17 @@ class PerformanceTracer { bool startTracing(); /** - * End tracing, and output chunked CDP trace events using the given - * callback. - * - * Returns `false` if tracing was not started. + * Mark trace session as stopped. Returns `false` if wasn't tracing. */ - bool stopTracingAndCollectEvents( - const std::function& - resultCallback); + bool stopTracing(); + /** + * Flush out buffered CDP Trace Events using the given callback. + */ + void collectEvents( + const std::function& + resultCallback, + uint16_t chunkSize); /** * Record a `Performance.mark()` event - a labelled timestamp. If not * currently tracing, this is a no-op. diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEvent.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEvent.h new file mode 100644 index 000000000000..f9b31f26a1ee --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEvent.h @@ -0,0 +1,59 @@ +/* + * 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 { + +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 TraceEvent { + /** The name of the event, as displayed in the Trace Viewer. */ + std::string name; + + /** + * 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; +}; + +} // namespace + +} // namespace facebook::react::jsinspector_modern