diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp index 24bcb0d25e11..bf9d31d24642 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp @@ -9,6 +9,7 @@ #include #include +#include namespace facebook::react::jsinspector_modern { @@ -29,6 +30,26 @@ const uint16_t TRACE_EVENT_CHUNK_SIZE = 1000; */ const uint16_t PROFILE_TRACE_EVENT_CHUNK_SIZE = 1; +void serializeTraceEventsInChunks( + std::vector&& traceEvents, + uint16_t chunkSize, + const std::function& resultCallback) { + auto serializedTraceEvents = folly::dynamic::array(); + for (auto&& traceEvent : traceEvents) { + // Emit trace events + serializedTraceEvents.push_back( + tracing::TraceEventSerializer::serialize(std::move(traceEvent))); + + if (serializedTraceEvents.size() == chunkSize) { + resultCallback(std::move(serializedTraceEvents)); + serializedTraceEvents = folly::dynamic::array(); + } + } + if (!serializedTraceEvents.empty()) { + resultCallback(std::move(serializedTraceEvents)); + } +} + } // namespace TracingAgent::TracingAgent( @@ -89,8 +110,8 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { tracing::PerformanceTracer& performanceTracer = tracing::PerformanceTracer::getInstance(); - bool correctlyStopped = performanceTracer.stopTracing(); - if (!correctlyStopped) { + auto collectedEvents = performanceTracer.stopTracing(); + if (!collectedEvents) { frontendChannel_(cdp::jsonError( req.id, cdp::ErrorCode::InternalError, @@ -107,8 +128,11 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { "Tracing.dataCollected", folly::dynamic::object("value", std::move(eventsChunk)))); }; - performanceTracer.collectEvents( - dataCollectedCallback, TRACE_EVENT_CHUNK_SIZE); + + serializeTraceEventsInChunks( + std::move(*collectedEvents), + TRACE_EVENT_CHUNK_SIZE, + dataCollectedCallback); auto tracingProfile = instanceAgent_->collectTracingProfile(); tracing::IdGenerator profileIdGenerator; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp index e287b6f584d4..424b9ad11b8c 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp @@ -28,38 +28,50 @@ PerformanceTracer::PerformanceTracer() bool PerformanceTracer::startTracing() { std::lock_guard lock(mutex_); + if (tracingAtomic_) { return false; } - tracingAtomic_ = true; - - buffer_.emplace_back(TraceEvent{ - .name = "TracingStartedInPage", - .cat = "disabled-by-default-devtools.timeline", - .ph = 'I', - .ts = HighResTimeStamp::now(), - .pid = processId_, - .tid = oscompat::getCurrentThreadId(), - .args = folly::dynamic::object("data", folly::dynamic::object()), - }); + tracingAtomic_ = true; + currentTraceStartTime_ = HighResTimeStamp::now(); return true; } -bool PerformanceTracer::stopTracing() { - std::lock_guard lock(mutex_); - if (!tracingAtomic_) { - return false; +std::optional> PerformanceTracer::stopTracing() { + std::vector events; + + { + std::lock_guard lock(mutex_); + + if (!tracingAtomic_) { + return std::nullopt; + } + + tracingAtomic_ = false; + performanceMeasureCount_ = 0; + + buffer_.swap(events); } - tracingAtomic_ = false; // This is synthetic Trace Event, which should not be represented on a // timeline. CDT is not using Profile or ProfileChunk events for determining // trace timeline window, this is why trace that only contains JavaScript - // samples will be displayed as empty. We use this event to avoid that. + // samples will be displayed as empty. We use these events to avoid that. // This could happen for non-bridgeless apps, where Performance interface is // not supported and no spec-compliant Event Loop implementation. - buffer_.emplace_back(TraceEvent{ + + events.emplace_back(TraceEvent{ + .name = "TracingStartedInPage", + .cat = "disabled-by-default-devtools.timeline", + .ph = 'I', + .ts = currentTraceStartTime_, + .pid = processId_, + .tid = oscompat::getCurrentThreadId(), + .args = folly::dynamic::object("data", folly::dynamic::object()), + }); + + events.emplace_back(TraceEvent{ .name = "ReactNative-TracingStopped", .cat = "disabled-by-default-devtools.timeline", .ph = 'I', @@ -68,75 +80,6 @@ bool PerformanceTracer::stopTracing() { .tid = oscompat::getCurrentThreadId(), }); - performanceMeasureCount_ = 0; - return true; -} - -void PerformanceTracer::collectEvents( - const std::function& resultCallback, - uint16_t chunkSize) { - std::vector localBuffer; - { - std::lock_guard lock(mutex_); - buffer_.swap(localBuffer); - } - - if (localBuffer.empty()) { - return; - } - - auto serializedTraceEvents = folly::dynamic::array(); - for (auto&& event : localBuffer) { - // Emit trace events - serializedTraceEvents.push_back( - TraceEventSerializer::serialize(std::move(event))); - - if (serializedTraceEvents.size() == chunkSize) { - resultCallback(std::move(serializedTraceEvents)); - serializedTraceEvents = folly::dynamic::array(); - } - } - if (!serializedTraceEvents.empty()) { - resultCallback(std::move(serializedTraceEvents)); - } -} - -folly::dynamic PerformanceTracer::collectEvents(uint16_t chunkSize) { - std::vector localBuffer; - { - std::lock_guard lock(mutex_); - buffer_.swap(localBuffer); - } - - auto chunks = folly::dynamic::array(); - if (localBuffer.empty()) { - return chunks; - } - - auto chunk = folly::dynamic::array(); - chunk.reserve(chunkSize); - for (auto&& event : localBuffer) { - chunk.push_back(TraceEventSerializer::serialize(std::move(event))); - - if (chunk.size() == chunkSize) { - chunks.push_back(std::move(chunk)); - chunk = folly::dynamic::array(); - chunk.reserve(chunkSize); - } - } - - if (!chunk.empty()) { - chunks.push_back(std::move(chunk)); - } - return chunks; -} - -std::vector PerformanceTracer::collectTraceEvents() { - std::vector events; - { - std::lock_guard lock(mutex_); - buffer_.swap(events); - } return events; } diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h index 088f98a77586..7b83cff0a4ec 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h @@ -15,7 +15,6 @@ #include #include -#include #include #include #include @@ -34,14 +33,15 @@ class PerformanceTracer { static PerformanceTracer& getInstance(); /** - * Mark trace session as started. Returns `false` if already tracing. + * Starts a tracing session. Returns `false` if already tracing. */ bool startTracing(); /** - * Mark trace session as stopped. Returns `false` if wasn't tracing. + * If there is a current tracing session, it stops tracing and returns all + * collected events. Otherwise, it returns empty. */ - bool stopTracing(); + std::optional> stopTracing(); /** * Returns whether the tracer is currently tracing. This can be useful to @@ -52,25 +52,6 @@ class PerformanceTracer { return tracingAtomic_; } - /** - * Flush out buffered CDP Trace Events using the given callback. - */ - void collectEvents( - const std::function& resultCallback, - uint16_t chunkSize); - - /** - * Flush out buffered CDP Trace Events into a folly::dynamic collection of - * chunks, which can be sent over CDP later. - */ - folly::dynamic collectEvents(uint16_t chunkSize); - - /** - * Transfers an ownership of all buffered TraceEvents, the local buffer state - * is invalidated after this call. - */ - std::vector collectTraceEvents(); - /** * Record a `Performance.mark()` event - a labelled timestamp. If not * currently tracing, this is a no-op. @@ -168,7 +149,10 @@ class PerformanceTracer { */ uint32_t performanceMeasureCount_{0}; + HighResTimeStamp currentTraceStartTime_; + std::vector buffer_; + /** * Protects data members of this class for concurrent access, including * the tracingAtomic_, in order to eliminate potential "logic" races.