mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement local connection for perf metrics in HostTarget (#52838)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52838 **Context** Experimental V2 Performance Monitor prototype, beginning by bringing the [Interaction to Next Paint (INP)](https://web.dev/articles/inp) metric to React Native. **This diff** Wires up a client/subscriber for the `"__chromium_devtools_metrics_reporter"` runtime binding (to which we emit live metrics events since D78904748). This will be used to unpack these performance updates to send to the host platform. - Creates a new `HostRuntimeBinding` helper, which establishes a local/private CDP session. - Conditionally installs our perf metrics runtime binding in `HostTarget` when `perfMonitorV2Enabled` is set. - Wires up a new `onPerfMonitorUpdate` event on `HostTargetDelegate` (unimplemented until the next diff). Changelog: [Internal] Reviewed By: hoxyq Differential Revision: D78904766 fbshipit-source-id: 991f17a4cc69f574917750053a5da31bbc6dc0d5
This commit is contained in:
committed by
Facebook GitHub Bot
parent
be6f3c6f77
commit
2768c84445
+3
@@ -127,6 +127,9 @@ void JReactHostInspectorTarget::onSetPausedInDebuggerMessage(
|
||||
}
|
||||
}
|
||||
|
||||
void JReactHostInspectorTarget::unstable_onPerfMonitorUpdate(
|
||||
const PerfMonitorUpdateRequest& /* unused */) {}
|
||||
|
||||
void JReactHostInspectorTarget::loadNetworkResource(
|
||||
const jsinspector_modern::LoadNetworkResourceRequest& params,
|
||||
jsinspector_modern::ScopedExecutor<
|
||||
|
||||
+2
@@ -84,6 +84,8 @@ class JReactHostInspectorTarget
|
||||
void onReload(const PageReloadRequest& request) override;
|
||||
void onSetPausedInDebuggerMessage(
|
||||
const OverlaySetPausedInDebuggerMessageRequest&) override;
|
||||
void unstable_onPerfMonitorUpdate(
|
||||
const PerfMonitorUpdateRequest& /* unused */) override;
|
||||
void loadNetworkResource(
|
||||
const jsinspector_modern::LoadNetworkResourceRequest& params,
|
||||
jsinspector_modern::ScopedExecutor<
|
||||
|
||||
@@ -146,11 +146,53 @@ class HostCommandSender {
|
||||
std::unique_ptr<ILocalConnection> connection_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables the caller to install and subscribe to a named CDP runtime binding
|
||||
* on the HostTarget via a callback. Note: Per CDP spec, this does not need to
|
||||
* check if the `Runtime` domain is enabled.
|
||||
*/
|
||||
class HostRuntimeBinding {
|
||||
public:
|
||||
explicit HostRuntimeBinding(
|
||||
HostTarget& target,
|
||||
std::string name,
|
||||
std::function<void(std::string)> callback)
|
||||
: connection_(target.connect(std::make_unique<CallbackRemoteConnection>(
|
||||
[callback = std::move(callback)](const std::string& message) {
|
||||
auto parsedMessage = folly::parseJson(message);
|
||||
|
||||
// Ignore initial Runtime.addBinding response
|
||||
if (parsedMessage["id"] == 0 &&
|
||||
parsedMessage["result"].isObject() &&
|
||||
parsedMessage["result"].empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Assert that we only intercept bindingCalled responses
|
||||
assert(
|
||||
parsedMessage["method"].asString() ==
|
||||
"Runtime.bindingCalled");
|
||||
callback(parsedMessage["params"]["payload"].asString());
|
||||
}))) {
|
||||
// Install runtime binding
|
||||
connection_->sendMessage(cdp::jsonRequest(
|
||||
0,
|
||||
"Runtime.addBinding",
|
||||
folly::dynamic::object("name", std::move(name))));
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ILocalConnection> connection_;
|
||||
};
|
||||
|
||||
std::shared_ptr<HostTarget> HostTarget::create(
|
||||
HostTargetDelegate& delegate,
|
||||
VoidExecutor executor) {
|
||||
std::shared_ptr<HostTarget> hostTarget{new HostTarget(delegate)};
|
||||
hostTarget->setExecutor(std::move(executor));
|
||||
if (InspectorFlags::getInstance().getPerfMonitorV2Enabled()) {
|
||||
hostTarget->installPerfMetricsBinding();
|
||||
}
|
||||
return hostTarget;
|
||||
}
|
||||
|
||||
@@ -229,6 +271,19 @@ void HostTarget::sendCommand(HostCommand command) {
|
||||
});
|
||||
}
|
||||
|
||||
void HostTarget::installPerfMetricsBinding() {
|
||||
perfMetricsBinding_ = std::make_unique<HostRuntimeBinding>(
|
||||
*this, // Used immediately
|
||||
"__chromium_devtools_metrics_reporter",
|
||||
[this](const std::string& message) {
|
||||
auto payload = folly::parseJson(message);
|
||||
HostTargetDelegate::PerfMonitorUpdateRequest request{
|
||||
.interactionName = payload["eventName"].asString(),
|
||||
.durationMs = static_cast<uint16_t>(payload["duration"].asInt())};
|
||||
delegate_.unstable_onPerfMonitorUpdate(request);
|
||||
});
|
||||
}
|
||||
|
||||
HostTargetController::HostTargetController(HostTarget& target)
|
||||
: target_(target) {}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ class HostTargetSession;
|
||||
class HostAgent;
|
||||
class HostTracingAgent;
|
||||
class HostCommandSender;
|
||||
class HostRuntimeBinding;
|
||||
class HostTarget;
|
||||
class HostTargetTraceRecording;
|
||||
|
||||
@@ -97,6 +98,11 @@ class HostTargetDelegate : public LoadNetworkResourceDelegate {
|
||||
}
|
||||
};
|
||||
|
||||
struct PerfMonitorUpdateRequest {
|
||||
std::string interactionName;
|
||||
uint16_t durationMs;
|
||||
};
|
||||
|
||||
virtual ~HostTargetDelegate() override;
|
||||
|
||||
/**
|
||||
@@ -125,6 +131,13 @@ class HostTargetDelegate : public LoadNetworkResourceDelegate {
|
||||
virtual void onSetPausedInDebuggerMessage(
|
||||
const OverlaySetPausedInDebuggerMessageRequest& request) = 0;
|
||||
|
||||
/**
|
||||
* [Experimental] Called when the runtime has new data for the V2 Perf
|
||||
* Monitor overlay. This is called on the inspector thread.
|
||||
*/
|
||||
virtual void unstable_onPerfMonitorUpdate(
|
||||
const PerfMonitorUpdateRequest& /*request*/) {}
|
||||
|
||||
/**
|
||||
* Called by NetworkIOAgent on handling a `Network.loadNetworkResource` CDP
|
||||
* request. Platform implementations should override this to perform a
|
||||
@@ -296,6 +309,7 @@ class JSINSPECTOR_EXPORT HostTarget
|
||||
std::shared_ptr<ExecutionContextManager> executionContextManager_;
|
||||
std::shared_ptr<InstanceTarget> currentInstance_{nullptr};
|
||||
std::unique_ptr<HostCommandSender> commandSender_;
|
||||
std::unique_ptr<HostRuntimeBinding> perfMetricsBinding_;
|
||||
|
||||
/**
|
||||
* Current pending trace recording, which encapsulates the configuration of
|
||||
@@ -313,6 +327,13 @@ class JSINSPECTOR_EXPORT HostTarget
|
||||
return currentInstance_ != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a runtime binding subscribing to the Interaction to Next Paint
|
||||
* (INP) live metric, which we broadcast to the V2 Perf Monitor overlay
|
||||
* via \ref HostTargetDelegate::unstable_onPerfMonitorUpdate.
|
||||
*/
|
||||
void installPerfMetricsBinding();
|
||||
|
||||
// Necessary to allow HostAgent to access HostTarget's internals in a
|
||||
// controlled way (i.e. only HostTargetController gets friend access, while
|
||||
// HostAgent itself doesn't).
|
||||
|
||||
@@ -33,6 +33,10 @@ bool InspectorFlags::getNetworkInspectionEnabled() const {
|
||||
return loadFlagsAndAssertUnchanged().networkInspectionEnabled;
|
||||
}
|
||||
|
||||
bool InspectorFlags::getPerfMonitorV2Enabled() const {
|
||||
return loadFlagsAndAssertUnchanged().perfMonitorV2Enabled;
|
||||
}
|
||||
|
||||
void InspectorFlags::dangerouslyResetFlags() {
|
||||
*this = InspectorFlags{};
|
||||
}
|
||||
@@ -59,6 +63,9 @@ const InspectorFlags::Values& InspectorFlags::loadFlagsAndAssertUnchanged()
|
||||
.networkInspectionEnabled =
|
||||
ReactNativeFeatureFlags::enableBridgelessArchitecture() &&
|
||||
ReactNativeFeatureFlags::fuseboxNetworkInspectionEnabled(),
|
||||
.perfMonitorV2Enabled =
|
||||
ReactNativeFeatureFlags::enableBridgelessArchitecture() &&
|
||||
ReactNativeFeatureFlags::perfMonitorV2Enabled(),
|
||||
};
|
||||
|
||||
if (cachedValues_.has_value() && !inconsistentFlagsStateLogged_) {
|
||||
|
||||
@@ -35,6 +35,11 @@ class InspectorFlags {
|
||||
*/
|
||||
bool getNetworkInspectionEnabled() const;
|
||||
|
||||
/**
|
||||
* Flag determining if the V2 in-app Performance Monitor is enabled.
|
||||
*/
|
||||
bool getPerfMonitorV2Enabled() const;
|
||||
|
||||
/**
|
||||
* Forcibly disable the main `getFuseboxEnabled()` flag. This should ONLY be
|
||||
* used by `ReactInstanceIntegrationTest`.
|
||||
@@ -52,6 +57,7 @@ class InspectorFlags {
|
||||
bool fuseboxEnabled;
|
||||
bool isProfilingBuild;
|
||||
bool networkInspectionEnabled;
|
||||
bool perfMonitorV2Enabled;
|
||||
bool operator==(const Values&) const = default;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,14 @@ void CallbackLocalConnection::disconnect() {
|
||||
handler_ = nullptr;
|
||||
}
|
||||
|
||||
CallbackRemoteConnection::CallbackRemoteConnection(
|
||||
std::function<void(std::string)> handler)
|
||||
: handler_(std::move(handler)) {}
|
||||
|
||||
void CallbackRemoteConnection::onMessage(std::string message) {
|
||||
handler_(std::move(message));
|
||||
}
|
||||
|
||||
RAIIRemoteConnection::RAIIRemoteConnection(
|
||||
std::unique_ptr<IRemoteConnection> remote)
|
||||
: remote_(std::move(remote)) {}
|
||||
|
||||
@@ -33,6 +33,25 @@ class CallbackLocalConnection : public ILocalConnection {
|
||||
std::function<void(std::string)> handler_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps a callback function in IRemoteConnection.
|
||||
*/
|
||||
class CallbackRemoteConnection : public IRemoteConnection {
|
||||
public:
|
||||
/**
|
||||
* Creates a new Connection that uses the given callback to receive messages
|
||||
* from the backend.
|
||||
*/
|
||||
explicit CallbackRemoteConnection(std::function<void(std::string)> handler);
|
||||
|
||||
void onMessage(std::string message) override;
|
||||
|
||||
void onDisconnect() override {}
|
||||
|
||||
private:
|
||||
std::function<void(std::string)> handler_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps an IRemoteConnection in a simpler interface that calls `onDisconnect`
|
||||
* implicitly upon destruction.
|
||||
|
||||
Reference in New Issue
Block a user