From 51267c4ac9de8bf190505fbb7670ed99aae325f7 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 17 Jul 2020 11:24:26 -0400 Subject: [PATCH] Sync scheduling profiler marks and debug tracing to new reconciler fork (#19375, #19376, #19396) * Make enableSchedulingProfiler flag static * Copied debug tracing and scheduler profiling to .new fork and updated feature flags * Move profiler component stacks behind a feature flag --- .../src/ReactFiberClassComponent.new.js | 48 +++++- .../src/ReactFiberHooks.new.js | 23 ++- .../src/ReactFiberReconciler.new.js | 6 + .../src/ReactFiberThrow.new.js | 21 ++- .../src/ReactFiberWorkLoop.new.js | 146 ++++++++++++++++++ .../src/ReactFiberWorkLoop.old.js | 2 +- .../src/SchedulingProfiler.js | 49 +++--- .../SchedulingProfiler-test.internal.js | 105 ++++++++++--- packages/shared/ReactFeatureFlags.js | 3 +- .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.testing.js | 1 + .../forks/ReactFeatureFlags.testing.www.js | 1 + .../forks/ReactFeatureFlags.www-dynamic.js | 14 +- .../shared/forks/ReactFeatureFlags.www.js | 5 +- 17 files changed, 382 insertions(+), 46 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.new.js b/packages/react-reconciler/src/ReactFiberClassComponent.new.js index 0024e89291..5886d3f4cb 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.new.js @@ -16,6 +16,8 @@ import {Update, Snapshot} from './ReactSideEffectTags'; import { debugRenderPhaseSideEffectsForStrictMode, disableLegacyContext, + enableDebugTracing, + enableSchedulingProfiler, warnAboutDeprecatedLifecycles, } from 'shared/ReactFeatureFlags'; import ReactStrictModeWarnings from './ReactStrictModeWarnings.new'; @@ -27,7 +29,7 @@ import invariant from 'shared/invariant'; import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols'; import {resolveDefaultProps} from './ReactFiberLazyComponent.new'; -import {StrictMode} from './ReactTypeOfMode'; +import {DebugTracingMode, StrictMode} from './ReactTypeOfMode'; import { enqueueUpdate, @@ -55,8 +57,13 @@ import { scheduleUpdateOnFiber, } from './ReactFiberWorkLoop.new'; import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig'; +import {logForceUpdateScheduled, logStateUpdateScheduled} from './DebugTracing'; import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev'; +import { + markForceUpdateScheduled, + markStateUpdateScheduled, +} from './SchedulingProfiler'; const fakeInternalInstance = {}; const isArray = Array.isArray; @@ -203,6 +210,19 @@ const classComponentUpdater = { enqueueUpdate(fiber, update); scheduleUpdateOnFiber(fiber, lane, eventTime); + + if (__DEV__) { + if (enableDebugTracing) { + if (fiber.mode & DebugTracingMode) { + const name = getComponentName(fiber.type) || 'Unknown'; + logStateUpdateScheduled(name, lane, payload); + } + } + } + + if (enableSchedulingProfiler) { + markStateUpdateScheduled(fiber, lane); + } }, enqueueReplaceState(inst, payload, callback) { const fiber = getInstance(inst); @@ -223,6 +243,19 @@ const classComponentUpdater = { enqueueUpdate(fiber, update); scheduleUpdateOnFiber(fiber, lane, eventTime); + + if (__DEV__) { + if (enableDebugTracing) { + if (fiber.mode & DebugTracingMode) { + const name = getComponentName(fiber.type) || 'Unknown'; + logStateUpdateScheduled(name, lane, payload); + } + } + } + + if (enableSchedulingProfiler) { + markStateUpdateScheduled(fiber, lane); + } }, enqueueForceUpdate(inst, callback) { const fiber = getInstance(inst); @@ -242,6 +275,19 @@ const classComponentUpdater = { enqueueUpdate(fiber, update); scheduleUpdateOnFiber(fiber, lane, eventTime); + + if (__DEV__) { + if (enableDebugTracing) { + if (fiber.mode & DebugTracingMode) { + const name = getComponentName(fiber.type) || 'Unknown'; + logForceUpdateScheduled(name, lane); + } + } + } + + if (enableSchedulingProfiler) { + markForceUpdateScheduled(fiber, lane); + } }, }; diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 90afafc7a7..c5f766759e 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -24,9 +24,13 @@ import type {FiberRoot} from './ReactInternalTypes'; import type {OpaqueIDType} from './ReactFiberHostConfig'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import {enableNewReconciler} from 'shared/ReactFeatureFlags'; +import { + enableDebugTracing, + enableSchedulingProfiler, + enableNewReconciler, +} from 'shared/ReactFeatureFlags'; -import {NoMode, BlockingMode} from './ReactTypeOfMode'; +import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode'; import { NoLane, NoLanes, @@ -88,6 +92,8 @@ import { warnAboutMultipleRenderersDEV, } from './ReactMutableSource.new'; import {getIsRendering} from './ReactCurrentFiber'; +import {logStateUpdateScheduled} from './DebugTracing'; +import {markStateUpdateScheduled} from './SchedulingProfiler'; const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals; @@ -1751,6 +1757,19 @@ function dispatchAction( } scheduleUpdateOnFiber(fiber, lane, eventTime); } + + if (__DEV__) { + if (enableDebugTracing) { + if (fiber.mode & DebugTracingMode) { + const name = getComponentName(fiber.type) || 'Unknown'; + logStateUpdateScheduled(name, lane, action); + } + } + } + + if (enableSchedulingProfiler) { + markStateUpdateScheduled(fiber, lane); + } } export const ContextOnlyDispatcher: Dispatcher = { diff --git a/packages/react-reconciler/src/ReactFiberReconciler.new.js b/packages/react-reconciler/src/ReactFiberReconciler.new.js index 6b1b1c8676..6383603026 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.new.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.new.js @@ -39,6 +39,7 @@ import { } from './ReactWorkTags'; import getComponentName from 'shared/getComponentName'; import invariant from 'shared/invariant'; +import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import {getPublicInstance} from './ReactFiberHostConfig'; import { @@ -95,6 +96,7 @@ import { setRefreshHandler, findHostInstancesForRefresh, } from './ReactFiberHotReloading.new'; +import {markRenderScheduled} from './SchedulingProfiler'; export {registerMutableSourceForHydration} from './ReactMutableSource.new'; export {createPortal} from './ReactPortal'; @@ -273,6 +275,10 @@ export function updateContainer( const suspenseConfig = requestCurrentSuspenseConfig(); const lane = requestUpdateLane(current, suspenseConfig); + if (enableSchedulingProfiler) { + markRenderScheduled(lane); + } + const context = getContextForSubtree(parentComponent); if (container.context === null) { container.context = context; diff --git a/packages/react-reconciler/src/ReactFiberThrow.new.js b/packages/react-reconciler/src/ReactFiberThrow.new.js index 44a0165e2a..d8cacf6569 100644 --- a/packages/react-reconciler/src/ReactFiberThrow.new.js +++ b/packages/react-reconciler/src/ReactFiberThrow.new.js @@ -31,7 +31,11 @@ import { ForceUpdateForLegacySuspense, } from './ReactSideEffectTags'; import {shouldCaptureSuspense} from './ReactFiberSuspenseComponent.new'; -import {NoMode, BlockingMode} from './ReactTypeOfMode'; +import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode'; +import { + enableDebugTracing, + enableSchedulingProfiler, +} from 'shared/ReactFeatureFlags'; import {createCapturedValue} from './ReactCapturedValue'; import { enqueueCapturedUpdate, @@ -54,6 +58,8 @@ import { pingSuspendedRoot, } from './ReactFiberWorkLoop.new'; import {logCapturedError} from './ReactFiberErrorLogger'; +import {logComponentSuspended} from './DebugTracing'; +import {markComponentSuspended} from './SchedulingProfiler'; import { SyncLane, @@ -190,6 +196,19 @@ function throwException( // This is a wakeable. const wakeable: Wakeable = (value: any); + if (__DEV__) { + if (enableDebugTracing) { + if (sourceFiber.mode & DebugTracingMode) { + const name = getComponentName(sourceFiber.type) || 'Unknown'; + logComponentSuspended(name, wakeable); + } + } + } + + if (enableSchedulingProfiler) { + markComponentSuspended(sourceFiber, wakeable); + } + if ((sourceFiber.mode & BlockingMode) === NoMode) { // Reset the memoizedState to what it was before we attempted // to render it. diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 92772b3bf2..535100dff2 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -27,6 +27,8 @@ import { warnAboutUnmockedScheduler, deferRenderPhaseUpdateToNextBatch, decoupleUpdatePriorityFromScheduler, + enableDebugTracing, + enableSchedulingProfiler, enableScopeAPI, } from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; @@ -47,6 +49,27 @@ import { flushSyncCallbackQueue, scheduleSyncCallback, } from './SchedulerWithReactIntegration.new'; +import { + logCommitStarted, + logCommitStopped, + logLayoutEffectsStarted, + logLayoutEffectsStopped, + logPassiveEffectsStarted, + logPassiveEffectsStopped, + logRenderStarted, + logRenderStopped, +} from './DebugTracing'; +import { + markCommitStarted, + markCommitStopped, + markLayoutEffectsStarted, + markLayoutEffectsStopped, + markPassiveEffectsStarted, + markPassiveEffectsStopped, + markRenderStarted, + markRenderYielded, + markRenderStopped, +} from './SchedulingProfiler'; // The scheduler is imported here *only* to detect whether it's been mocked import * as Scheduler from 'scheduler'; @@ -1516,6 +1539,16 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) { const prevInteractions = pushInteractions(root); + if (__DEV__) { + if (enableDebugTracing) { + logRenderStarted(lanes); + } + } + + if (enableSchedulingProfiler) { + markRenderStarted(lanes); + } + do { try { workLoopSync(); @@ -1541,6 +1574,16 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) { ); } + if (__DEV__) { + if (enableDebugTracing) { + logRenderStopped(); + } + } + + if (enableSchedulingProfiler) { + markRenderStopped(); + } + // Set this to null to indicate there's no in-progress render. workInProgressRoot = null; workInProgressRootRenderLanes = NoLanes; @@ -1571,6 +1614,16 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) { const prevInteractions = pushInteractions(root); + if (__DEV__) { + if (enableDebugTracing) { + logRenderStarted(lanes); + } + } + + if (enableSchedulingProfiler) { + markRenderStarted(lanes); + } + do { try { workLoopConcurrent(); @@ -1587,12 +1640,25 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) { popDispatcher(prevDispatcher); executionContext = prevExecutionContext; + if (__DEV__) { + if (enableDebugTracing) { + logRenderStopped(); + } + } + // Check if the tree has completed. if (workInProgress !== null) { // Still work remaining. + if (enableSchedulingProfiler) { + markRenderYielded(); + } return RootIncomplete; } else { // Completed the tree. + if (enableSchedulingProfiler) { + markRenderStopped(); + } + // Set this to null to indicate there's no in-progress render. workInProgressRoot = null; workInProgressRootRenderLanes = NoLanes; @@ -1950,7 +2016,28 @@ function commitRootImpl(root, renderPriorityLevel) { const finishedWork = root.finishedWork; const lanes = root.finishedLanes; + + if (__DEV__) { + if (enableDebugTracing) { + logCommitStarted(lanes); + } + } + + if (enableSchedulingProfiler) { + markCommitStarted(lanes); + } + if (finishedWork === null) { + if (__DEV__) { + if (enableDebugTracing) { + logCommitStopped(); + } + } + + if (enableSchedulingProfiler) { + markCommitStopped(); + } + return null; } root.finishedWork = null; @@ -2062,8 +2149,27 @@ function commitRootImpl(root, renderPriorityLevel) { // The next phase is the layout phase, where we call effects that read // the host tree after it's been mutated. The idiomatic use case for this is // layout, but class component lifecycles also fire here for legacy reasons. + + if (__DEV__) { + if (enableDebugTracing) { + logLayoutEffectsStarted(lanes); + } + } + if (enableSchedulingProfiler) { + markLayoutEffectsStarted(lanes); + } + commitLayoutEffects(finishedWork, root, lanes); + if (__DEV__) { + if (enableDebugTracing) { + logLayoutEffectsStopped(); + } + } + if (enableSchedulingProfiler) { + markLayoutEffectsStopped(); + } + // Tell Scheduler to yield at the end of the frame, so the browser has an // opportunity to paint. requestPaint(); @@ -2165,6 +2271,16 @@ function commitRootImpl(root, renderPriorityLevel) { } if ((executionContext & LegacyUnbatchedContext) !== NoContext) { + if (__DEV__) { + if (enableDebugTracing) { + logCommitStopped(); + } + } + + if (enableSchedulingProfiler) { + markCommitStopped(); + } + // This is a legacy edge case. We just committed the initial mount of // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired // synchronously, but layout updates should be deferred until the end @@ -2175,6 +2291,16 @@ function commitRootImpl(root, renderPriorityLevel) { // If layout work was scheduled, flush it now. flushSyncCallbackQueue(); + if (__DEV__) { + if (enableDebugTracing) { + logCommitStopped(); + } + } + + if (enableSchedulingProfiler) { + markCommitStopped(); + } + return null; } @@ -2572,6 +2698,16 @@ function flushPassiveEffectsImpl() { 'Cannot flush passive effects while already rendering.', ); + if (__DEV__) { + if (enableDebugTracing) { + logPassiveEffectsStarted(lanes); + } + } + + if (enableSchedulingProfiler) { + markPassiveEffectsStarted(lanes); + } + if (__DEV__) { isFlushingPassiveEffects = true; } @@ -2716,6 +2852,16 @@ function flushPassiveEffectsImpl() { isFlushingPassiveEffects = false; } + if (__DEV__) { + if (enableDebugTracing) { + logPassiveEffectsStopped(); + } + } + + if (enableSchedulingProfiler) { + markPassiveEffectsStopped(); + } + executionContext = prevExecutionContext; flushSyncCallbackQueue(); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 303d1cc0fb..42811a73b1 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -1361,7 +1361,7 @@ function handleError(root, thrownValue): void { // sibling, or the parent if there are no siblings. But since the root // has no siblings nor a parent, we set it to null. Usually this is // handled by `completeUnitOfWork` or `unwindWork`, but since we're - // interntionally not calling those, we need set it here. + // intentionally not calling those, we need set it here. // TODO: Consider calling `unwindWork` to pop the contexts. workInProgress = null; return; diff --git a/packages/react-reconciler/src/SchedulingProfiler.js b/packages/react-reconciler/src/SchedulingProfiler.js index 5b7673c628..c6032014a1 100644 --- a/packages/react-reconciler/src/SchedulingProfiler.js +++ b/packages/react-reconciler/src/SchedulingProfiler.js @@ -11,7 +11,10 @@ import type {Lane, Lanes} from './ReactFiberLane'; import type {Fiber} from './ReactInternalTypes'; import type {Wakeable} from 'shared/ReactTypes'; -import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags'; +import { + enableSchedulingProfiler, + enableSchedulingProfilerComponentStacks, +} from 'shared/ReactFeatureFlags'; import getComponentName from 'shared/getComponentName'; import {getStackByFiberInDevAndProd} from './ReactFiberComponentStack'; @@ -54,21 +57,31 @@ function getWakeableID(wakeable: Wakeable): number { return ((wakeableIDs.get(wakeable): any): number); } -// $FlowFixMe: Flow cannot handle polymorphic WeakMaps -const cachedFiberStacks: WeakMap = new PossiblyWeakMap(); -function cacheFirstGetComponentStackByFiber(fiber: Fiber): string { - if (cachedFiberStacks.has(fiber)) { - return ((cachedFiberStacks.get(fiber): any): string); - } else { - const alternate = fiber.alternate; - if (alternate !== null && cachedFiberStacks.has(alternate)) { - return ((cachedFiberStacks.get(alternate): any): string); +let getComponentStackByFiber = function getComponentStackByFiberDisabled( + fiber: Fiber, +): string { + return ''; +}; + +if (enableSchedulingProfilerComponentStacks) { + // $FlowFixMe: Flow cannot handle polymorphic WeakMaps + const cachedFiberStacks: WeakMap = new PossiblyWeakMap(); + getComponentStackByFiber = function cacheFirstGetComponentStackByFiber( + fiber: Fiber, + ): string { + if (cachedFiberStacks.has(fiber)) { + return ((cachedFiberStacks.get(fiber): any): string); + } else { + const alternate = fiber.alternate; + if (alternate !== null && cachedFiberStacks.has(alternate)) { + return ((cachedFiberStacks.get(alternate): any): string); + } } - } - // TODO (brian) Generate and store temporary ID so DevTools can match up a component stack later. - const componentStack = getStackByFiberInDevAndProd(fiber) || ''; - cachedFiberStacks.set(fiber, componentStack); - return componentStack; + // TODO (brian) Generate and store temporary ID so DevTools can match up a component stack later. + const componentStack = getStackByFiberInDevAndProd(fiber) || ''; + cachedFiberStacks.set(fiber, componentStack); + return componentStack; + }; } export function markComponentSuspended(fiber: Fiber, wakeable: Wakeable): void { @@ -76,7 +89,7 @@ export function markComponentSuspended(fiber: Fiber, wakeable: Wakeable): void { if (supportsUserTiming) { const id = getWakeableID(wakeable); const componentName = getComponentName(fiber.type) || 'Unknown'; - const componentStack = cacheFirstGetComponentStackByFiber(fiber); + const componentStack = getComponentStackByFiber(fiber); performance.mark( `--suspense-suspend-${id}-${componentName}-${componentStack}`, ); @@ -162,7 +175,7 @@ export function markForceUpdateScheduled(fiber: Fiber, lane: Lane): void { if (enableSchedulingProfiler) { if (supportsUserTiming) { const componentName = getComponentName(fiber.type) || 'Unknown'; - const componentStack = cacheFirstGetComponentStackByFiber(fiber); + const componentStack = getComponentStackByFiber(fiber); performance.mark( `--schedule-forced-update-${formatLanes( lane, @@ -176,7 +189,7 @@ export function markStateUpdateScheduled(fiber: Fiber, lane: Lane): void { if (enableSchedulingProfiler) { if (supportsUserTiming) { const componentName = getComponentName(fiber.type) || 'Unknown'; - const componentStack = cacheFirstGetComponentStackByFiber(fiber); + const componentStack = getComponentStackByFiber(fiber); performance.mark( `--schedule-state-update-${formatLanes( lane, diff --git a/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js b/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js index b6d19a18ba..8086079b56 100644 --- a/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js +++ b/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js @@ -19,6 +19,20 @@ function normalizeCodeLocInfo(str) { ); } +// TODO (enableSchedulingProfilerComponentStacks) Clean this up once the feature flag has been removed. +function toggleComponentStacks(mark) { + let expectedMark = mark; + gate(({enableSchedulingProfilerComponentStacks}) => { + if (!enableSchedulingProfilerComponentStacks) { + const index = mark.indexOf('\n '); + if (index >= 0) { + expectedMark = mark.substr(0, index); + } + } + }); + return expectedMark; +} + describe('SchedulingProfiler', () => { let React; let ReactTestRenderer; @@ -136,7 +150,9 @@ describe('SchedulingProfiler', () => { expect(marks).toEqual([ '--schedule-render-1', '--render-start-1', - '--suspense-suspend-0-Example-\n at Example\n at Suspense', + toggleComponentStacks( + '--suspense-suspend-0-Example-\n at Example\n at Suspense', + ), '--render-stop', '--commit-start-1', '--layout-effects-start-1', @@ -148,7 +164,9 @@ describe('SchedulingProfiler', () => { await fakeSuspensePromise; expect(marks).toEqual([ - '--suspense-resolved-0-Example-\n at Example\n at Suspense', + toggleComponentStacks( + '--suspense-resolved-0-Example-\n at Example\n at Suspense', + ), ]); }); @@ -168,7 +186,9 @@ describe('SchedulingProfiler', () => { expect(marks).toEqual([ '--schedule-render-1', '--render-start-1', - '--suspense-suspend-0-Example-\n at Example\n at Suspense', + toggleComponentStacks( + '--suspense-suspend-0-Example-\n at Example\n at Suspense', + ), '--render-stop', '--commit-start-1', '--layout-effects-start-1', @@ -180,7 +200,9 @@ describe('SchedulingProfiler', () => { await expect(fakeSuspensePromise).rejects.toThrow(); expect(marks).toEqual([ - '--suspense-rejected-0-Example-\n at Example\n at Suspense', + toggleComponentStacks( + '--suspense-rejected-0-Example-\n at Example\n at Suspense', + ), ]); }); @@ -206,7 +228,9 @@ describe('SchedulingProfiler', () => { expect(marks).toEqual([ '--render-start-512', - '--suspense-suspend-0-Example-\n at Example\n at Suspense', + toggleComponentStacks( + '--suspense-suspend-0-Example-\n at Example\n at Suspense', + ), '--render-stop', '--commit-start-512', '--layout-effects-start-512', @@ -218,7 +242,9 @@ describe('SchedulingProfiler', () => { await fakeSuspensePromise; expect(marks).toEqual([ - '--suspense-resolved-0-Example-\n at Example\n at Suspense', + toggleComponentStacks( + '--suspense-resolved-0-Example-\n at Example\n at Suspense', + ), ]); }); @@ -244,7 +270,9 @@ describe('SchedulingProfiler', () => { expect(marks).toEqual([ '--render-start-512', - '--suspense-suspend-0-Example-\n at Example\n at Suspense', + toggleComponentStacks( + '--suspense-suspend-0-Example-\n at Example\n at Suspense', + ), '--render-stop', '--commit-start-512', '--layout-effects-start-512', @@ -256,7 +284,9 @@ describe('SchedulingProfiler', () => { await expect(fakeSuspensePromise).rejects.toThrow(); expect(marks).toEqual([ - '--suspense-rejected-0-Example-\n at Example\n at Suspense', + toggleComponentStacks( + '--suspense-rejected-0-Example-\n at Example\n at Suspense', + ), ]); }); @@ -285,7 +315,9 @@ describe('SchedulingProfiler', () => { '--render-stop', '--commit-start-512', '--layout-effects-start-512', - '--schedule-state-update-1-Example-\n in Example (at **)', + toggleComponentStacks( + '--schedule-state-update-1-Example-\n in Example (at **)', + ), '--layout-effects-stop', '--render-start-1', '--render-stop', @@ -319,7 +351,9 @@ describe('SchedulingProfiler', () => { '--render-stop', '--commit-start-512', '--layout-effects-start-512', - '--schedule-forced-update-1-Example-\n in Example (at **)', + toggleComponentStacks( + '--schedule-forced-update-1-Example-\n in Example (at **)', + ), '--layout-effects-stop', '--render-start-1', '--render-stop', @@ -351,8 +385,18 @@ describe('SchedulingProfiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); }).toErrorDev('Cannot update during an existing state transition'); - expect(marks.map(normalizeCodeLocInfo)).toContain( - '--schedule-state-update-1024-Example-\n in Example (at **)', + gate(({old}) => + old + ? expect(marks.map(normalizeCodeLocInfo)).toContain( + toggleComponentStacks( + '--schedule-state-update-1024-Example-\n in Example (at **)', + ), + ) + : expect(marks.map(normalizeCodeLocInfo)).toContain( + toggleComponentStacks( + '--schedule-state-update-512-Example-\n in Example (at **)', + ), + ), ); }); @@ -378,8 +422,18 @@ describe('SchedulingProfiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); }).toErrorDev('Cannot update during an existing state transition'); - expect(marks.map(normalizeCodeLocInfo)).toContain( - '--schedule-forced-update-1024-Example-\n in Example (at **)', + gate(({old}) => + old + ? expect(marks.map(normalizeCodeLocInfo)).toContain( + toggleComponentStacks( + '--schedule-forced-update-1024-Example-\n in Example (at **)', + ), + ) + : expect(marks.map(normalizeCodeLocInfo)).toContain( + toggleComponentStacks( + '--schedule-forced-update-512-Example-\n in Example (at **)', + ), + ), ); }); @@ -406,7 +460,9 @@ describe('SchedulingProfiler', () => { '--render-stop', '--commit-start-512', '--layout-effects-start-512', - '--schedule-state-update-1-Example-\n in Example (at **)', + toggleComponentStacks( + '--schedule-state-update-1-Example-\n in Example (at **)', + ), '--layout-effects-stop', '--render-start-1', '--render-stop', @@ -429,6 +485,7 @@ describe('SchedulingProfiler', () => { ReactTestRenderer.act(() => { ReactTestRenderer.create(, {unstable_isConcurrent: true}); }); + expect(marks.map(normalizeCodeLocInfo)).toEqual([ '--schedule-render-512', '--render-start-512', @@ -438,7 +495,9 @@ describe('SchedulingProfiler', () => { '--layout-effects-stop', '--commit-stop', '--passive-effects-start-512', - '--schedule-state-update-1024-Example-\n in Example (at **)', + toggleComponentStacks( + '--schedule-state-update-1024-Example-\n in Example (at **)', + ), '--passive-effects-stop', '--render-start-1024', '--render-stop', @@ -461,8 +520,18 @@ describe('SchedulingProfiler', () => { ReactTestRenderer.create(, {unstable_isConcurrent: true}); }); - expect(marks.map(normalizeCodeLocInfo)).toContain( - '--schedule-state-update-1024-Example-\n in Example (at **)', + gate(({old}) => + old + ? expect(marks.map(normalizeCodeLocInfo)).toContain( + toggleComponentStacks( + '--schedule-state-update-1024-Example-\n in Example (at **)', + ), + ) + : expect(marks.map(normalizeCodeLocInfo)).toContain( + toggleComponentStacks( + '--schedule-state-update-512-Example-\n in Example (at **)', + ), + ), ); }); }); diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 0837686bf1..e0c38e9baa 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -17,7 +17,8 @@ export const enableDebugTracing = false; // Adds user timing marks for e.g. state updates, suspense, and work loop stuff, // for an experimental scheduling profiler tool. -export const enableSchedulingProfiler = false; +export const enableSchedulingProfiler = __PROFILE__ && __EXPERIMENTAL__; +export const enableSchedulingProfilerComponentStacks = false; // Helps identify side effects in render-phase lifecycle hooks and setState // reducers by double invoking them in Strict Mode. diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 1ac12703a2..1418024513 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.native-fb'; // The rest of the flags are static for better dead code elimination. export const enableDebugTracing = false; export const enableSchedulingProfiler = false; +export const enableSchedulingProfilerComponentStacks = false; export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = false; export const enableSchedulerTracing = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 1e41269fc8..e1f4100fda 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.native-oss'; export const debugRenderPhaseSideEffectsForStrictMode = false; export const enableDebugTracing = false; export const enableSchedulingProfiler = false; +export const enableSchedulingProfilerComponentStacks = false; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; export const warnAboutDeprecatedLifecycles = true; export const enableProfilerTimer = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 0425f62fc0..2c94f623ef 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.test-renderer'; export const debugRenderPhaseSideEffectsForStrictMode = false; export const enableDebugTracing = false; export const enableSchedulingProfiler = false; +export const enableSchedulingProfilerComponentStacks = false; export const warnAboutDeprecatedLifecycles = true; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 2e2f0aab0d..eeb6866fc9 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.test-renderer.www'; export const debugRenderPhaseSideEffectsForStrictMode = false; export const enableDebugTracing = false; export const enableSchedulingProfiler = false; +export const enableSchedulingProfilerComponentStacks = false; export const warnAboutDeprecatedLifecycles = true; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index b60f732c38..56e3f0284d 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.testing'; export const debugRenderPhaseSideEffectsForStrictMode = false; export const enableDebugTracing = false; export const enableSchedulingProfiler = false; +export const enableSchedulingProfilerComponentStacks = false; export const warnAboutDeprecatedLifecycles = true; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index 72cb1a10e1..414f5db9b0 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.testing.www'; export const debugRenderPhaseSideEffectsForStrictMode = false; export const enableDebugTracing = false; export const enableSchedulingProfiler = false; +export const enableSchedulingProfilerComponentStacks = false; export const warnAboutDeprecatedLifecycles = true; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index 84fafd6e14..201a40fdbf 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -19,9 +19,17 @@ export const enableFilterEmptyStringAttributesDOM = __VARIANT__; export const enableLegacyFBSupport = __VARIANT__; export const decoupleUpdatePriorityFromScheduler = __VARIANT__; -// TODO: These features do not currently exist in the new reconciler fork. -export const enableDebugTracing = !__VARIANT__; -export const enableSchedulingProfiler = !__VARIANT__ && __PROFILE__; +// Enable this flag to help with concurrent mode debugging. +// It logs information to the console about React scheduling, rendering, and commit phases. +// +// NOTE: This feature will only work in DEV mode; all callsights are wrapped with __DEV__. +export const enableDebugTracing = false; + +// TODO: getStackByFiberInDevAndProd() causes errors when synced to www. +// This flag can be used to disable component stacks for the profiler marks, +// so that the feature can be synced for others, +// while still enabling investigation into the underlying source of the errors. +export const enableSchedulingProfilerComponentStacks = false; // This only has an effect in the new reconciler. But also, the new reconciler // is only enabled when __VARIANT__ is true. So this is set to the opposite of diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index f8d399b9cf..6f0d031aff 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -26,7 +26,7 @@ export const { deferRenderPhaseUpdateToNextBatch, decoupleUpdatePriorityFromScheduler, enableDebugTracing, - enableSchedulingProfiler, + enableSchedulingProfilerComponentStacks, } = dynamicFeatureFlags; // On WWW, __EXPERIMENTAL__ is used for a new modern build. @@ -35,6 +35,9 @@ export const { export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = __PROFILE__; +// Logs additional User Timing API marks for use with an experimental profiling tool. +export const enableSchedulingProfiler = __PROFILE__; + // Note: we'll want to remove this when we to userland implementation. // For now, we'll turn it on for everyone because it's *already* on for everyone in practice. // At least this will let us stop shipping implementation to all users.