Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ target_link_libraries(jsinspector
react_featureflags
runtimeexecutor
reactperflogger
react_utils
)
target_compile_reactnative_options(jsinspector PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
Expand Down
45 changes: 45 additions & 0 deletions packages/react-native/ReactCommon/jsinspector-modern/EnumArray.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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 <array>
#include <limits>

namespace facebook::react::jsinspector_modern {

/**
* A statically-sized array with an enum class as the index type.
* Values are value-initialized (i.e. zero-initialized for integral types).
* Requires that the enum class has a kMaxValue member.
*/
template <class IndexType, class ValueType>
requires std::is_enum_v<IndexType> &&
std::is_same_v<std::underlying_type_t<IndexType>, int> &&
requires { IndexType::kMaxValue; } &&
(static_cast<int>(IndexType::kMaxValue) < std::numeric_limits<int>::max())

class EnumArray {
public:
constexpr ValueType& operator[](IndexType i) {
return array_[static_cast<int>(i)];
}

constexpr const ValueType& operator[](IndexType i) const {
return array_[static_cast<int>(i)];
}

constexpr int size() const {
return size_;
}

private:
constexpr static int size_ = static_cast<int>(IndexType::kMaxValue) + 1;

std::array<ValueType, size_> array_{};
};
} // namespace facebook::react::jsinspector_modern
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Pod::Spec.new do |s|
add_dependency(s, "React-jsinspectortracing", :framework_name => 'jsinspector_moderntracing')
s.dependency "React-perflogger", version
add_dependency(s, "React-oscompat")

add_dependency(s, "React-utils", :additional_framework_paths => ["react/utils/platform/ios"])
if use_hermes()
s.dependency "hermes-engine"
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,19 @@ RuntimeAgent::RuntimeAgent(
}
}

if (sessionState_.isRuntimeDomainEnabled &&
sessionState_.isLogDomainEnabled) {
targetController_.notifyDebuggerSessionCreated();
if (sessionState_.isRuntimeDomainEnabled) {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Runtime, true, *this);
}

if (sessionState_.isLogDomainEnabled) {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Log, true, *this);
}

if (sessionState_.isNetworkDomainEnabled) {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Network, true, *this);
}
}

Expand All @@ -55,18 +65,29 @@ bool RuntimeAgent::handleRequest(const cdp::PreparsedRequest& req) {
// We are not responding to this request, just processing a side effect.
return false;
}
if (req.method == "Runtime.enable" && sessionState_.isLogDomainEnabled) {
targetController_.notifyDebuggerSessionCreated();
}
if (req.method == "Log.enable" && sessionState_.isRuntimeDomainEnabled) {
targetController_.notifyDebuggerSessionCreated();
}
if (req.method == "Runtime.disable" && sessionState_.isLogDomainEnabled) {
targetController_.notifyDebuggerSessionDestroyed();
}
if (req.method == "Log.disable" && sessionState_.isRuntimeDomainEnabled) {
targetController_.notifyDebuggerSessionDestroyed();
if (req.method == "Runtime.enable" || req.method == "Runtime.disable") {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Runtime,
sessionState_.isRuntimeDomainEnabled,
*this);
// Fall through
} else if (req.method == "Log.enable" || req.method == "Log.disable") {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Log,
sessionState_.isLogDomainEnabled,
*this);
// Fall through
} else if (
req.method == "Network.enable" || req.method == "Network.disable") {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Network,
sessionState_.isNetworkDomainEnabled,
*this);

// We are not responding to this request, just processing a side effect.
return false;
}

if (delegate_) {
return delegate_->handleRequest(req);
}
Expand Down Expand Up @@ -104,9 +125,17 @@ RuntimeAgent::ExportedState RuntimeAgent::getExportedState() {
}

RuntimeAgent::~RuntimeAgent() {
if (sessionState_.isRuntimeDomainEnabled &&
sessionState_.isLogDomainEnabled) {
targetController_.notifyDebuggerSessionDestroyed();
if (sessionState_.isRuntimeDomainEnabled) {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Runtime, false, *this);
}
if (sessionState_.isLogDomainEnabled) {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Log, false, *this);
}
if (sessionState_.isNetworkDomainEnabled) {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Network, false, *this);
}

// TODO: Eventually, there may be more than one Runtime per Page, and we'll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ RuntimeTarget::RuntimeTarget(
void RuntimeTarget::installGlobals() {
// NOTE: RuntimeTarget::installConsoleHandler is in RuntimeTargetConsole.cpp
installConsoleHandler();
// NOTE: RuntimeTarget::installDebuggerSessionObserver is in
// RuntimeTargetDebuggerSessionObserver.cpp
installDebuggerSessionObserver();
// NOTE: RuntimeTarget::installNetworkReporterAPI is in
// RuntimeTargetNetwork.cpp
installNetworkReporterAPI();
}

std::shared_ptr<RuntimeAgent> RuntimeTarget::createAgent(
Expand Down Expand Up @@ -158,6 +163,60 @@ void RuntimeTarget::emitDebuggerSessionDestroyed() {
});
}

void RuntimeTarget::enableSamplingProfiler() {
delegate_.enableSamplingProfiler();
}

void RuntimeTarget::disableSamplingProfiler() {
delegate_.disableSamplingProfiler();
}

tracing::RuntimeSamplingProfile RuntimeTarget::collectSamplingProfile() {
return delegate_.collectSamplingProfile();
}

void RuntimeTarget::notifyDomainStateChanged(
Domain domain,
bool enabled,
const RuntimeAgent& notifyingAgent) {
bool runtimeAndLogStatusBefore = false, runtimeAndLogStatusAfter = false;
if (domain == Domain::Log || domain == Domain::Runtime) {
runtimeAndLogStatusBefore =
agentsByEnabledDomain_[Domain::Runtime].contains(&notifyingAgent) &&
agentsByEnabledDomain_[Domain::Log].contains(&notifyingAgent);
}

if (enabled) {
agentsByEnabledDomain_[domain].insert(&notifyingAgent);
} else {
agentsByEnabledDomain_[domain].erase(&notifyingAgent);
}
threadSafeDomainStatus_[domain] = !agentsByEnabledDomain_[domain].empty();

if (domain == Domain::Log || domain == Domain::Runtime) {
runtimeAndLogStatusAfter =
agentsByEnabledDomain_[Domain::Runtime].contains(&notifyingAgent) &&
agentsByEnabledDomain_[Domain::Log].contains(&notifyingAgent);

if (runtimeAndLogStatusBefore != runtimeAndLogStatusAfter) {
if (runtimeAndLogStatusAfter) {
if (++agentsWithRuntimeAndLogDomainsEnabled_ == 1) {
emitDebuggerSessionCreated();
}
} else {
assert(agentsWithRuntimeAndLogDomainsEnabled_ > 0);
if (--agentsWithRuntimeAndLogDomainsEnabled_ == 0) {
emitDebuggerSessionDestroyed();
}
}
}
}
}

bool RuntimeTarget::isDomainEnabled(Domain domain) const {
return threadSafeDomainStatus_[domain];
}

RuntimeTargetController::RuntimeTargetController(RuntimeTarget& target)
: target_(target) {}

Expand All @@ -166,14 +225,6 @@ void RuntimeTargetController::installBindingHandler(
target_.installBindingHandler(bindingName);
}

void RuntimeTargetController::notifyDebuggerSessionCreated() {
target_.emitDebuggerSessionCreated();
}

void RuntimeTargetController::notifyDebuggerSessionDestroyed() {
target_.emitDebuggerSessionDestroyed();
}

void RuntimeTargetController::enableSamplingProfiler() {
target_.enableSamplingProfiler();
}
Expand All @@ -187,16 +238,11 @@ RuntimeTargetController::collectSamplingProfile() {
return target_.collectSamplingProfile();
}

void RuntimeTarget::enableSamplingProfiler() {
delegate_.enableSamplingProfiler();
}

void RuntimeTarget::disableSamplingProfiler() {
delegate_.disableSamplingProfiler();
}

tracing::RuntimeSamplingProfile RuntimeTarget::collectSamplingProfile() {
return delegate_.collectSamplingProfile();
void RuntimeTargetController::notifyDomainStateChanged(
Domain domain,
bool enabled,
const RuntimeAgent& notifyingAgent) {
target_.notifyDomainStateChanged(domain, enabled, notifyingAgent);
}

} // namespace facebook::react::jsinspector_modern
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include "ConsoleMessage.h"
#include "EnumArray.h"
#include "ExecutionContext.h"
#include "InspectorInterfaces.h"
#include "RuntimeAgent.h"
Expand Down Expand Up @@ -123,6 +124,8 @@ class RuntimeTargetDelegate {
*/
class RuntimeTargetController {
public:
enum class Domain { Network, Runtime, Log, kMaxValue };

explicit RuntimeTargetController(RuntimeTarget& target);

/**
Expand All @@ -133,16 +136,13 @@ class RuntimeTargetController {
void installBindingHandler(const std::string& bindingName);

/**
* Notifies the target to emit some message that debugger session is
* created.
*/
void notifyDebuggerSessionCreated();

/**
* Notifies the target to emit some message that debugger session is
* destroyed.
* Notifies the target that an agent has received an enable or disable
* message for the given domain.
*/
void notifyDebuggerSessionDestroyed();
void notifyDomainStateChanged(
Domain domain,
bool enabled,
const RuntimeAgent& notifyingAgent);

/**
* Start sampling profiler for the corresponding RuntimeTarget.
Expand Down Expand Up @@ -239,6 +239,8 @@ class JSINSPECTOR_EXPORT RuntimeTarget
tracing::RuntimeSamplingProfile collectSamplingProfile();

private:
using Domain = RuntimeTargetController::Domain;

/**
* Constructs a new RuntimeTarget. The caller must call setExecutor
* immediately afterwards.
Expand Down Expand Up @@ -267,6 +269,25 @@ class JSINSPECTOR_EXPORT RuntimeTarget
WeakList<RuntimeAgent> agents_;
RuntimeTargetController controller_{*this};

/**
* Keeps track of the agents that have enabled various domains.
*/
EnumArray<Domain, std::unordered_set<const RuntimeAgent*>>
agentsByEnabledDomain_;

/**
* For each Domain, contains true if the domain has been enabled by any
* active agent. Unlike agentsByEnabledDomain_, this is safe to read from any
* thread. \see isDomainEnabled.
*/
EnumArray<Domain, std::atomic<bool>> threadSafeDomainStatus_{};

/**
* The number of agents that currently have both the Log and Runtime domains
* enabled.
*/
size_t agentsWithRuntimeAndLogDomainsEnabled_{0};

/**
* This TracingAgent is owned by the InstanceTracingAgent, both are bound to
* the lifetime of their corresponding targets and the lifetime of the tracing
Expand Down Expand Up @@ -299,6 +320,12 @@ class JSINSPECTOR_EXPORT RuntimeTarget
*/
void installDebuggerSessionObserver();

/**
* Installs the private __NETWORK_REPORTER__ object on the Runtime's
* global object.
*/
void installNetworkReporterAPI();

/**
* Propagates the debugger session state change to the JavaScript via calling
* onStatusChange on __DEBUGGER_SESSION_OBSERVER__.
Expand All @@ -311,6 +338,29 @@ class JSINSPECTOR_EXPORT RuntimeTarget
*/
void emitDebuggerSessionDestroyed();

/**
* \returns a globally unique ID for a network request.
* May be called from any thread as long as the RuntimeTarget is valid.
*/
std::string createNetworkRequestId();

/**
* Notifies the target that an agent has received an enable or disable
* message for the given domain.
*/
void notifyDomainStateChanged(
Domain domain,
bool enabled,
const RuntimeAgent& notifyingAgent);

/**
* Checks whether the given domain is enabled in at least one session
* that is currently connected. This may be called from any thread, with
* the caveat that the result can change at arbitrary times unless the caller
* is on the inspector thread.
*/
bool isDomainEnabled(Domain domain) const;

// Necessary to allow RuntimeAgent to access RuntimeTarget's internals in a
// controlled way (i.e. only RuntimeTargetController gets friend access, while
// RuntimeAgent itself doesn't).
Expand Down
Loading
Loading