diff --git a/packages/react-native/Package.swift b/packages/react-native/Package.swift index be3ead700dc0..fd34d2dfb0f0 100644 --- a/packages/react-native/Package.swift +++ b/packages/react-native/Package.swift @@ -463,7 +463,7 @@ let reactFabric = RNTarget( "components/virtualview", "components/root/tests", ], - dependencies: [.reactNativeDependencies, .reactJsiExecutor, .rctTypesafety, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .reactRendererDebug, .reactGraphics, .yoga], + dependencies: [.reactNativeDependencies, .reactJsiExecutor, .rctTypesafety, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .reactRendererDebug, .reactGraphics, .yoga, .reactJsInspectorTracing], sources: ["animated", "animationbackend", "animations", "attributedstring", "core", "componentregistry", "componentregistry/native", "components/root", "components/view", "components/view/platform/cxx", "components/scrollview", "components/scrollview/platform/cxx", "components/scrollview/platform/ios", "components/legacyviewmanagerinterop", "components/legacyviewmanagerinterop/platform/ios", "dom", "scheduler", "mounting", "observers/events", "observers/intersection", "observers/mutation", "telemetry", "consistency", "leakchecker", "uimanager", "uimanager/consistency", "viewtransition"] ) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 98883b24dc94..8871af16dbc1 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -3114,6 +3114,7 @@ public final class com/facebook/react/soloader/OpenSourceMergedSoMapping : com/f public final fun libreact_devsupportjni_so ()I public final fun libreact_featureflagsjni_so ()I public final fun libreact_newarchdefaults_so ()I + public final fun libreact_tracingjni_so ()I public final fun libreactnative_so ()I public final fun libreactnativeblob_so ()I public final fun libreactnativejni_common_so ()I diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.kt index 9d372648c81b..984ff9e75074 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.kt @@ -21,6 +21,7 @@ import com.facebook.react.fabric.FabricUIManager import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem import com.facebook.react.fabric.mounting.mountitems.MountItem import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags +import com.facebook.react.internal.tracing.PerformanceTracer import com.facebook.systrace.Systrace import java.util.Queue import java.util.concurrent.ConcurrentLinkedQueue @@ -201,9 +202,16 @@ internal class MountItemDispatcher( "MountItemDispatcher::mountViews viewCommandMountItems", ) - for (command in commands) { - dispatchViewCommand(command) - } + PerformanceTracer.trace( + "view commands", + "Renderer", + "\u269b Native", + { -> + for (command in commands) { + dispatchViewCommand(command) + } + }, + ) Systrace.endSection(Systrace.TRACE_TAG_REACT) } @@ -215,12 +223,21 @@ internal class MountItemDispatcher( Systrace.TRACE_TAG_REACT, "MountItemDispatcher::mountViews preMountItems", ) - for (preMountItem in preMountItems) { - if (ReactNativeFeatureFlags.enableFabricLogs()) { - printMountItem(preMountItem, "dispatchMountItems: Executing preMountItem") - } - executeOrEnqueue(preMountItem) - } + + PerformanceTracer.trace( + "premount", + "Renderer", + "\u269b Native", + { -> + for (preMountItem in preMountItems) { + if (ReactNativeFeatureFlags.enableFabricLogs()) { + printMountItem(preMountItem, "dispatchMountItems: Executing preMountItem") + } + executeOrEnqueue(preMountItem) + } + }, + ) + Systrace.endSection(Systrace.TRACE_TAG_REACT) } @@ -229,46 +246,58 @@ internal class MountItemDispatcher( Systrace.TRACE_TAG_REACT, "MountItemDispatcher::mountViews mountItems to execute", ) - val batchedExecutionStartTime = SystemClock.uptimeMillis() - for (mountItem in items) { - if (ReactNativeFeatureFlags.enableFabricLogs()) { - printMountItem(mountItem, "dispatchMountItems: Executing mountItem") - } + PerformanceTracer.trace( + "mount", + "Renderer", + "\u269b Native", + { -> + val batchedExecutionStartTime = SystemClock.uptimeMillis() - val command = mountItem as? DispatchCommandMountItem - if (command != null) { - dispatchViewCommand(command) - continue - } + for (mountItem in items) { + if (ReactNativeFeatureFlags.enableFabricLogs()) { + printMountItem(mountItem, "dispatchMountItems: Executing mountItem") + } - try { - executeOrEnqueue(mountItem) - } catch (e: Throwable) { - // If there's an exception, we want to log diagnostics in prod and rethrow. - FLog.e(TAG, "dispatchMountItems: caught exception, displaying mount state", e) - if (ReactNativeFeatureFlags.enableFabricLogs()) { - for (m in items) { - if (m === mountItem) { - // We want to mark the mount item that caused exception - FLog.e(TAG, "dispatchMountItems: mountItem: next mountItem triggered exception!") + val command = mountItem as? DispatchCommandMountItem + if (command != null) { + dispatchViewCommand(command) + continue } - printMountItem(m, "dispatchMountItems: mountItem") - } - } - if (mountItem.getSurfaceId() != View.NO_ID) { - mountingManager.getSurfaceManager(mountItem.getSurfaceId())?.printSurfaceState() - } + try { + executeOrEnqueue(mountItem) + } catch (e: Throwable) { + // If there's an exception, we want to log diagnostics in prod and rethrow. + FLog.e(TAG, "dispatchMountItems: caught exception, displaying mount state", e) + if (ReactNativeFeatureFlags.enableFabricLogs()) { + for (m in items) { + if (m === mountItem) { + // We want to mark the mount item that caused exception + FLog.e( + TAG, + "dispatchMountItems: mountItem: next mountItem triggered exception!", + ) + } + printMountItem(m, "dispatchMountItems: mountItem") + } + } + + if (mountItem.getSurfaceId() != View.NO_ID) { + mountingManager.getSurfaceManager(mountItem.getSurfaceId())?.printSurfaceState() + } + + if (ReactIgnorableMountingException.isIgnorable(e)) { + ReactSoftExceptionLogger.logSoftException(TAG, e) + } else { + throw e + } + } + } + batchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime + }, + ) - if (ReactIgnorableMountingException.isIgnorable(e)) { - ReactSoftExceptionLogger.logSoftException(TAG, e) - } else { - throw e - } - } - } - batchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime Systrace.endSection(Systrace.TRACE_TAG_REACT) } @@ -299,26 +328,34 @@ internal class MountItemDispatcher( private fun dispatchPreMountItemsImpl(deadline: Long) { Systrace.beginSection(Systrace.TRACE_TAG_REACT, "MountItemDispatcher::premountViews") - // dispatchPreMountItems cannot be reentrant, but we want to prevent dispatchMountItems from - // reentering during dispatchPreMountItems - inDispatch = true - - try { - while (true) { - if (System.nanoTime() > deadline) { - break - } + PerformanceTracer.trace( + "premount", + "Renderer", + "\u269b Native", + { -> + // dispatchPreMountItems cannot be reentrant, but we want to prevent dispatchMountItems + // from + // reentering during dispatchPreMountItems + inDispatch = true + + try { + while (true) { + if (System.nanoTime() > deadline) { + break + } - // If list is empty, `poll` will return null, or var will never be set - val preMountItemToDispatch = preMountItems.poll() ?: break - if (ReactNativeFeatureFlags.enableFabricLogs()) { - printMountItem(preMountItemToDispatch, "dispatchPreMountItems") - } - executeOrEnqueue(preMountItemToDispatch) - } - } finally { - inDispatch = false - } + // If list is empty, `poll` will return null, or var will never be set + val preMountItemToDispatch = preMountItems.poll() ?: break + if (ReactNativeFeatureFlags.enableFabricLogs()) { + printMountItem(preMountItemToDispatch, "dispatchPreMountItems") + } + executeOrEnqueue(preMountItemToDispatch) + } + } finally { + inDispatch = false + } + }, + ) Systrace.endSection(Systrace.TRACE_TAG_REACT) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/tracing/PerformanceTracer.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/tracing/PerformanceTracer.kt index 8bac4f57c382..c8abc7bb3850 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/tracing/PerformanceTracer.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/tracing/PerformanceTracer.kt @@ -20,7 +20,7 @@ import com.facebook.soloader.SoLoader @DoNotStrip public object PerformanceTracer { init { - SoLoader.loadLibrary("react_performancetracerjni") + SoLoader.loadLibrary("react_tracingjni") } public fun trace(name: String, block: () -> T): T { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/soloader/OpenSourceMergedSoMapping.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/soloader/OpenSourceMergedSoMapping.kt index c8ba2b2190c4..b81427f3ae6f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/soloader/OpenSourceMergedSoMapping.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/soloader/OpenSourceMergedSoMapping.kt @@ -28,6 +28,7 @@ public object OpenSourceMergedSoMapping : ExternalSoMapping { "react_devsupportjni", "react_featureflagsjni", "react_newarchdefaults", + "react_tracingjni", "reactnativeblob", "reactnativejni", "reactnativejni_common", @@ -57,6 +58,7 @@ public object OpenSourceMergedSoMapping : ExternalSoMapping { "react_devsupportjni" -> libreact_devsupportjni_so() "react_featureflagsjni" -> libreact_featureflagsjni_so() "react_newarchdefaults" -> libreact_newarchdefaults_so() + "react_tracingjni" -> libreact_tracingjni_so() "reactnative" -> libreactnative_so() "reactnativeblob" -> libreactnativeblob_so() "reactnativejni" -> libreactnativejni_so() @@ -88,6 +90,8 @@ public object OpenSourceMergedSoMapping : ExternalSoMapping { public external fun libreact_newarchdefaults_so(): Int + public external fun libreact_tracingjni_so(): Int + public external fun libreactnative_so(): Int public external fun libreactnativeblob_so(): Int diff --git a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt index de4106d57321..6d245b2239cb 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt +++ b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt @@ -159,6 +159,7 @@ add_react_android_subdir(src/main/jni/react/hermes/instrumentation/) add_react_android_subdir(src/main/jni/react/runtime/cxxreactpackage) add_react_android_subdir(src/main/jni/react/runtime/jni) add_react_android_subdir(src/main/jni/react/runtime/hermes/jni) +add_react_android_subdir(src/main/jni/react/tracing) add_react_android_subdir(src/main/jni/react/devsupport) # SoMerging Utils @@ -231,6 +232,7 @@ add_library(reactnative $ $ $ + $ $ $ $ @@ -331,6 +333,7 @@ target_include_directories(reactnative $ $ $ + $ $ $ $ diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/tracing/CMakeLists.txt b/packages/react-native/ReactAndroid/src/main/jni/react/tracing/CMakeLists.txt new file mode 100644 index 000000000000..ee65da316089 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/jni/react/tracing/CMakeLists.txt @@ -0,0 +1,28 @@ +# 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. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_VERBOSE_MAKEFILE on) + +include(${REACT_ANDROID_DIR}/src/main/jni/first-party/jni-lib-merge/SoMerging-utils.cmake) +include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake) + +file(GLOB react_tracingjni_SRC CONFIGURE_DEPENDS *.cpp) + +add_library(react_tracingjni OBJECT ${react_tracingjni_SRC}) + +target_merge_so(react_tracingjni) + +target_include_directories(react_tracingjni PUBLIC .) + +target_link_libraries(react_tracingjni + fbjni + folly_runtime + jsinspector + jsinspector_tracing + react_timing + reactnativejni) + +target_compile_reactnative_options(react_tracingjni PRIVATE) diff --git a/packages/react-native/ReactCommon/React-Fabric.podspec b/packages/react-native/ReactCommon/React-Fabric.podspec index 327baa5da5b7..bc7f02073d01 100644 --- a/packages/react-native/ReactCommon/React-Fabric.podspec +++ b/packages/react-native/ReactCommon/React-Fabric.podspec @@ -174,6 +174,7 @@ Pod::Spec.new do |s| end s.subspec "mounting" do |ss| + ss.dependency "React-jsinspectortracing" ss.source_files = podspec_sources("react/renderer/mounting/**/*.{m,mm,cpp,h}", "react/renderer/mounting/**/*.h") ss.exclude_files = "react/renderer/mounting/tests" ss.header_dir = "react/renderer/mounting" diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/CMakeLists.txt b/packages/react-native/ReactCommon/react/renderer/mounting/CMakeLists.txt index eefd67c9f21a..895801657e24 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/renderer/mounting/CMakeLists.txt @@ -22,6 +22,7 @@ target_link_libraries(react_renderer_mounting glog glog_init jsi + jsinspector_tracing react_debug react_renderer_core react_renderer_debug diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp index 9dbeee58107f..9666041e47fe 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -8,6 +8,7 @@ #include "ShadowTree.h" #include +#include #include #include #include @@ -24,6 +25,19 @@ namespace facebook::react { namespace { const int MAX_COMMIT_ATTEMPTS_BEFORE_LOCKING = 3; + +std::string getShadowTreeCommitSourceName(ShadowTreeCommitSource source) { + switch (source) { + case ShadowTreeCommitSource::Unknown: + return "Unknown"; + case ShadowTreeCommitSource::React: + return "React"; + case ShadowTreeCommitSource::AnimationEndSync: + return "AnimationEndSync"; + case ShadowTreeCommitSource::ReactRevisionMerge: + return "ReactRevisionMerge"; + } +} } // namespace using CommitStatus = ShadowTree::CommitStatus; @@ -316,6 +330,13 @@ CommitStatus ShadowTree::tryCommit( const ShadowTreeCommitTransaction& transaction, const CommitOptions& commitOptions) const { TraceSection s("ShadowTree::commit"); + jsinspector_modern::tracing::PerformanceTracerSection s1( + "commit", + "Renderer", + "\u269b Native", + nullptr, + "source", + getShadowTreeCommitSourceName(commitOptions.source)); auto isReactBranch = ReactNativeFeatureFlags::enableFabricCommitBranching() && commitOptions.source == CommitSource::React; @@ -382,7 +403,11 @@ CommitStatus ShadowTree::tryCommit( telemetry.willLayout(); telemetry.setAsThreadLocal(); - newRootShadowNode->layoutIfNeeded(&affectedLayoutableNodes); + { + jsinspector_modern::tracing::PerformanceTracerSection s2( + "layout", "Renderer", "\u269b Native"); + newRootShadowNode->layoutIfNeeded(&affectedLayoutableNodes); + } telemetry.unsetAsThreadLocal(); telemetry.didLayout(static_cast(affectedLayoutableNodes.size()));