From 5112fed0795486d92cda9951ab9a5218b95db4af Mon Sep 17 00:00:00 2001 From: Devan Buggay Date: Tue, 9 Sep 2025 10:47:30 -0700 Subject: [PATCH] Propagate RuntimeTargetConsole events to HostTarget Summary: Pipe through console events to HostTarget, so they can be further pushed to the new perf monitor. Console callback currently noops. Event filtering and further propagation to perf monitor to be handled in a future diff. Changelog: [Internal] Differential Revision: D82005323 --- .../jsinspector-modern/HostTarget.cpp | 5 ++- .../jsinspector-modern/InstanceTarget.cpp | 16 +++++--- .../jsinspector-modern/InstanceTarget.h | 15 ++++++- .../jsinspector-modern/RuntimeTarget.cpp | 35 +++++++++++++--- .../jsinspector-modern/RuntimeTarget.h | 40 +++++++++++++++++-- .../RuntimeTargetConsole.cpp | 31 +++++++++----- 6 files changed, 114 insertions(+), 28 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp index 12af8080e91a..995238fde041 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp @@ -239,7 +239,10 @@ HostTargetDelegate::~HostTargetDelegate() = default; InstanceTarget& HostTarget::registerInstance(InstanceTargetDelegate& delegate) { assert(!currentInstance_ && "Only one instance allowed"); currentInstance_ = InstanceTarget::create( - executionContextManager_, delegate, makeVoidExecutor(executorFromThis())); + executionContextManager_, + delegate, + makeVoidExecutor(executorFromThis()), + delegate_); // Pass HostTargetDelegate reference through sessions_.forEach( [currentInstance = &*currentInstance_](HostTargetSession& session) { session.setCurrentInstance(currentInstance); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.cpp b/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.cpp index 016e399a8b0f..12020a90a8cc 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.cpp @@ -17,18 +17,21 @@ namespace facebook::react::jsinspector_modern { std::shared_ptr InstanceTarget::create( std::shared_ptr executionContextManager, InstanceTargetDelegate& delegate, - VoidExecutor executor) { - std::shared_ptr instanceTarget{ - new InstanceTarget(executionContextManager, delegate)}; + VoidExecutor executor, + HostTargetDelegate& hostTargetDelegate) { + std::shared_ptr instanceTarget{new InstanceTarget( + executionContextManager, delegate, hostTargetDelegate)}; instanceTarget->setExecutor(std::move(executor)); return instanceTarget; } InstanceTarget::InstanceTarget( std::shared_ptr executionContextManager, - InstanceTargetDelegate& delegate) + InstanceTargetDelegate& delegate, + HostTargetDelegate& hostTargetDelegate) : delegate_(delegate), - executionContextManager_(std::move(executionContextManager)) { + executionContextManager_(std::move(executionContextManager)), + hostTargetDelegate_(hostTargetDelegate) { (void)delegate_; } @@ -77,7 +80,8 @@ RuntimeTarget& InstanceTarget::registerRuntime( .uniqueId = std::nullopt}, delegate, std::move(jsExecutor), - makeVoidExecutor(executorFromThis())); + makeVoidExecutor(executorFromThis()), + hostTargetDelegate_); agents_.forEach([currentRuntime = &*currentRuntime_](InstanceAgent& agent) { agent.setCurrentRuntime(currentRuntime); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.h b/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.h index 4941bab53acc..ee0aaac31ff5 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InstanceTarget.h @@ -24,6 +24,7 @@ namespace facebook::react::jsinspector_modern { class InstanceAgent; class InstanceTracingAgent; class HostTargetTraceRecording; +class HostTargetDelegate; /** * Receives events from an InstanceTarget. This is a shared interface that @@ -55,11 +56,14 @@ class InstanceTarget : public EnableExecutorFromThis { * \param executor An executor that may be used to call methods on this * InstanceTarget while it exists. \c create additionally guarantees that the * executor will not be called after the InstanceTarget is destroyed. + * \param hostTargetDelegate Reference to HostTargetDelegate for + * passing to RuntimeTarget. */ static std::shared_ptr create( std::shared_ptr executionContextManager, InstanceTargetDelegate& delegate, - VoidExecutor executor); + VoidExecutor executor, + HostTargetDelegate& hostTargetDelegate); InstanceTarget(const InstanceTarget&) = delete; InstanceTarget(InstanceTarget&&) = delete; @@ -109,10 +113,12 @@ class InstanceTarget : public EnableExecutorFromThis { * \param delegate The object that will receive events from this target. * The caller is responsible for ensuring that the delegate outlives this * object. + * \param hostTargetDelegate Reference to HostTargetDelegate. */ InstanceTarget( std::shared_ptr executionContextManager, - InstanceTargetDelegate& delegate); + InstanceTargetDelegate& delegate, + HostTargetDelegate& hostTargetDelegate); InstanceTargetDelegate& delegate_; std::shared_ptr currentRuntime_{nullptr}; @@ -125,6 +131,11 @@ class InstanceTarget : public EnableExecutorFromThis { * session - HostTargetTraceRecording. */ std::weak_ptr tracingAgent_; + + /** + * Reference to HostTargetDelegate for passing to RuntimeTarget. + */ + HostTargetDelegate& hostTargetDelegate_; }; } // 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 50562676704a..b65702a829c6 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp @@ -35,9 +35,13 @@ std::shared_ptr RuntimeTarget::create( const ExecutionContextDescription& executionContextDescription, RuntimeTargetDelegate& delegate, RuntimeExecutor jsExecutor, - VoidExecutor selfExecutor) { + VoidExecutor selfExecutor, + HostTargetDelegate& hostTargetDelegate) { std::shared_ptr runtimeTarget{new RuntimeTarget( - executionContextDescription, delegate, std::move(jsExecutor))}; + executionContextDescription, + delegate, + std::move(jsExecutor), + hostTargetDelegate)}; runtimeTarget->setExecutor(std::move(selfExecutor)); runtimeTarget->installGlobals(); return runtimeTarget; @@ -46,14 +50,31 @@ std::shared_ptr RuntimeTarget::create( RuntimeTarget::RuntimeTarget( ExecutionContextDescription executionContextDescription, RuntimeTargetDelegate& delegate, - RuntimeExecutor jsExecutor) + RuntimeExecutor jsExecutor, + HostTargetDelegate& hostTargetDelegate) : executionContextDescription_(std::move(executionContextDescription)), delegate_(delegate), - jsExecutor_(std::move(jsExecutor)) {} + jsExecutor_(std::move(jsExecutor)), + hostTargetDelegate_(hostTargetDelegate) {} + +void RuntimeTarget::consoleTimestampCallback( + std::string_view label, + std::optional start, + std::optional end, + std::optional trackName, + std::optional trackGroup) {} void RuntimeTarget::installGlobals() { // NOTE: RuntimeTarget::installConsoleHandler is in RuntimeTargetConsole.cpp - installConsoleHandler(); + installConsoleHandler([this]( + std::string_view label, + std::optional start, + std::optional end, + std::optional trackName, + std::optional trackGroup) { + consoleTimestampCallback( + label, std::move(start), std::move(end), trackName, trackGroup); + }); installDebuggerSessionObserver(); } @@ -187,6 +208,10 @@ RuntimeTargetController::collectSamplingProfile() { return target_.collectSamplingProfile(); } +HostTargetDelegate& RuntimeTargetController::getHostTargetDelegate() const { + return target_.hostTargetDelegate_; +} + void RuntimeTarget::enableSamplingProfiler() { delegate_.enableSamplingProfiler(); } diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.h b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.h index 1dd9b8ef3984..c98ac22e62b4 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.h @@ -16,6 +16,7 @@ #include "WeakList.h" #include +#include #include #include @@ -39,8 +40,16 @@ class RuntimeAgent; class RuntimeTracingAgent; class RuntimeAgentDelegate; class RuntimeTarget; +class HostTargetDelegate; struct SessionState; +using ConsoleTimeStampCallback = std::function start, + std::optional end, + std::optional trackName, + std::optional trackGroup)>; + /** * Receives events from a RuntimeTarget. This is a shared interface that * each React Native platform needs to implement in order to integrate with @@ -151,6 +160,11 @@ class RuntimeTargetController { */ tracing::RuntimeSamplingProfile collectSamplingProfile(); + /** + * Get access to HostTargetDelegate. + */ + HostTargetDelegate& getHostTargetDelegate() const; + private: RuntimeTarget& target_; }; @@ -178,12 +192,14 @@ class JSINSPECTOR_EXPORT RuntimeTarget * \param selfExecutor An executor that may be used to call methods on this * RuntimeTarget while it exists. \c create additionally guarantees that the * executor will not be called after the RuntimeTarget is destroyed. + * \param hostTargetDelegate Reference to HostTargetDelegate. */ static std::shared_ptr create( const ExecutionContextDescription& executionContextDescription, RuntimeTargetDelegate& delegate, RuntimeExecutor jsExecutor, - VoidExecutor selfExecutor); + VoidExecutor selfExecutor, + HostTargetDelegate& hostTargetDelegate); RuntimeTarget(const RuntimeTarget&) = delete; RuntimeTarget(RuntimeTarget&&) = delete; @@ -251,7 +267,8 @@ class JSINSPECTOR_EXPORT RuntimeTarget RuntimeTarget( ExecutionContextDescription executionContextDescription, RuntimeTargetDelegate& delegate, - RuntimeExecutor jsExecutor); + RuntimeExecutor jsExecutor, + HostTargetDelegate& hostTargetDelegate); const ExecutionContextDescription executionContextDescription_; RuntimeTargetDelegate& delegate_; @@ -266,6 +283,11 @@ class JSINSPECTOR_EXPORT RuntimeTarget */ std::weak_ptr tracingAgent_; + /** + * Reference to HostTargetDelegate. + */ + HostTargetDelegate& hostTargetDelegate_; + /** * 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 @@ -279,10 +301,22 @@ class JSINSPECTOR_EXPORT RuntimeTarget */ void installGlobals(); + /** + * console.timeStamp() callback for propagating events up to HostTarget. + */ + void consoleTimestampCallback( + std::string_view label, + std::optional start, + std::optional end, + std::optional trackName, + std::optional trackGroup); + /** * Install the console API handler. + * \param timestampCallback Callback function for console.timeStamp() event + * propagation. */ - void installConsoleHandler(); + void installConsoleHandler(const ConsoleTimeStampCallback& timestampCallback); /** * Installs __DEBUGGER_SESSION_OBSERVER__ object on the JavaScript's global diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp index 6b30b9886314..e4082ec6368e 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp @@ -6,7 +6,6 @@ */ #include -#include #include #include @@ -389,7 +388,8 @@ auto forwardToOriginalConsole( void consoleTimeStamp( jsi::Runtime& runtime, const jsi::Value* arguments, - size_t argumentsCount) { + size_t argumentsCount, + const ConsoleTimeStampCallback& timestampCallback) { auto& performanceTracer = tracing::PerformanceTracer::getInstance(); if ((!performanceTracer.isTracing() && !ReactPerfettoLogger::isTracing()) || argumentsCount == 0) { @@ -460,6 +460,10 @@ void consoleTimeStamp( } } + if (timestampCallback) { + timestampCallback(label, start, end, trackName, trackGroup); + } + if (performanceTracer.isTracing()) { performanceTracer.reportTimeStamp( label, start, end, trackName, trackGroup, color); @@ -485,7 +489,8 @@ void consoleTimeStamp( void installConsoleTimeStamp( jsi::Runtime& runtime, std::shared_ptr originalConsole, - jsi::Object& consoleObject) { + jsi::Object& consoleObject, + const ConsoleTimeStampCallback& timestampCallback) { consoleObject.setProperty( runtime, "timeStamp", @@ -496,22 +501,25 @@ void installConsoleTimeStamp( forwardToOriginalConsole( originalConsole, "timeStamp", - [](jsi::Runtime& runtime, - const jsi::Value& /*thisVal*/, - const jsi::Value* args, - size_t count) { - consoleTimeStamp(runtime, args, count); + [timestampCallback]( + jsi::Runtime& runtime, + const jsi::Value& /*thisVal*/, + const jsi::Value* args, + size_t count) { + consoleTimeStamp(runtime, args, count, timestampCallback); return jsi::Value::undefined(); }))); } } // namespace -void RuntimeTarget::installConsoleHandler() { +void RuntimeTarget::installConsoleHandler( + const ConsoleTimeStampCallback& timestampCallback) { auto delegateSupportsConsole = delegate_.supportsConsole(); jsExecutor_([selfWeak = weak_from_this(), selfExecutor = executorFromThis(), - delegateSupportsConsole](jsi::Runtime& runtime) { + delegateSupportsConsole, + timestampCallback](jsi::Runtime& runtime) { jsi::Value consolePrototype = jsi::Value::null(); auto originalConsoleVal = runtime.global().getProperty(runtime, "console"); std::shared_ptr originalConsole; @@ -624,7 +632,8 @@ void RuntimeTarget::installConsoleHandler() { /** * console.timeStamp */ - installConsoleTimeStamp(runtime, originalConsole, console); + installConsoleTimeStamp( + runtime, originalConsole, console, timestampCallback); // Install forwarding console methods. #define FORWARDING_CONSOLE_METHOD(name, type) \