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 @@ -72,7 +72,6 @@ import com.facebook.react.devsupport.perfmonitor.PerfMonitorDevHelper
import com.facebook.react.devsupport.perfmonitor.PerfMonitorOverlayManager
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags
import com.facebook.react.internal.tracing.PerformanceTracer
import com.facebook.react.modules.core.RCTNativeAppEventEmitter
import com.facebook.react.modules.debug.interfaces.DeveloperSettings
import com.facebook.react.packagerconnection.RequestHandler
Expand Down Expand Up @@ -212,8 +211,6 @@ public abstract class DevSupportManagerBase(
private var perfMonitorOverlayManager: PerfMonitorOverlayManager? = null
private var perfMonitorInitialized = false
private var tracingStateProvider: TracingStateProvider? = null
private var tracingStateSubscriptionId: Int? = null
private var frameTiming: FrameTiming? = null

public override var keyboardShortcutsEnabled: Boolean = true
public override var devMenuEnabled: Boolean = true
Expand Down Expand Up @@ -972,37 +969,12 @@ public abstract class DevSupportManagerBase(
isPackagerConnected = true
perfMonitorOverlayManager?.enable()
perfMonitorOverlayManager?.startBackgroundTrace()

// Subscribe to tracing state changes
tracingStateSubscriptionId =
PerformanceTracer.subscribeToTracingStateChanges(
object : PerformanceTracer.TracingStateCallback {
override fun onTracingStateChanged(isTracing: Boolean) {
if (isTracing) {
if (frameTiming == null) {
currentActivity?.window?.let { window ->
frameTiming = FrameTiming(window)
}
}
frameTiming?.startMonitoring()
} else {
frameTiming?.stopMonitoring()
}
}
}
)
}

override fun onPackagerDisconnected() {
isPackagerConnected = false
perfMonitorOverlayManager?.disable()
perfMonitorOverlayManager?.stopBackgroundTrace()

// Unsubscribe from tracing state changes
tracingStateSubscriptionId?.let { subscriptionId ->
PerformanceTracer.unsubscribeFromTracingStateChanges(subscriptionId)
tracingStateSubscriptionId = null
}
}

override fun onPackagerReloadCommand() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.devsupport
package com.facebook.react.devsupport.inspector

import android.os.Build
import android.os.Handler
Expand All @@ -16,11 +16,8 @@ import com.facebook.proguard.annotations.DoNotStripAny
import com.facebook.soloader.SoLoader

@DoNotStripAny
internal class FrameTiming(private val window: Window) {
init {
SoLoader.loadLibrary("react_devsupportjni")
}

internal class FrameTimingsObserver(private val window: Window) {
private val handler = Handler(Looper.getMainLooper())
private var frameCounter: Int = 0

private external fun setLayerTreeId(frame: String, layerTreeId: Int)
Expand All @@ -40,29 +37,34 @@ internal class FrameTiming(private val window: Window) {
)
}

companion object {
@JvmStatic
private external fun reportFrameTiming(frame: Int, paintStartNanos: Long, paintEndNanos: Long)
}

private val handler = Handler(Looper.getMainLooper())

internal fun startMonitoring() {
fun start() {
frameCounter = 0
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
return
}

window.addOnFrameMetricsAvailableListener(frameMetricsListener, handler)

// Hardcoded frame identfier and layerTreeId. Needed for DevTools to
// begin parsing frame events.
setLayerTreeId("", 1)
}

internal fun stopMonitoring() {
fun stop() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
return
}

window.removeOnFrameMetricsAvailableListener(frameMetricsListener)
handler.removeCallbacksAndMessages(null)
}

private companion object {
init {
SoLoader.loadLibrary("react_devsupportjni")
}

@JvmStatic
private external fun reportFrameTiming(frame: Int, paintStartNanos: Long, paintEndNanos: Long)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@ import com.facebook.react.devsupport.DevMenuConfiguration
import com.facebook.react.devsupport.DevSupportManagerBase
import com.facebook.react.devsupport.DevSupportManagerFactory
import com.facebook.react.devsupport.InspectorFlags
import com.facebook.react.devsupport.inspector.FrameTimingsObserver
import com.facebook.react.devsupport.inspector.InspectorNetworkHelper
import com.facebook.react.devsupport.inspector.InspectorNetworkRequestListener
import com.facebook.react.devsupport.inspector.TracingState
import com.facebook.react.devsupport.inspector.TracingStateListener
import com.facebook.react.devsupport.interfaces.BundleLoadCallback
import com.facebook.react.devsupport.interfaces.DevSupportManager
import com.facebook.react.devsupport.interfaces.DevSupportManager.PausedInDebuggerOverlayCommandListener
Expand Down Expand Up @@ -147,6 +150,7 @@ public class ReactHostImpl(
private val beforeDestroyListeners: MutableList<() -> Unit> = CopyOnWriteArrayList()

internal var reactHostInspectorTarget: ReactHostInspectorTarget? = null
private var frameTimingsObserver: FrameTimingsObserver? = null

@Volatile private var hostInvalidated = false

Expand Down Expand Up @@ -1442,8 +1446,7 @@ public class ReactHostImpl(
// If the host has been invalidated, now that the current context/instance
// has been unregistered, we can safely destroy the host's inspector
// target.
reactHostInspectorTarget?.close()
reactHostInspectorTarget = null
destroyReactHostInspectorTarget()
}

// Step 1: Destroy DevSupportManager
Expand Down Expand Up @@ -1554,13 +1557,45 @@ public class ReactHostImpl(

internal fun getOrCreateReactHostInspectorTarget(): ReactHostInspectorTarget? {
if (reactHostInspectorTarget == null && InspectorFlags.getFuseboxEnabled()) {
// NOTE: ReactHostInspectorTarget only retains a weak reference to `this`.
reactHostInspectorTarget = ReactHostInspectorTarget(this)
reactHostInspectorTarget = createReactHostInspectorTarget()
}

return reactHostInspectorTarget
}

private fun createReactHostInspectorTarget(): ReactHostInspectorTarget {
// NOTE: ReactHostInspectorTarget only retains a weak reference to `this`.
val inspectorTarget = ReactHostInspectorTarget(this)
inspectorTarget.registerTracingStateListener(
TracingStateListener { state: TracingState, _screenshotsEnabled: Boolean ->
when (state) {
TracingState.ENABLED_IN_BACKGROUND_MODE,
TracingState.ENABLED_IN_CDP_MODE -> {
currentActivity?.window?.let { window ->
val observer = FrameTimingsObserver(window)
observer.start()
frameTimingsObserver = observer
}
}
TracingState.DISABLED -> {
frameTimingsObserver?.stop()
frameTimingsObserver = null
}
}
}
)

return inspectorTarget
}

private fun destroyReactHostInspectorTarget() {
frameTimingsObserver?.stop()
frameTimingsObserver = null

reactHostInspectorTarget?.close()
reactHostInspectorTarget = null
}

@ThreadConfined(ThreadConfined.UI)
internal fun unregisterInstanceFromInspector(reactInstance: ReactInstance?) {
if (reactInstance != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace facebook::react::jsinspector_modern {
*/
class JFrameTiming : public jni::JavaClass<JFrameTiming> {
public:
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/devsupport/FrameTiming;";
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/devsupport/inspector/FrameTimingsObserver;";

static void
reportFrameTiming(jni::alias_ref<jclass> /*unused*/, jint frame, jlong paintStartNanos, jlong paintEndNanos);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ bool JReactHostInspectorTarget::startBackgroundTrace() {
}
}

tracing::TraceRecordingState JReactHostInspectorTarget::stopTracing() {
tracing::HostTracingProfile JReactHostInspectorTarget::stopTracing() {
if (inspectorTarget_) {
return inspectorTarget_->stopTracing();
} else {
Expand All @@ -205,29 +205,29 @@ tracing::TraceRecordingState JReactHostInspectorTarget::stopTracing() {
jboolean JReactHostInspectorTarget::stopAndMaybeEmitBackgroundTrace() {
auto capturedTrace = inspectorTarget_->stopTracing();
if (inspectorTarget_->hasActiveSessionWithFuseboxClient()) {
inspectorTarget_->emitTraceRecordingForFirstFuseboxClient(
inspectorTarget_->emitTracingProfileForFirstFuseboxClient(
std::move(capturedTrace));
return jboolean(true);
}

stashTraceRecordingState(std::move(capturedTrace));
stashTracingProfile(std::move(capturedTrace));
return jboolean(false);
}

void JReactHostInspectorTarget::stopAndDiscardBackgroundTrace() {
inspectorTarget_->stopTracing();
}

void JReactHostInspectorTarget::stashTraceRecordingState(
tracing::TraceRecordingState&& state) {
stashedTraceRecordingState_ = std::move(state);
void JReactHostInspectorTarget::stashTracingProfile(
tracing::HostTracingProfile&& hostTracingProfile) {
stashedTracingProfile_ = std::move(hostTracingProfile);
}

std::optional<tracing::TraceRecordingState> JReactHostInspectorTarget::
unstable_getTraceRecordingThatWillBeEmittedOnInitialization() {
auto state = std::move(stashedTraceRecordingState_);
stashedTraceRecordingState_.reset();
return state;
std::optional<tracing::HostTracingProfile> JReactHostInspectorTarget::
unstable_getHostTracingProfileThatWillBeEmittedOnInitialization() {
auto tracingProfile = std::move(stashedTracingProfile_);
stashedTracingProfile_.reset();
return tracingProfile;
}

void JReactHostInspectorTarget::registerNatives() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ class JReactHostInspectorTarget : public jni::HybridClass<JReactHostInspectorTar
void loadNetworkResource(
const jsinspector_modern::LoadNetworkResourceRequest &params,
jsinspector_modern::ScopedExecutor<jsinspector_modern::NetworkRequestListener> executor) override;
std::optional<jsinspector_modern::tracing::TraceRecordingState>
unstable_getTraceRecordingThatWillBeEmittedOnInitialization() override;
std::optional<jsinspector_modern::tracing::HostTracingProfile>
unstable_getHostTracingProfileThatWillBeEmittedOnInitialization() override;
jsinspector_modern::HostTargetTracingDelegate *getTracingDelegate() override;

private:
Expand All @@ -213,20 +213,19 @@ class JReactHostInspectorTarget : public jni::HybridClass<JReactHostInspectorTar
std::optional<int> inspectorPageId_;

/**
* Stops previously started trace recording and returns the captured trace.
* Stops previously started trace recording and returns the captured HostTracingProfile.
*/
jsinspector_modern::tracing::TraceRecordingState stopTracing();
jsinspector_modern::tracing::HostTracingProfile stopTracing();
/**
* Stashes previously recorded trace recording state that will be emitted when
* Stashes previously recorded HostTracingProfile that will be emitted when
* CDP session is created. Once emitted, the value will be cleared from this
* instance.
*/
void stashTraceRecordingState(jsinspector_modern::tracing::TraceRecordingState &&state);
void stashTracingProfile(jsinspector_modern::tracing::HostTracingProfile &&hostTracingProfile);
/**
* Previously recorded trace recording state that will be emitted when
* CDP session is created.
* Previously recorded HostTracingProfile that will be emitted when CDP session is created.
*/
std::optional<jsinspector_modern::tracing::TraceRecordingState> stashedTraceRecordingState_;
std::optional<jsinspector_modern::tracing::HostTracingProfile> stashedTracingProfile_;
/**
* Encapsulates the logic around tracing for this HostInspectorTarget.
*/
Expand Down
19 changes: 9 additions & 10 deletions packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,9 @@ class HostAgent::Impl final {

auto stashedTraceRecording =
targetController_.getDelegate()
.unstable_getTraceRecordingThatWillBeEmittedOnInitialization();
.unstable_getHostTracingProfileThatWillBeEmittedOnInitialization();
if (stashedTraceRecording.has_value()) {
tracingAgent_.emitExternalTraceRecording(
tracingAgent_.emitExternalHostTracingProfile(
std::move(stashedTraceRecording.value()));
}

Expand Down Expand Up @@ -385,12 +385,12 @@ class HostAgent::Impl final {
return fuseboxClientType_ == FuseboxClientType::Fusebox;
}

void emitExternalTraceRecording(
tracing::TraceRecordingState traceRecording) const {
void emitExternalTracingProfile(
tracing::HostTracingProfile tracingProfile) const {
assert(
hasFuseboxClientConnected() &&
"Attempted to emit a trace recording to a non-Fusebox client");
tracingAgent_.emitExternalTraceRecording(std::move(traceRecording));
tracingAgent_.emitExternalHostTracingProfile(std::move(tracingProfile));
}

void emitSystemStateChanged(bool isSingleHost) {
Expand Down Expand Up @@ -506,8 +506,7 @@ class HostAgent::Impl final {
bool hasFuseboxClientConnected() const {
return false;
}
void emitExternalTraceRecording(tracing::TraceRecordingState traceRecording) {
}
void emitExternalTracingProfile(tracing::HostTracingProfile tracingProfile) {}
void emitSystemStateChanged(bool isSingleHost) {}
};

Expand Down Expand Up @@ -543,9 +542,9 @@ bool HostAgent::hasFuseboxClientConnected() const {
return impl_->hasFuseboxClientConnected();
}

void HostAgent::emitExternalTraceRecording(
tracing::TraceRecordingState traceRecording) const {
impl_->emitExternalTraceRecording(std::move(traceRecording));
void HostAgent::emitExternalTracingProfile(
tracing::HostTracingProfile tracingProfile) const {
impl_->emitExternalTracingProfile(std::move(tracingProfile));
}

void HostAgent::emitSystemStateChanged(bool isSingleHost) const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ class HostAgent final {
bool hasFuseboxClientConnected() const;

/**
* Emits the trace recording that was captured externally, not via the
* Emits the HostTracingProfile that was captured externally, not via the
* CDP-initiated request.
*/
void emitExternalTraceRecording(tracing::TraceRecordingState traceRecording) const;
void emitExternalTracingProfile(tracing::HostTracingProfile tracingProfile) const;

/**
* Emits a system state changed event when the number of ReactHost instances
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@ class HostTargetSession {
return hostAgent_.hasFuseboxClientConnected();
}

void emitTraceRecording(tracing::TraceRecordingState traceRecording) const {
hostAgent_.emitExternalTraceRecording(std::move(traceRecording));
void emitHostTracingProfile(
tracing::HostTracingProfile tracingProfile) const {
hostAgent_.emitExternalTracingProfile(std::move(tracingProfile));
}

private:
Expand Down Expand Up @@ -382,21 +383,21 @@ bool HostTarget::hasActiveSessionWithFuseboxClient() const {
return hasActiveFuseboxSession;
}

void HostTarget::emitTraceRecordingForFirstFuseboxClient(
tracing::TraceRecordingState traceRecording) const {
void HostTarget::emitTracingProfileForFirstFuseboxClient(
tracing::HostTracingProfile tracingProfile) const {
bool emitted = false;
sessions_.forEach([&](HostTargetSession& session) {
if (emitted) {
/**
* TraceRecordingState object is not copiable for performance reasons,
* HostTracingProfile object is not copiable for performance reasons,
* because it could contain large Runtime sampling profile object.
*
* This approach would not work with multi-client debugger setup.
*/
return;
}
if (session.hasFuseboxClient()) {
session.emitTraceRecording(std::move(traceRecording));
session.emitHostTracingProfile(std::move(tracingProfile));
emitted = true;
}
});
Expand Down
Loading