From 11e94d42d69fb7d7092509cd93822b89ddeeb5e8 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Tue, 5 Aug 2025 13:04:39 -0700 Subject: [PATCH 01/11] Create a local session for Tracing Summary: # Changelog: [Internal] The current design for Tracing is flawed. Right now the logic is mostly scattered around CDP Agents, lifetime of which is tied to CDP session. This diff introduces a new approach: - The HostTarget will be the only public entrypoints to startTracing as part of the jsinspector backend. - It will create and own TraceRecording that acts as a local session. We won't use wording session here, because it is already reserved for CDP case. - Every Target will implement TracingAgent, lifetime of which will be limited by lifetime of either Target or TraceRecording. - All these TracingAgent will have a reference to TraceRecordingState, which they can mutate This approach unblocks: - Recording traces without active CDP sessions, for example in a background. - Recording full instance reloads and multiple profiles for Instances and Runtimes. {F1980838032} Differential Revision: D79371359 --- .../jsinspector-modern/HostAgent.cpp | 13 ++++ .../jsinspector-modern/HostAgent.h | 22 ++++++ .../jsinspector-modern/HostTarget.cpp | 17 +++++ .../jsinspector-modern/HostTarget.h | 32 +++++++++ .../HostTargetTraceRecording.cpp | 49 +++++++++++++ .../HostTargetTraceRecording.h | 71 +++++++++++++++++++ .../jsinspector-modern/HostTargetTracing.cpp | 41 +++++++++++ .../jsinspector-modern/InstanceAgent.cpp | 13 ++++ .../jsinspector-modern/InstanceAgent.h | 22 ++++++ .../jsinspector-modern/InstanceTarget.cpp | 27 +++++++ .../jsinspector-modern/InstanceTarget.h | 22 ++++++ .../jsinspector-modern/RuntimeAgent.cpp | 5 ++ .../jsinspector-modern/RuntimeAgent.h | 15 ++++ .../jsinspector-modern/RuntimeTarget.cpp | 12 ++++ .../jsinspector-modern/RuntimeTarget.h | 20 ++++++ .../tracing/TargetTracingAgent.h | 33 +++++++++ .../tracing/TraceRecordingState.h | 17 +++++ 17 files changed, 431 insertions(+) create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.h create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/tracing/TargetTracingAgent.h create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp index 35fc991859b8..566d1a7fda0f 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp @@ -462,4 +462,17 @@ void HostAgent::setCurrentInstanceAgent( impl_->setCurrentInstanceAgent(std::move(instanceAgent)); } +#pragma mark - Tracing + +HostTracingAgent::HostTracingAgent(tracing::TraceRecordingState& state) + : tracing::TargetTracingAgent(state) {} + +void HostTracingAgent::setTracedInstance(InstanceTarget* instanceTarget) { + if (instanceTarget != nullptr) { + instanceTracingAgent_ = instanceTarget->createTracingAgent(state_); + } else { + instanceTracingAgent_ = nullptr; + } +} + } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h index 6689aa021abc..ac4728062903 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace facebook::react::jsinspector_modern { @@ -75,4 +76,25 @@ class HostAgent final { std::unique_ptr impl_; }; +#pragma mark - Tracing + +/** + * An Agent that handles Tracing events for a particular InstanceTarget. + * + * Lifetime of this agent is bound to the lifetime of the Tracing session - + * HostTargetTraceRecording. + */ +class HostTracingAgent : tracing::TargetTracingAgent { + public: + explicit HostTracingAgent(tracing::TraceRecordingState& state); + + /** + * Registers the InstanceTarget with this tracing agent. + */ + void setTracedInstance(InstanceTarget* instanceTarget); + + private: + std::shared_ptr instanceTracingAgent_{nullptr}; +}; + } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp index ee6d29ed6bae..9d7a9810d6e9 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp @@ -7,6 +7,7 @@ #include "HostTarget.h" #include "HostAgent.h" +#include "HostTargetTraceRecording.h" #include "InspectorInterfaces.h" #include "InspectorUtilities.h" #include "InstanceTarget.h" @@ -179,6 +180,8 @@ HostTarget::~HostTarget() { assert( sessions_.empty() && "HostTargetSession objects must be destroyed before their HostTarget. Did you call getInspectorInstance().removePage()?"); + // Trace Recording object (traceRecording_) doesn't create an actual session, + // so we don't need to reset it explicitly here. } HostTargetDelegate::~HostTargetDelegate() = default; @@ -191,6 +194,13 @@ InstanceTarget& HostTarget::registerInstance(InstanceTargetDelegate& delegate) { [currentInstance = &*currentInstance_](HostTargetSession& session) { session.setCurrentInstance(currentInstance); }); + + if (traceRecording_) { + // Registers the Instance for tracing, if a Trace is currently being + // recorded. + traceRecording_->setTracedInstance(currentInstance_.get()); + } + return *currentInstance_; } @@ -200,6 +210,13 @@ void HostTarget::unregisterInstance(InstanceTarget& instance) { "Invalid unregistration"); sessions_.forEach( [](HostTargetSession& session) { session.setCurrentInstance(nullptr); }); + + if (traceRecording_) { + // Unregisters the Instance for tracing, if a Trace is currently being + // recorded. + traceRecording_->setTracedInstance(nullptr); + } + currentInstance_.reset(); } diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h index 938271857198..8bc2494101bb 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h @@ -34,8 +34,10 @@ namespace facebook::react::jsinspector_modern { class HostTargetSession; class HostAgent; +class HostTracingAgent; class HostCommandSender; class HostTarget; +class HostTargetTraceRecording; struct HostTargetMetadata { std::optional appDisplayName; @@ -237,6 +239,28 @@ class JSINSPECTOR_EXPORT HostTarget */ void sendCommand(HostCommand command); + /** + * Creates a new HostTracingAgent. + * This Agent is not owned by the HostTarget. The Agent will be destroyed at + * the end of the tracing session. + * + * \param state A reference to the state of the active trace recording. + */ + std::shared_ptr createTracingAgent( + tracing::TraceRecordingState& state); + + /** + * Starts trace recording for this HostTarget. + * + * \return false if already tracing, true otherwise. + */ + bool startTracing(); + + /** + * Stops previously started trace recording. + */ + tracing::TraceRecordingState stopTracing(); + private: /** * Constructs a new HostTarget. @@ -257,6 +281,14 @@ class JSINSPECTOR_EXPORT HostTarget std::shared_ptr currentInstance_{nullptr}; std::unique_ptr commandSender_; + /** + * Current pending trace recording, which encapsulates the configuration of + * the tracing session and the state. + * + * Should only be allocated when there is an active tracing session. + */ + std::unique_ptr traceRecording_{nullptr}; + inline HostTargetDelegate& getDelegate() { return delegate_; } diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp new file mode 100644 index 000000000000..406fab16eef2 --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#include "HostTargetTraceRecording.h" +#include "HostTarget.h" + +namespace facebook::react::jsinspector_modern { + +HostTargetTraceRecording::HostTargetTraceRecording(HostTarget& hostTarget) + : hostTarget_(hostTarget) {} + +void HostTargetTraceRecording::setTracedInstance( + InstanceTarget* instanceTarget) { + // If HostTracingAgent is allocated, it means that there is an active tracing + // recording session. + if (hostTracingAgent_ != nullptr) { + hostTracingAgent_->setTracedInstance(instanceTarget); + } +} + +void HostTargetTraceRecording::start() { + assert( + hostTracingAgent_ == nullptr && + "Tracing Agent for the HostTarget was already initialized."); + + state_ = tracing::TraceRecordingState{}; + hostTracingAgent_ = hostTarget_.createTracingAgent(*state_); +} + +tracing::TraceRecordingState HostTargetTraceRecording::stop() { + assert( + hostTracingAgent_ != nullptr && + "TracingAgent for the HostTarget has not been initialized."); + hostTracingAgent_.reset(); + + assert( + state_.has_value() && + "The state for this tracing session has not been initialized."); + auto state = std::move(*state_); + state_.reset(); + + return state; +} + +} // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.h b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.h new file mode 100644 index 000000000000..963c931dad15 --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.h @@ -0,0 +1,71 @@ +/* + * 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 "HostAgent.h" +#include "HostTarget.h" +#include "InstanceTarget.h" + +#include + +#include + +namespace facebook::react::jsinspector_modern { + +/** + * A local representation of the Tracing "session". + * + * Owned by the HostTarget and should only be allocated during an active + * recording. + * + * Owns all allocated Tracing Agents. A single Target can have a single active + * Tracing Agent, but only as a std::weak_ptr. + */ +class HostTargetTraceRecording { + public: + explicit HostTargetTraceRecording(HostTarget& hostTarget); + + /** + * Updates the current traced Instance for this recording. + */ + void setTracedInstance(InstanceTarget* instanceTarget); + + /** + * Starts the recording. + * + * Will allocate all Tracing Agents for all currently registered Targets. + */ + void start(); + + /** + * Stops the recording and drops the recording state. + * + * Will deallocate all Tracing Agents. + */ + tracing::TraceRecordingState stop(); + + private: + /** + * The Host for which this Trace Recording is going to happen. + */ + HostTarget& hostTarget_; + + /** + * The state of the current Trace Recording. + * Only allocated if the recording is enabled. + */ + std::optional state_; + + /** + * The TracingAgent of the targeted Host. + * Only allocated if the recording is enabled. + */ + std::shared_ptr hostTracingAgent_; +}; + +} // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp new file mode 100644 index 000000000000..45d08dc95713 --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#include "HostTarget.h" +#include "HostTargetTraceRecording.h" + +namespace facebook::react::jsinspector_modern { + +std::shared_ptr HostTarget::createTracingAgent( + tracing::TraceRecordingState& state) { + auto agent = std::make_shared(state); + agent->setTracedInstance(currentInstance_.get()); + return agent; +} + +bool HostTarget::startTracing() { + if (traceRecording_ != nullptr) { + return false; + } + + traceRecording_ = std::make_unique(*this); + traceRecording_->setTracedInstance(currentInstance_.get()); + traceRecording_->start(); + + return true; +} + +tracing::TraceRecordingState HostTarget::stopTracing() { + assert(traceRecording_ != nullptr && "No tracing in progress"); + + auto state = traceRecording_->stop(); + traceRecording_.reset(); + + return state; +} + +} // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp index 2c7a3ee097f7..0ad8c92eef48 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp @@ -176,4 +176,17 @@ tracing::InstanceTracingProfile InstanceAgent::collectTracingProfile() { }; } +#pragma mark - Tracing + +InstanceTracingAgent::InstanceTracingAgent(tracing::TraceRecordingState& state) + : tracing::TargetTracingAgent(state) {} + +void InstanceTracingAgent::setTracedRuntime(RuntimeTarget* runtimeTarget) { + if (runtimeTarget != nullptr) { + runtimeTracingAgent_ = runtimeTarget->createTracingAgent(state_); + } else { + runtimeTracingAgent_ = nullptr; + } +} + } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h index 159bbbe1fa72..2d7727720893 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -87,4 +88,25 @@ class InstanceAgent final { SessionState& sessionState_; }; +#pragma mark - Tracing + +/** + * An Agent that handles Tracing events for a particular InstanceTarget. + * + * Lifetime of this agent is bound to the lifetime of the Tracing session - + * HostTargetTraceRecording and to the lifetime of the InstanceTarget. + */ +class InstanceTracingAgent : tracing::TargetTracingAgent { + public: + explicit InstanceTracingAgent(tracing::TraceRecordingState& state); + + /** + * Registers the RuntimeTarget with this tracing agent. + */ + void setTracedRuntime(RuntimeTarget* runtimeTarget); + + private: + std::shared_ptr runtimeTracingAgent_; +}; + } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.cpp b/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.cpp index 29b9563ef753..016e399a8b0f 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.cpp @@ -44,12 +44,25 @@ std::shared_ptr InstanceTarget::createAgent( return instanceAgent; } +std::shared_ptr InstanceTarget::createTracingAgent( + tracing::TraceRecordingState& state) { + auto agent = std::make_shared(state); + agent->setTracedRuntime(currentRuntime_.get()); + tracingAgent_ = agent; + return agent; +} + InstanceTarget::~InstanceTarget() { // Agents are owned by the session, not by InstanceTarget, but // they hold an InstanceTarget& that we must guarantee is valid. assert( agents_.empty() && "InstanceAgent objects must be destroyed before their InstanceTarget. Did you call HostTarget::unregisterInstance()?"); + + // Tracing Agents are owned by the HostTargetTraceRecording. + assert( + tracingAgent_.expired() && + "InstanceTracingAgent must be destroyed before their InstanceTarget. Did you call HostTarget::unregisterInstance()?"); } RuntimeTarget& InstanceTarget::registerRuntime( @@ -69,6 +82,13 @@ RuntimeTarget& InstanceTarget::registerRuntime( agents_.forEach([currentRuntime = &*currentRuntime_](InstanceAgent& agent) { agent.setCurrentRuntime(currentRuntime); }); + + if (auto tracingAgent = tracingAgent_.lock()) { + // Registers the Runtime for tracing, if a Trace is currently being + // recorded. + tracingAgent->setTracedRuntime(currentRuntime_.get()); + } + return *currentRuntime_; } @@ -78,6 +98,13 @@ void InstanceTarget::unregisterRuntime(RuntimeTarget& Runtime) { "Invalid unregistration"); agents_.forEach( [](InstanceAgent& agent) { agent.setCurrentRuntime(nullptr); }); + + if (auto tracingAgent = tracingAgent_.lock()) { + // Unregisters the Runtime for tracing, if a Trace is currently being + // recorded. + tracingAgent->setTracedRuntime(nullptr); + } + currentRuntime_.reset(); } diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.h b/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.h index 60385229a8f3..4941bab53acc 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.h @@ -15,12 +15,15 @@ #include #include +#include #include namespace facebook::react::jsinspector_modern { class InstanceAgent; +class InstanceTracingAgent; +class HostTargetTraceRecording; /** * Receives events from an InstanceTarget. This is a shared interface that @@ -68,6 +71,18 @@ class InstanceTarget : public EnableExecutorFromThis { const FrontendChannel& channel, SessionState& sessionState); + /** + * Creates a new InstanceTracingAgent. + * This Agent is not owned by the InstanceTarget. The Agent will be destroyed + * either before the InstanceTarget is destroyed, as part of the + * InstanceTarget unregistration in HostTarget, or at the end of the tracing + * session. + * + * \param state A reference to the state of the active trace recording. + */ + std::shared_ptr createTracingAgent( + tracing::TraceRecordingState& state); + /** * Registers a JS runtime with this InstanceTarget. \returns a reference to * the created RuntimeTarget, which is owned by the \c InstanceTarget. All the @@ -103,6 +118,13 @@ class InstanceTarget : public EnableExecutorFromThis { std::shared_ptr currentRuntime_{nullptr}; WeakList agents_; std::shared_ptr executionContextManager_; + + /** + * This TracingAgent is owned by the HostTracingAgent, both are bound to + * the lifetime of their corresponding targets and the lifetime of the tracing + * session - HostTargetTraceRecording. + */ + std::weak_ptr tracingAgent_; }; } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.cpp index 5ee5d324aeea..ea8af7993625 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.cpp @@ -128,4 +128,9 @@ tracing::RuntimeSamplingProfile RuntimeAgent::collectSamplingProfile() { return targetController_.collectSamplingProfile(); } +#pragma mark - Tracing + +RuntimeTracingAgent::RuntimeTracingAgent(tracing::TraceRecordingState& state) + : tracing::TargetTracingAgent(state) {} + } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.h index 08817c5abff5..39140bfcfb5b 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.h @@ -13,6 +13,8 @@ #include #include +#include +#include namespace facebook::react::jsinspector_modern { @@ -106,4 +108,17 @@ class RuntimeAgent final { const ExecutionContextDescription executionContextDescription_; }; +#pragma mark - Tracing + +/** + * An Agent that handles Tracing events for a particular RuntimeTarget. + * + * Lifetime of this agent is bound to the lifetime of the Tracing session - + * HostTargetTraceRecording and to the lifetime of the RuntimeTarget. + */ +class RuntimeTracingAgent : tracing::TargetTracingAgent { + public: + explicit RuntimeTracingAgent(tracing::TraceRecordingState& state); +}; + } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp index d574b204dc6f..a034aa16ca8e 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp @@ -77,12 +77,24 @@ std::shared_ptr RuntimeTarget::createAgent( return runtimeAgent; } +std::shared_ptr RuntimeTarget::createTracingAgent( + tracing::TraceRecordingState& state) { + auto agent = std::make_shared(state); + tracingAgent_ = agent; + return agent; +} + RuntimeTarget::~RuntimeTarget() { // Agents are owned by the session, not by RuntimeTarget, but // they hold a RuntimeTarget& that we must guarantee is valid. assert( agents_.empty() && "RuntimeAgent objects must be destroyed before their RuntimeTarget. Did you call InstanceTarget::unregisterRuntime()?"); + + // Tracing Agents are owned by the HostTargetTraceRecording. + assert( + tracingAgent_.expired() && + "RuntimeTracingAgent must be destroyed before their InstanceTarget. Did you call InstanceTarget::unregisterRuntime()?"); } void RuntimeTarget::installBindingHandler(const std::string& bindingName) { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.h b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.h index 3ad4fc396a94..1dd9b8ef3984 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.h @@ -17,6 +17,7 @@ #include #include +#include #include @@ -35,6 +36,7 @@ namespace facebook::react::jsinspector_modern { class RuntimeAgent; +class RuntimeTracingAgent; class RuntimeAgentDelegate; class RuntimeTarget; struct SessionState; @@ -202,6 +204,17 @@ class JSINSPECTOR_EXPORT RuntimeTarget const FrontendChannel& channel, SessionState& sessionState); + /** + * Creates a new RuntimeTracingAgent. + * This Agent is not owned by the RuntimeTarget. The Agent will be destroyed + * either before the RuntimeTarget is destroyed, as part of the RuntimeTarget + * unregistration in InstanceTarget, or at the end of the tracing session. + * + * \param state A reference to the state of the active trace recording. + */ + std::shared_ptr createTracingAgent( + tracing::TraceRecordingState& state); + /** * Start sampling profiler for a particular JavaScript runtime. */ @@ -246,6 +259,13 @@ class JSINSPECTOR_EXPORT RuntimeTarget WeakList agents_; RuntimeTargetController controller_{*this}; + /** + * This TracingAgent is owned by the InstanceTracingAgent, both are bound to + * the lifetime of their corresponding targets and the lifetime of the tracing + * session - HostTargetTraceRecording. + */ + std::weak_ptr tracingAgent_; + /** * Adds a function with the given name on the runtime's global object, that * when called will send a Runtime.bindingCalled event through all connected diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TargetTracingAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TargetTracingAgent.h new file mode 100644 index 000000000000..dce095dd04d3 --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TargetTracingAgent.h @@ -0,0 +1,33 @@ +/* + * 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 "TraceRecordingState.h" + +namespace facebook::react::jsinspector_modern::tracing { + +/** + * An interface for a tracing agent of a target. + * Tracing Agents are only allocated during an active tracing session. + * + * Construction of a TracingAgent means that either the recording has just + * started or the target was just created during an active recording. + * Destruction of a TracingAgent means that either the recording has stopped or + * the target is about to be destroyed. + */ +class TargetTracingAgent { + public: + explicit TargetTracingAgent(TraceRecordingState& state) : state_(state) { + (void)state_; + } + + protected: + TraceRecordingState& state_; +}; + +} // namespace facebook::react::jsinspector_modern::tracing diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h new file mode 100644 index 000000000000..ccc37344de05 --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h @@ -0,0 +1,17 @@ +/* + * 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 + +namespace facebook::react::jsinspector_modern::tracing { + +/** + * Encapsulates the state of the Trace. + */ +struct TraceRecordingState {}; + +} // namespace facebook::react::jsinspector_modern::tracing From ed7315928cf3b78933405d4b4817a6771de98366 Mon Sep 17 00:00:00 2001 From: hoxy Date: Tue, 5 Aug 2025 13:04:39 -0700 Subject: [PATCH 02/11] Record RuntimeSamplingProfile on TraceRecordingState Summary: # Changelog: [Internal] Now that we've defined all actors and relationship between Agents, we can start recording profiles and store them on a recording state. Differential Revision: D79415396 --- .../jsinspector-modern/RuntimeAgent.cpp | 15 +++++++++++++-- .../ReactCommon/jsinspector-modern/RuntimeAgent.h | 9 ++++++++- .../jsinspector-modern/RuntimeTarget.cpp | 2 +- .../tracing/TraceRecordingState.h | 12 ++++++++---- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.cpp index ea8af7993625..12ec37a4175c 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.cpp @@ -130,7 +130,18 @@ tracing::RuntimeSamplingProfile RuntimeAgent::collectSamplingProfile() { #pragma mark - Tracing -RuntimeTracingAgent::RuntimeTracingAgent(tracing::TraceRecordingState& state) - : tracing::TargetTracingAgent(state) {} +RuntimeTracingAgent::RuntimeTracingAgent( + tracing::TraceRecordingState& state, + RuntimeTargetController& targetController) + : tracing::TargetTracingAgent(state), targetController_(targetController) { + targetController_.enableSamplingProfiler(); +} + +RuntimeTracingAgent::~RuntimeTracingAgent() { + targetController_.disableSamplingProfiler(); + auto profile = targetController_.collectSamplingProfile(); + + state_.runtimeSamplingProfiles.emplace_back(std::move(profile)); +} } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.h index 39140bfcfb5b..186eb0ba4e8d 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.h @@ -118,7 +118,14 @@ class RuntimeAgent final { */ class RuntimeTracingAgent : tracing::TargetTracingAgent { public: - explicit RuntimeTracingAgent(tracing::TraceRecordingState& state); + explicit RuntimeTracingAgent( + tracing::TraceRecordingState& state, + RuntimeTargetController& targetController); + + ~RuntimeTracingAgent(); + + private: + RuntimeTargetController& targetController_; }; } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp index a034aa16ca8e..50562676704a 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp @@ -79,7 +79,7 @@ std::shared_ptr RuntimeTarget::createAgent( std::shared_ptr RuntimeTarget::createTracingAgent( tracing::TraceRecordingState& state) { - auto agent = std::make_shared(state); + auto agent = std::make_shared(state, controller_); tracingAgent_ = agent; return agent; } diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h index ccc37344de05..5cd112177401 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h @@ -7,11 +7,15 @@ #pragma once +#include "RuntimeSamplingProfile.h" + +#include + namespace facebook::react::jsinspector_modern::tracing { -/** - * Encapsulates the state of the Trace. - */ -struct TraceRecordingState {}; +struct TraceRecordingState { + // All captured Runtime Sampling Profiles during this Trace Recording. + std::vector runtimeSamplingProfiles{}; +}; } // namespace facebook::react::jsinspector_modern::tracing From 5d995a392f2ab0db7d6b3107baa907c62316c254 Mon Sep 17 00:00:00 2001 From: hoxy Date: Tue, 5 Aug 2025 13:04:39 -0700 Subject: [PATCH 03/11] Record InstanceTracing profile on TraceRecordingState Summary: # Changelog: [Internal] We are deprecating previous `InstanceTarcingProfile` struct, but not removing yet, since this is still used and we will migrate in one of the diffs at the top of the stack. The `InstanceTracingProfile` will consist only of Trace Events that were captured by PerformanceTracer, and will not store Runtime Sampling Profiles. There are multiple reasons for this: 1. As of right now, Runtime Sampling Profiles are completely independant from Instance Profiles and do not require anythings from Instance as a Target to be represented on a timeline. 2. Although PerformanceTracer is a singleton, it should be this way. It captures events for something that can only be dispatched if there is an allocated React Instance, so in the future PerformanceTracer could become a data-member of InstanceTarget. Differential Revision: D79433498 --- .../jsinspector-modern/InstanceAgent.cpp | 19 ++++++++++++++++--- .../jsinspector-modern/InstanceAgent.h | 4 +++- .../tracing/InstanceTracingProfile.h | 7 ++++++- .../tracing/TraceRecordingState.h | 4 ++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp index 0ad8c92eef48..1e0ffe558adb 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp @@ -9,6 +9,7 @@ #include "RuntimeTarget.h" #include +#include #include @@ -167,11 +168,11 @@ void InstanceAgent::stopTracing() { } } -tracing::InstanceTracingProfile InstanceAgent::collectTracingProfile() { +tracing::InstanceTracingProfileLegacy InstanceAgent::collectTracingProfile() { tracing::RuntimeSamplingProfile runtimeSamplingProfile = runtimeAgent_->collectSamplingProfile(); - return tracing::InstanceTracingProfile{ + return tracing::InstanceTracingProfileLegacy{ .runtimeSamplingProfile = std::move(runtimeSamplingProfile), }; } @@ -179,7 +180,19 @@ tracing::InstanceTracingProfile InstanceAgent::collectTracingProfile() { #pragma mark - Tracing InstanceTracingAgent::InstanceTracingAgent(tracing::TraceRecordingState& state) - : tracing::TargetTracingAgent(state) {} + : tracing::TargetTracingAgent(state) { + tracing::PerformanceTracer::getInstance().startTracing(); +} + +InstanceTracingAgent::~InstanceTracingAgent() { + auto& performanceTracer = tracing::PerformanceTracer::getInstance(); + auto performanceTraceEvents = performanceTracer.stopTracing(); + if (performanceTraceEvents) { + state_.instanceTracingProfiles.emplace_back(tracing::InstanceTracingProfile{ + .performanceTraceEvents = std::move(*performanceTraceEvents), + }); + } +} void InstanceTracingAgent::setTracedRuntime(RuntimeTarget* runtimeTarget) { if (runtimeTarget != nullptr) { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h index 2d7727720893..bf886edcb739 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h @@ -75,7 +75,7 @@ class InstanceAgent final { /** * Return recorded profile for the previous tracing session. */ - tracing::InstanceTracingProfile collectTracingProfile(); + tracing::InstanceTracingProfileLegacy collectTracingProfile(); private: void maybeSendExecutionContextCreatedNotification(); @@ -100,6 +100,8 @@ class InstanceTracingAgent : tracing::TargetTracingAgent { public: explicit InstanceTracingAgent(tracing::TraceRecordingState& state); + ~InstanceTracingAgent(); + /** * Registers the RuntimeTarget with this tracing agent. */ diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/InstanceTracingProfile.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/InstanceTracingProfile.h index 47cd70437eb4..4831f14598e7 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/InstanceTracingProfile.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/InstanceTracingProfile.h @@ -8,12 +8,17 @@ #pragma once #include "RuntimeSamplingProfile.h" +#include "TraceEvent.h" namespace facebook::react::jsinspector_modern::tracing { -struct InstanceTracingProfile { +struct InstanceTracingProfileLegacy { public: RuntimeSamplingProfile runtimeSamplingProfile; }; +struct InstanceTracingProfile { + std::vector performanceTraceEvents; +}; + } // namespace facebook::react::jsinspector_modern::tracing diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h index 5cd112177401..a530b712f398 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h @@ -7,6 +7,7 @@ #pragma once +#include "InstanceTracingProfile.h" #include "RuntimeSamplingProfile.h" #include @@ -16,6 +17,9 @@ namespace facebook::react::jsinspector_modern::tracing { struct TraceRecordingState { // All captured Runtime Sampling Profiles during this Trace Recording. std::vector runtimeSamplingProfiles{}; + + // All captures Instance Tracing Profiles during this Trace Recording. + std::vector instanceTracingProfiles{}; }; } // namespace facebook::react::jsinspector_modern::tracing From 12f1a541f80cdd79b8cdf15faca7efbbfb03f455 Mon Sep 17 00:00:00 2001 From: hoxy Date: Tue, 5 Aug 2025 13:04:39 -0700 Subject: [PATCH 04/11] Record processId on TraceRecordingState Summary: # Changelog: [Internal] We would need this to correctly serialize Runtime Sampling Profiles. Differential Revision: D79433501 --- .../jsinspector-modern/tracing/TraceRecordingState.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h index a530b712f398..788b479d0ab1 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h @@ -10,11 +10,17 @@ #include "InstanceTracingProfile.h" #include "RuntimeSamplingProfile.h" +#include + #include namespace facebook::react::jsinspector_modern::tracing { struct TraceRecordingState { + // The ID of the OS-level process that this Trace Recording is associated + // with. + ProcessId processId = oscompat::getCurrentProcessId(); + // All captured Runtime Sampling Profiles during this Trace Recording. std::vector runtimeSamplingProfiles{}; From aec8d060a1f5cc1ad1c720af3c091bff03bef385 Mon Sep 17 00:00:00 2001 From: hoxy Date: Tue, 5 Aug 2025 13:04:39 -0700 Subject: [PATCH 05/11] Record startTime on TraceRecordingState Summary: # Changelog: [Internal] We would need this to correctly serialize Runtime Sampling Profiles. In case of recording the trace in the background, this might not be the value we would want to use, we would probably add another field or calculate it dynamically, depending on the tracing mode. This is not the case for now, will be solved separately on top of the stack. Differential Revision: D79433502 --- .../jsinspector-modern/HostTargetTraceRecording.cpp | 2 +- .../jsinspector-modern/tracing/TraceRecordingState.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp index 406fab16eef2..de5fa18b6f27 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp @@ -27,7 +27,7 @@ void HostTargetTraceRecording::start() { hostTracingAgent_ == nullptr && "Tracing Agent for the HostTarget was already initialized."); - state_ = tracing::TraceRecordingState{}; + state_ = tracing::TraceRecordingState{.startTime = HighResTimeStamp::now()}; hostTracingAgent_ = hostTarget_.createTracingAgent(*state_); } diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h index 788b479d0ab1..1c4643e9feab 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h @@ -11,6 +11,7 @@ #include "RuntimeSamplingProfile.h" #include +#include #include @@ -21,6 +22,9 @@ struct TraceRecordingState { // with. ProcessId processId = oscompat::getCurrentProcessId(); + // The timestamp at which this Trace Recording started. + HighResTimeStamp startTime; + // All captured Runtime Sampling Profiles during this Trace Recording. std::vector runtimeSamplingProfiles{}; From af4cdf757b8ba469510c4d134515a5e6e1fa0660 Mon Sep 17 00:00:00 2001 From: hoxy Date: Tue, 5 Aug 2025 14:28:42 -0700 Subject: [PATCH 06/11] Implement serializer for TraceRecordingState Summary: # Changelog: [Internal] Now TraceRecordingState should capture everything we need in order to display a trace on a timeline. We just need to serialize it properly into collection of serialized Trace Events that would be sent via `Tracing.dataCollected` CDP events. This is what this serializer is doing. Differential Revision: D79434655 --- .../tracing/TraceRecordingStateSerializer.cpp | 68 +++++++++++++++++++ .../tracing/TraceRecordingStateSerializer.h | 42 ++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingStateSerializer.cpp create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingStateSerializer.h diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingStateSerializer.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingStateSerializer.cpp new file mode 100644 index 000000000000..09fe7368404c --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingStateSerializer.cpp @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#include "TraceRecordingStateSerializer.h" +#include "RuntimeSamplingProfileTraceEventSerializer.h" +#include "TraceEventSerializer.h" + +namespace facebook::react::jsinspector_modern::tracing { + +namespace { + +folly::dynamic generateNewChunk(uint16_t chunkSize) { + folly::dynamic chunk = folly::dynamic::array(); + chunk.reserve(chunkSize); + + return chunk; +} + +} // namespace + +/* static */ void TraceRecordingStateSerializer::emitAsDataCollectedChunks( + TraceRecordingState&& recording, + const std::function& chunkCallback, + uint16_t performanceTraceEventsChunkSize, + uint16_t profileTraceEventsChunkSize) { + auto instancesProfiles = std::move(recording.instanceTracingProfiles); + IdGenerator profileIdGenerator; + + for (auto& instanceProfile : instancesProfiles) { + emitPerformanceTraceEvents( + std::move(instanceProfile.performanceTraceEvents), + chunkCallback, + performanceTraceEventsChunkSize); + } + + RuntimeSamplingProfileTraceEventSerializer::serializeAndDispatch( + std::move(recording.runtimeSamplingProfiles), + profileIdGenerator, + recording.startTime, + chunkCallback, + profileTraceEventsChunkSize); +} + +/* static */ void TraceRecordingStateSerializer::emitPerformanceTraceEvents( + std::vector&& events, + const std::function& chunkCallback, + uint16_t chunkSize) { + folly::dynamic chunk = generateNewChunk(chunkSize); + + for (auto& event : events) { + if (chunk.size() == chunkSize) { + chunkCallback(std::move(chunk)); + chunk = generateNewChunk(chunkSize); + } + + chunk.push_back(TraceEventSerializer::serialize(std::move(event))); + } + + if (!chunk.empty()) { + chunkCallback(std::move(chunk)); + } +} + +} // namespace facebook::react::jsinspector_modern::tracing diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingStateSerializer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingStateSerializer.h new file mode 100644 index 000000000000..469519e6afc4 --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingStateSerializer.h @@ -0,0 +1,42 @@ +/* + * 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 "TraceEvent.h" +#include "TraceRecordingState.h" + +#include +#include + +namespace facebook::react::jsinspector_modern::tracing { + +/** + * A serializer for TraceRecordingState that can be used for tranforming the + * recording into sequence of serialized Trace Events. + */ +class TraceRecordingStateSerializer { + public: + /** + * Transforms the recording into a sequence of serialized Trace Events, which + * is split in chunks of sizes \p performanceTraceEventsChunkSize or + * \p profileTraceEventsChunkSize, depending on type, and sent with \p + * chunkCallback. + */ + static void emitAsDataCollectedChunks( + TraceRecordingState&& recording, + const std::function& chunkCallback, + uint16_t performanceTraceEventsChunkSize, + uint16_t profileTraceEventsChunkSize); + + static void emitPerformanceTraceEvents( + std::vector&& events, + const std::function& chunkCallback, + uint16_t chunkSize); +}; + +} // namespace facebook::react::jsinspector_modern::tracing From b332d45fcf2b31746f4f385902db2f64f46b9b52 Mon Sep 17 00:00:00 2001 From: hoxy Date: Tue, 5 Aug 2025 14:28:42 -0700 Subject: [PATCH 07/11] Expose start and stop tracing endpoints for HostTargetController Summary: # Changelog: [Internal] CDP Agents should interact with Targets by using Controllers: https://www.internalfb.com/code/fbsource/[0b20b752c43f]/xplat/js/react-native-github/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h?lines=300-303 This diff adds public endpoinst for controlling the trace recording on a `HostTargetController` that will be used by `TracingAgent` later. Differential Revision: D79433500 --- .../ReactCommon/jsinspector-modern/HostTarget.h | 12 ++++++++++++ .../jsinspector-modern/HostTargetTracing.cpp | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h index 8bc2494101bb..08e5dc3c1247 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h @@ -168,6 +168,18 @@ class HostTargetController final { */ bool decrementPauseOverlayCounter(); + /** + * Starts trace recording for this HostTarget. + * + * \return false if already tracing, true otherwise. + */ + bool startTracing(); + + /** + * Stops previously started trace recording. + */ + tracing::TraceRecordingState stopTracing(); + private: HostTarget& target_; size_t pauseOverlayCounter_{0}; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp index 45d08dc95713..f321e0151f7d 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp @@ -10,6 +10,14 @@ namespace facebook::react::jsinspector_modern { +bool HostTargetController::startTracing() { + return target_.startTracing(); +} + +tracing::TraceRecordingState HostTargetController::stopTracing() { + return target_.stopTracing(); +} + std::shared_ptr HostTarget::createTracingAgent( tracing::TraceRecordingState& state) { auto agent = std::make_shared(state); From 131b14a65053dec0b4d6555da3b376532523cd7b Mon Sep 17 00:00:00 2001 From: hoxy Date: Tue, 5 Aug 2025 14:29:14 -0700 Subject: [PATCH 08/11] Use new endpoints in CDP TracingAgent Summary: # Changelog: [Internal] Now that we've implemented all required serializers and TraceRecordedState has everything needed, we can migrate TracingAgent to use this new infra. Differential Revision: D79433497 --- .../jsinspector-modern/HostAgent.cpp | 3 +- .../jsinspector-modern/TracingAgent.cpp | 90 ++++--------------- .../jsinspector-modern/TracingAgent.h | 6 +- 3 files changed, 23 insertions(+), 76 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp index 566d1a7fda0f..1c29f8e7e82d 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp @@ -47,7 +47,8 @@ class HostAgent::Impl final { hostMetadata_(std::move(hostMetadata)), sessionState_(sessionState), networkIOAgent_(NetworkIOAgent(frontendChannel, std::move(executor))), - tracingAgent_(TracingAgent(frontendChannel, sessionState)) {} + tracingAgent_( + TracingAgent(frontendChannel, sessionState, targetController)) {} ~Impl() { if (isPausedInDebuggerOverlayVisible_) { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp index bf9d31d24642..5864a5f0bc28 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace facebook::react::jsinspector_modern { @@ -24,39 +25,22 @@ const uint16_t TRACE_EVENT_CHUNK_SIZE = 1000; /** * The maximum number of ProfileChunk trace events * that will be sent in a single CDP Tracing.dataCollected message. - * TODO(T219394401): Increase the size once we manage the queue on OkHTTP side + * TODO(T219394401): Increase the size once we manage the queue on OkHTTP + side * properly and avoid WebSocket disconnections when sending a message larger * than 16MB. */ 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( FrontendChannel frontendChannel, - const SessionState& sessionState) + const SessionState& sessionState, + HostTargetController& hostTargetController) : frontendChannel_(std::move(frontendChannel)), - sessionState_(sessionState) {} + sessionState_(sessionState), + hostTargetController_(hostTargetController) {} bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { if (req.method == "Tracing.start") { @@ -69,56 +53,23 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { return true; } - if (!instanceAgent_) { - frontendChannel_(cdp::jsonError( - req.id, - cdp::ErrorCode::InternalError, - "Couldn't find instance available for Tracing")); - - return true; - } - - bool correctlyStartedPerformanceTracer = - tracing::PerformanceTracer::getInstance().startTracing(); - if (!correctlyStartedPerformanceTracer) { + bool didNotHaveAlreadyRunningRecording = + hostTargetController_.startTracing(); + if (!didNotHaveAlreadyRunningRecording) { frontendChannel_(cdp::jsonError( req.id, - cdp::ErrorCode::InternalError, - "Tracing session already started")); + cdp::ErrorCode::InvalidRequest, + "Tracing has already been started")); return true; } - - instanceAgent_->startTracing(); - instanceTracingStartTimestamp_ = HighResTimeStamp::now(); frontendChannel_(cdp::jsonResult(req.id)); return true; } else if (req.method == "Tracing.end") { // @cdp Tracing.end support is experimental. - if (!instanceAgent_) { - frontendChannel_(cdp::jsonError( - req.id, - cdp::ErrorCode::InternalError, - "Couldn't find instance available for Tracing")); - - return true; - } - - instanceAgent_->stopTracing(); - - tracing::PerformanceTracer& performanceTracer = - tracing::PerformanceTracer::getInstance(); - auto collectedEvents = performanceTracer.stopTracing(); - if (!collectedEvents) { - frontendChannel_(cdp::jsonError( - req.id, - cdp::ErrorCode::InternalError, - "Tracing session not started")); - - return true; - } + auto state = hostTargetController_.stopTracing(); // Send response to Tracing.end request. frontendChannel_(cdp::jsonResult(req.id)); @@ -128,19 +79,10 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { "Tracing.dataCollected", folly::dynamic::object("value", std::move(eventsChunk)))); }; - - serializeTraceEventsInChunks( - std::move(*collectedEvents), - TRACE_EVENT_CHUNK_SIZE, - dataCollectedCallback); - - auto tracingProfile = instanceAgent_->collectTracingProfile(); - tracing::IdGenerator profileIdGenerator; - tracing::RuntimeSamplingProfileTraceEventSerializer::serializeAndDispatch( - std::move(tracingProfile.runtimeSamplingProfile), - profileIdGenerator, - instanceTracingStartTimestamp_, + tracing::TraceRecordingStateSerializer::emitAsDataCollectedChunks( + std::move(state), dataCollectedCallback, + TRACE_EVENT_CHUNK_SIZE, PROFILE_TRACE_EVENT_CHUNK_SIZE); frontendChannel_(cdp::jsonNotification( diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h index f58e01f26831..c96ca1d9d641 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h @@ -7,6 +7,7 @@ #pragma once +#include "HostTarget.h" #include "InspectorInterfaces.h" #include "InstanceAgent.h" @@ -27,7 +28,8 @@ class TracingAgent { */ TracingAgent( FrontendChannel frontendChannel, - const SessionState& sessionState); + const SessionState& sessionState, + HostTargetController& hostTargetController); /** * Handle a CDP request. The response will be sent over the provided @@ -63,6 +65,8 @@ class TracingAgent { HighResTimeStamp instanceTracingStartTimestamp_; const SessionState& sessionState_; + + HostTargetController& hostTargetController_; }; } // namespace facebook::react::jsinspector_modern From a876fc162bb21f9a7eb71688bfaa9597049526fd Mon Sep 17 00:00:00 2001 From: hoxy Date: Tue, 5 Aug 2025 14:29:14 -0700 Subject: [PATCH 09/11] Cleanup legacy tracing endpoints from Agents Summary: # Changelog: [Internal] Differential Revision: D79434656 --- .../jsinspector-modern/HostAgent.cpp | 2 -- .../jsinspector-modern/InstanceAgent.cpp | 21 ------------------- .../jsinspector-modern/InstanceAgent.h | 17 --------------- .../jsinspector-modern/TracingAgent.cpp | 5 ----- .../jsinspector-modern/TracingAgent.h | 21 ------------------- .../tracing/InstanceTracingProfile.h | 6 ------ 6 files changed, 72 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp index 1c29f8e7e82d..21c4fb8dbd77 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp @@ -311,8 +311,6 @@ class HostAgent::Impl final { } void setCurrentInstanceAgent(std::shared_ptr instanceAgent) { - tracingAgent_.setCurrentInstanceAgent(instanceAgent); - auto previousInstanceAgent = std::move(instanceAgent_); instanceAgent_ = std::move(instanceAgent); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp index 1e0ffe558adb..870ff718230e 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp @@ -156,27 +156,6 @@ void InstanceAgent::maybeSendPendingConsoleMessages() { } } -void InstanceAgent::startTracing() { - if (runtimeAgent_) { - runtimeAgent_->enableSamplingProfiler(); - } -} - -void InstanceAgent::stopTracing() { - if (runtimeAgent_) { - runtimeAgent_->disableSamplingProfiler(); - } -} - -tracing::InstanceTracingProfileLegacy InstanceAgent::collectTracingProfile() { - tracing::RuntimeSamplingProfile runtimeSamplingProfile = - runtimeAgent_->collectSamplingProfile(); - - return tracing::InstanceTracingProfileLegacy{ - .runtimeSamplingProfile = std::move(runtimeSamplingProfile), - }; -} - #pragma mark - Tracing InstanceTracingAgent::InstanceTracingAgent(tracing::TraceRecordingState& state) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h index bf886edcb739..d465601254db 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h @@ -60,23 +60,6 @@ class InstanceAgent final { */ void sendConsoleMessage(SimpleConsoleMessage message); - /** - * Notify Instance about started Tracing session. Should be initiated by - * TracingAgent on Tracing.start CDP method. - */ - void startTracing(); - - /** - * Notify Instance about stopped Tracing session. Should be initiated by - * TracingAgent on Tracing.end CDP method. - */ - void stopTracing(); - - /** - * Return recorded profile for the previous tracing session. - */ - tracing::InstanceTracingProfileLegacy collectTracingProfile(); - private: void maybeSendExecutionContextCreatedNotification(); void sendConsoleMessageImmediately(SimpleConsoleMessage message); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp index 5864a5f0bc28..883bfaf9f327 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp @@ -95,9 +95,4 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { return false; } -void TracingAgent::setCurrentInstanceAgent( - std::shared_ptr instanceAgent) { - instanceAgent_ = std::move(instanceAgent); -} - } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h index c96ca1d9d641..6d029c149cff 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h @@ -9,7 +9,6 @@ #include "HostTarget.h" #include "InspectorInterfaces.h" -#include "InstanceAgent.h" #include #include @@ -38,32 +37,12 @@ class TracingAgent { */ bool handleRequest(const cdp::PreparsedRequest& req); - /** - * Replace the current InstanceAgent with the given one. - * \param agent The new InstanceAgent. May be null to signify that there is - * currently no active instance. - */ - void setCurrentInstanceAgent(std::shared_ptr agent); - private: /** * A channel used to send responses and events to the frontend. */ FrontendChannel frontendChannel_; - /** - * Current InstanceAgent. May be null to signify that there is - * currently no active instance. - */ - std::shared_ptr instanceAgent_; - - /** - * Timestamp of when we started tracing of an Instance, will be used as a - * a start of JavaScript samples recording and as a time origin for the events - * in this trace. - */ - HighResTimeStamp instanceTracingStartTimestamp_; - const SessionState& sessionState_; HostTargetController& hostTargetController_; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/InstanceTracingProfile.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/InstanceTracingProfile.h index 4831f14598e7..59493bca2ac0 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/InstanceTracingProfile.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/InstanceTracingProfile.h @@ -7,16 +7,10 @@ #pragma once -#include "RuntimeSamplingProfile.h" #include "TraceEvent.h" namespace facebook::react::jsinspector_modern::tracing { -struct InstanceTracingProfileLegacy { - public: - RuntimeSamplingProfile runtimeSamplingProfile; -}; - struct InstanceTracingProfile { std::vector performanceTraceEvents; }; From 13f24aaf94f06a8528ba5d4cfc9b13ac6796d4b7 Mon Sep 17 00:00:00 2001 From: hoxy Date: Tue, 5 Aug 2025 14:29:14 -0700 Subject: [PATCH 10/11] Stop potentially running trace recording if CDP session is destroyed Summary: # Changelog: [Internal] We never call `stopTracing()` on a HostTarget, if the Agent was destroyed. This could only happen if the session was destroyed. There could me multiple sessions, so we have to keep the source of truth for recording status in a session state. Differential Revision: D79435494 --- .../ReactCommon/jsinspector-modern/SessionState.h | 6 ++++++ .../ReactCommon/jsinspector-modern/TracingAgent.cpp | 13 ++++++++++++- .../ReactCommon/jsinspector-modern/TracingAgent.h | 6 ++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/SessionState.h b/packages/react-native/ReactCommon/jsinspector-modern/SessionState.h index 28074ba4bd34..9ff27db0a798 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/SessionState.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/SessionState.h @@ -25,6 +25,12 @@ struct SessionState { bool isReactNativeApplicationDomainEnabled{false}; bool isRuntimeDomainEnabled{false}; + /** + * Whether the Trace Recording was initialized via CDP Tracing.start method + * and not finished yet with Tracing.stop. + */ + bool hasPendingTraceRecording{false}; + /** * A map from binding names (registered during this session using @cdp * Runtime.addBinding) to execution context selectors. diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp index 883bfaf9f327..481439e6f070 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp @@ -36,12 +36,20 @@ const uint16_t PROFILE_TRACE_EVENT_CHUNK_SIZE = 1; TracingAgent::TracingAgent( FrontendChannel frontendChannel, - const SessionState& sessionState, + SessionState& sessionState, HostTargetController& hostTargetController) : frontendChannel_(std::move(frontendChannel)), sessionState_(sessionState), hostTargetController_(hostTargetController) {} +TracingAgent::~TracingAgent() { + // Agents are owned by the session. If the agent is destroyed, it means that + // the session was destroyed. We should stop pending recording. + if (sessionState_.hasPendingTraceRecording) { + hostTargetController_.stopTracing(); + } +} + bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { if (req.method == "Tracing.start") { // @cdp Tracing.start support is experimental. @@ -64,6 +72,8 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { return true; } + + sessionState_.hasPendingTraceRecording = true; frontendChannel_(cdp::jsonResult(req.id)); return true; @@ -71,6 +81,7 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { // @cdp Tracing.end support is experimental. auto state = hostTargetController_.stopTracing(); + sessionState_.hasPendingTraceRecording = false; // Send response to Tracing.end request. frontendChannel_(cdp::jsonResult(req.id)); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h index 6d029c149cff..b46d9a78130d 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h @@ -27,9 +27,11 @@ class TracingAgent { */ TracingAgent( FrontendChannel frontendChannel, - const SessionState& sessionState, + SessionState& sessionState, HostTargetController& hostTargetController); + ~TracingAgent(); + /** * Handle a CDP request. The response will be sent over the provided * \c FrontendChannel synchronously or asynchronously. @@ -43,7 +45,7 @@ class TracingAgent { */ FrontendChannel frontendChannel_; - const SessionState& sessionState_; + SessionState& sessionState_; HostTargetController& hostTargetController_; }; From aa9ba0f83dbbe6cd5dfca9207eb9c04c56859103 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Wed, 6 Aug 2025 04:39:21 -0700 Subject: [PATCH 11/11] Create TracingMode (#53035) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53035 # Changelog: [Internal] This adds an ability to distinguish different trace recordings, based on mode Reviewed By: sbuggay Differential Revision: D79557790 --- .../ReactCommon/jsinspector-modern/HostTarget.h | 8 ++++++-- .../HostTargetTraceRecording.cpp | 6 ++++-- .../HostTargetTraceRecording.h | 17 ++++++++++++++++- .../jsinspector-modern/HostTargetTracing.cpp | 9 +++++---- .../jsinspector-modern/TracingAgent.cpp | 3 ++- .../jsinspector-modern/tracing/TracingMode.h | 17 +++++++++++++++++ 6 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/tracing/TracingMode.h diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h index 08e5dc3c1247..7c7e3c64fd2a 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h @@ -18,6 +18,8 @@ #include #include +#include + #ifndef JSINSPECTOR_EXPORT #ifdef _MSC_VER #ifdef CREATE_SHARED_LIBRARY @@ -171,9 +173,10 @@ class HostTargetController final { /** * Starts trace recording for this HostTarget. * + * \param mode In which mode to start the trace recording. * \return false if already tracing, true otherwise. */ - bool startTracing(); + bool startTracing(tracing::Mode mode); /** * Stops previously started trace recording. @@ -264,9 +267,10 @@ class JSINSPECTOR_EXPORT HostTarget /** * Starts trace recording for this HostTarget. * + * \param mode In which mode to start the trace recording. * \return false if already tracing, true otherwise. */ - bool startTracing(); + bool startTracing(tracing::Mode mode); /** * Stops previously started trace recording. diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp index de5fa18b6f27..d055e5328815 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp @@ -10,8 +10,10 @@ namespace facebook::react::jsinspector_modern { -HostTargetTraceRecording::HostTargetTraceRecording(HostTarget& hostTarget) - : hostTarget_(hostTarget) {} +HostTargetTraceRecording::HostTargetTraceRecording( + tracing::Mode tracingMode, + HostTarget& hostTarget) + : tracingMode_(tracingMode), hostTarget_(hostTarget) {} void HostTargetTraceRecording::setTracedInstance( InstanceTarget* instanceTarget) { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.h b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.h index 963c931dad15..b6157d510d53 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTraceRecording.h @@ -28,7 +28,17 @@ namespace facebook::react::jsinspector_modern { */ class HostTargetTraceRecording { public: - explicit HostTargetTraceRecording(HostTarget& hostTarget); + explicit HostTargetTraceRecording( + tracing::Mode tracingMode, + HostTarget& hostTarget); + + inline bool isBackgroundInitiated() const { + return tracingMode_ == tracing::Mode::Background; + } + + inline bool isUserInitiated() const { + return tracingMode_ == tracing::Mode::CDP; + } /** * Updates the current traced Instance for this recording. @@ -50,6 +60,11 @@ class HostTargetTraceRecording { tracing::TraceRecordingState stop(); private: + /** + * The mode in which this trace recording was initialized. + */ + tracing::Mode tracingMode_; + /** * The Host for which this Trace Recording is going to happen. */ diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp index f321e0151f7d..cdf270d1fbdb 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp @@ -10,8 +10,8 @@ namespace facebook::react::jsinspector_modern { -bool HostTargetController::startTracing() { - return target_.startTracing(); +bool HostTargetController::startTracing(tracing::Mode tracingMode) { + return target_.startTracing(tracingMode); } tracing::TraceRecordingState HostTargetController::stopTracing() { @@ -25,12 +25,13 @@ std::shared_ptr HostTarget::createTracingAgent( return agent; } -bool HostTarget::startTracing() { +bool HostTarget::startTracing(tracing::Mode tracingMode) { if (traceRecording_ != nullptr) { return false; } - traceRecording_ = std::make_unique(*this); + traceRecording_ = + std::make_unique(tracingMode, *this); traceRecording_->setTracedInstance(currentInstance_.get()); traceRecording_->start(); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp index 481439e6f070..49568d70799a 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace facebook::react::jsinspector_modern { @@ -63,7 +64,7 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { } bool didNotHaveAlreadyRunningRecording = - hostTargetController_.startTracing(); + hostTargetController_.startTracing(tracing::Mode::CDP); if (!didNotHaveAlreadyRunningRecording) { frontendChannel_(cdp::jsonError( req.id, diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TracingMode.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TracingMode.h new file mode 100644 index 000000000000..a909bf11196d --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TracingMode.h @@ -0,0 +1,17 @@ +/* + * 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 + +namespace facebook::react::jsinspector_modern::tracing { + +enum class Mode { + CDP, // Initiated by the user via Chrome DevTools Frontend. + Background, // Initiated by the host, doesn't require active CDP session. +}; + +}