Add user timing marks for scheduling profiler tool (#19223)
High level breakdown of this commit: * Add a enableSchedulingProfiling feature flag. * Add functions that call User Timing APIs to a new SchedulingProfiler file. The file follows DebugTracing's structure. * Add user timing marks to places where DebugTracing logs. * Add user timing marks to most other places where @bvaughn's original draft DebugTracing branch marks. * Tests added * More context (and discussions with @bvaughn) available at our internal PR MLH-Fellowship#11 and issue MLH-Fellowship#5. Similar to DebugTracing, we've only added scheduling profiling calls to the old reconciler fork. Co-authored-by: Kartik Choudhary <kartik.c918@gmail.com> Co-authored-by: Kartik Choudhary <kartikc.918@gmail.com> Co-authored-by: Brian Vaughn <brian.david.vaughn@gmail.com>
This commit is contained in:
parent
b85b47630b
commit
40cddfeeb1
|
@ -17,6 +17,7 @@ import {
|
||||||
debugRenderPhaseSideEffectsForStrictMode,
|
debugRenderPhaseSideEffectsForStrictMode,
|
||||||
disableLegacyContext,
|
disableLegacyContext,
|
||||||
enableDebugTracing,
|
enableDebugTracing,
|
||||||
|
enableSchedulingProfiler,
|
||||||
warnAboutDeprecatedLifecycles,
|
warnAboutDeprecatedLifecycles,
|
||||||
} from 'shared/ReactFeatureFlags';
|
} from 'shared/ReactFeatureFlags';
|
||||||
import ReactStrictModeWarnings from './ReactStrictModeWarnings.old';
|
import ReactStrictModeWarnings from './ReactStrictModeWarnings.old';
|
||||||
|
@ -59,6 +60,10 @@ import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';
|
||||||
import {logForceUpdateScheduled, logStateUpdateScheduled} from './DebugTracing';
|
import {logForceUpdateScheduled, logStateUpdateScheduled} from './DebugTracing';
|
||||||
|
|
||||||
import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
|
import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
|
||||||
|
import {
|
||||||
|
markForceUpdateScheduled,
|
||||||
|
markStateUpdateScheduled,
|
||||||
|
} from './SchedulingProfiler';
|
||||||
|
|
||||||
const fakeInternalInstance = {};
|
const fakeInternalInstance = {};
|
||||||
const isArray = Array.isArray;
|
const isArray = Array.isArray;
|
||||||
|
@ -214,6 +219,10 @@ const classComponentUpdater = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markStateUpdateScheduled(fiber, lane);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
enqueueReplaceState(inst, payload, callback) {
|
enqueueReplaceState(inst, payload, callback) {
|
||||||
const fiber = getInstance(inst);
|
const fiber = getInstance(inst);
|
||||||
|
@ -243,6 +252,10 @@ const classComponentUpdater = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markStateUpdateScheduled(fiber, lane);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
enqueueForceUpdate(inst, callback) {
|
enqueueForceUpdate(inst, callback) {
|
||||||
const fiber = getInstance(inst);
|
const fiber = getInstance(inst);
|
||||||
|
@ -271,6 +284,10 @@ const classComponentUpdater = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markForceUpdateScheduled(fiber, lane);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import type {OpaqueIDType} from './ReactFiberHostConfig';
|
||||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||||
import {
|
import {
|
||||||
enableDebugTracing,
|
enableDebugTracing,
|
||||||
|
enableSchedulingProfiler,
|
||||||
enableNewReconciler,
|
enableNewReconciler,
|
||||||
} from 'shared/ReactFeatureFlags';
|
} from 'shared/ReactFeatureFlags';
|
||||||
|
|
||||||
|
@ -92,6 +93,7 @@ import {
|
||||||
} from './ReactMutableSource.old';
|
} from './ReactMutableSource.old';
|
||||||
import {getIsRendering} from './ReactCurrentFiber';
|
import {getIsRendering} from './ReactCurrentFiber';
|
||||||
import {logStateUpdateScheduled} from './DebugTracing';
|
import {logStateUpdateScheduled} from './DebugTracing';
|
||||||
|
import {markStateUpdateScheduled} from './SchedulingProfiler';
|
||||||
|
|
||||||
const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
|
const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
|
||||||
|
|
||||||
|
@ -1764,6 +1766,10 @@ function dispatchAction<S, A>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markStateUpdateScheduled(fiber, lane);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ContextOnlyDispatcher: Dispatcher = {
|
export const ContextOnlyDispatcher: Dispatcher = {
|
||||||
|
|
|
@ -39,6 +39,7 @@ import {
|
||||||
} from './ReactWorkTags';
|
} from './ReactWorkTags';
|
||||||
import getComponentName from 'shared/getComponentName';
|
import getComponentName from 'shared/getComponentName';
|
||||||
import invariant from 'shared/invariant';
|
import invariant from 'shared/invariant';
|
||||||
|
import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
|
||||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||||
import {getPublicInstance} from './ReactFiberHostConfig';
|
import {getPublicInstance} from './ReactFiberHostConfig';
|
||||||
import {
|
import {
|
||||||
|
@ -95,6 +96,7 @@ import {
|
||||||
setRefreshHandler,
|
setRefreshHandler,
|
||||||
findHostInstancesForRefresh,
|
findHostInstancesForRefresh,
|
||||||
} from './ReactFiberHotReloading.old';
|
} from './ReactFiberHotReloading.old';
|
||||||
|
import {markRenderScheduled} from './SchedulingProfiler';
|
||||||
|
|
||||||
export {registerMutableSourceForHydration} from './ReactMutableSource.new';
|
export {registerMutableSourceForHydration} from './ReactMutableSource.new';
|
||||||
export {createPortal} from './ReactPortal';
|
export {createPortal} from './ReactPortal';
|
||||||
|
@ -273,6 +275,10 @@ export function updateContainer(
|
||||||
const suspenseConfig = requestCurrentSuspenseConfig();
|
const suspenseConfig = requestCurrentSuspenseConfig();
|
||||||
const lane = requestUpdateLane(current, suspenseConfig);
|
const lane = requestUpdateLane(current, suspenseConfig);
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markRenderScheduled(lane);
|
||||||
|
}
|
||||||
|
|
||||||
const context = getContextForSubtree(parentComponent);
|
const context = getContextForSubtree(parentComponent);
|
||||||
if (container.context === null) {
|
if (container.context === null) {
|
||||||
container.context = context;
|
container.context = context;
|
||||||
|
|
|
@ -32,7 +32,10 @@ import {
|
||||||
} from './ReactSideEffectTags';
|
} from './ReactSideEffectTags';
|
||||||
import {shouldCaptureSuspense} from './ReactFiberSuspenseComponent.old';
|
import {shouldCaptureSuspense} from './ReactFiberSuspenseComponent.old';
|
||||||
import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
|
import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
|
||||||
import {enableDebugTracing} from 'shared/ReactFeatureFlags';
|
import {
|
||||||
|
enableDebugTracing,
|
||||||
|
enableSchedulingProfiler,
|
||||||
|
} from 'shared/ReactFeatureFlags';
|
||||||
import {createCapturedValue} from './ReactCapturedValue';
|
import {createCapturedValue} from './ReactCapturedValue';
|
||||||
import {
|
import {
|
||||||
enqueueCapturedUpdate,
|
enqueueCapturedUpdate,
|
||||||
|
@ -56,6 +59,7 @@ import {
|
||||||
} from './ReactFiberWorkLoop.old';
|
} from './ReactFiberWorkLoop.old';
|
||||||
import {logCapturedError} from './ReactFiberErrorLogger';
|
import {logCapturedError} from './ReactFiberErrorLogger';
|
||||||
import {logComponentSuspended} from './DebugTracing';
|
import {logComponentSuspended} from './DebugTracing';
|
||||||
|
import {markComponentSuspended} from './SchedulingProfiler';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SyncLane,
|
SyncLane,
|
||||||
|
@ -201,6 +205,10 @@ function throwException(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markComponentSuspended(sourceFiber, wakeable);
|
||||||
|
}
|
||||||
|
|
||||||
if ((sourceFiber.mode & BlockingMode) === NoMode) {
|
if ((sourceFiber.mode & BlockingMode) === NoMode) {
|
||||||
// Reset the memoizedState to what it was before we attempted
|
// Reset the memoizedState to what it was before we attempted
|
||||||
// to render it.
|
// to render it.
|
||||||
|
|
|
@ -28,6 +28,7 @@ import {
|
||||||
deferRenderPhaseUpdateToNextBatch,
|
deferRenderPhaseUpdateToNextBatch,
|
||||||
decoupleUpdatePriorityFromScheduler,
|
decoupleUpdatePriorityFromScheduler,
|
||||||
enableDebugTracing,
|
enableDebugTracing,
|
||||||
|
enableSchedulingProfiler,
|
||||||
} from 'shared/ReactFeatureFlags';
|
} from 'shared/ReactFeatureFlags';
|
||||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||||
import invariant from 'shared/invariant';
|
import invariant from 'shared/invariant';
|
||||||
|
@ -57,6 +58,17 @@ import {
|
||||||
logRenderStarted,
|
logRenderStarted,
|
||||||
logRenderStopped,
|
logRenderStopped,
|
||||||
} from './DebugTracing';
|
} 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
|
// The scheduler is imported here *only* to detect whether it's been mocked
|
||||||
import * as Scheduler from 'scheduler';
|
import * as Scheduler from 'scheduler';
|
||||||
|
@ -1509,6 +1521,10 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markRenderStarted(lanes);
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try {
|
try {
|
||||||
workLoopSync();
|
workLoopSync();
|
||||||
|
@ -1540,6 +1556,10 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markRenderStopped();
|
||||||
|
}
|
||||||
|
|
||||||
// Set this to null to indicate there's no in-progress render.
|
// Set this to null to indicate there's no in-progress render.
|
||||||
workInProgressRoot = null;
|
workInProgressRoot = null;
|
||||||
workInProgressRootRenderLanes = NoLanes;
|
workInProgressRootRenderLanes = NoLanes;
|
||||||
|
@ -1576,6 +1596,10 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markRenderStarted(lanes);
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try {
|
try {
|
||||||
workLoopConcurrent();
|
workLoopConcurrent();
|
||||||
|
@ -1601,9 +1625,16 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
|
||||||
// Check if the tree has completed.
|
// Check if the tree has completed.
|
||||||
if (workInProgress !== null) {
|
if (workInProgress !== null) {
|
||||||
// Still work remaining.
|
// Still work remaining.
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markRenderYielded();
|
||||||
|
}
|
||||||
return RootIncomplete;
|
return RootIncomplete;
|
||||||
} else {
|
} else {
|
||||||
// Completed the tree.
|
// Completed the tree.
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markRenderStopped();
|
||||||
|
}
|
||||||
|
|
||||||
// Set this to null to indicate there's no in-progress render.
|
// Set this to null to indicate there's no in-progress render.
|
||||||
workInProgressRoot = null;
|
workInProgressRoot = null;
|
||||||
workInProgressRootRenderLanes = NoLanes;
|
workInProgressRootRenderLanes = NoLanes;
|
||||||
|
@ -1893,6 +1924,10 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markCommitStarted(lanes);
|
||||||
|
}
|
||||||
|
|
||||||
if (finishedWork === null) {
|
if (finishedWork === null) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
if (enableDebugTracing) {
|
if (enableDebugTracing) {
|
||||||
|
@ -1900,6 +1935,10 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markCommitStopped();
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
root.finishedWork = null;
|
root.finishedWork = null;
|
||||||
|
@ -2196,6 +2235,10 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markCommitStopped();
|
||||||
|
}
|
||||||
|
|
||||||
// This is a legacy edge case. We just committed the initial mount of
|
// This is a legacy edge case. We just committed the initial mount of
|
||||||
// a ReactDOM.render-ed root inside of batchedUpdates. The commit fired
|
// a ReactDOM.render-ed root inside of batchedUpdates. The commit fired
|
||||||
// synchronously, but layout updates should be deferred until the end
|
// synchronously, but layout updates should be deferred until the end
|
||||||
|
@ -2212,6 +2255,10 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markCommitStopped();
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2342,6 +2389,10 @@ function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markLayoutEffectsStarted(committedLanes);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Should probably move the bulk of this function to commitWork.
|
// TODO: Should probably move the bulk of this function to commitWork.
|
||||||
while (nextEffect !== null) {
|
while (nextEffect !== null) {
|
||||||
setCurrentDebugFiberInDEV(nextEffect);
|
setCurrentDebugFiberInDEV(nextEffect);
|
||||||
|
@ -2366,6 +2417,10 @@ function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes) {
|
||||||
logLayoutEffectsStopped();
|
logLayoutEffectsStopped();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markLayoutEffectsStopped();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flushPassiveEffects() {
|
export function flushPassiveEffects() {
|
||||||
|
@ -2461,6 +2516,10 @@ function flushPassiveEffectsImpl() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markPassiveEffectsStarted(lanes);
|
||||||
|
}
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
isFlushingPassiveEffects = true;
|
isFlushingPassiveEffects = true;
|
||||||
}
|
}
|
||||||
|
@ -2623,6 +2682,10 @@ function flushPassiveEffectsImpl() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
markPassiveEffectsStopped();
|
||||||
|
}
|
||||||
|
|
||||||
executionContext = prevExecutionContext;
|
executionContext = prevExecutionContext;
|
||||||
|
|
||||||
flushSyncCallbackQueue();
|
flushSyncCallbackQueue();
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {Lane, Lanes} from './ReactFiberLane';
|
||||||
|
import type {Fiber} from './ReactInternalTypes';
|
||||||
|
import type {Wakeable} from 'shared/ReactTypes';
|
||||||
|
|
||||||
|
import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
|
||||||
|
import getComponentName from 'shared/getComponentName';
|
||||||
|
import {getStackByFiberInDevAndProd} from './ReactFiberComponentStack';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If performance exists and supports the subset of the User Timing API that we
|
||||||
|
* require.
|
||||||
|
*/
|
||||||
|
const supportsUserTiming =
|
||||||
|
typeof performance !== 'undefined' && typeof performance.mark === 'function';
|
||||||
|
|
||||||
|
function formatLanes(laneOrLanes: Lane | Lanes): string {
|
||||||
|
return ((laneOrLanes: any): number).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markCommitStarted(lanes: Lanes): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
performance.mark(`--commit-start-${formatLanes(lanes)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markCommitStopped(): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
performance.mark('--commit-stop');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
|
||||||
|
|
||||||
|
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
|
||||||
|
const wakeableIDs: WeakMap<Wakeable, number> = new PossiblyWeakMap();
|
||||||
|
let wakeableID: number = 0;
|
||||||
|
function getWakeableID(wakeable: Wakeable): number {
|
||||||
|
if (!wakeableIDs.has(wakeable)) {
|
||||||
|
wakeableIDs.set(wakeable, wakeableID++);
|
||||||
|
}
|
||||||
|
return ((wakeableIDs.get(wakeable): any): number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
|
||||||
|
const cachedFiberStacks: WeakMap<Fiber, string> = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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 {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
const id = getWakeableID(wakeable);
|
||||||
|
const componentName = getComponentName(fiber.type) || 'Unknown';
|
||||||
|
const componentStack = cacheFirstGetComponentStackByFiber(fiber);
|
||||||
|
performance.mark(
|
||||||
|
`--suspense-suspend-${id}-${componentName}-${componentStack}`,
|
||||||
|
);
|
||||||
|
wakeable.then(
|
||||||
|
() =>
|
||||||
|
performance.mark(
|
||||||
|
`--suspense-resolved-${id}-${componentName}-${componentStack}`,
|
||||||
|
),
|
||||||
|
() =>
|
||||||
|
performance.mark(
|
||||||
|
`--suspense-rejected-${id}-${componentName}-${componentStack}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markLayoutEffectsStarted(lanes: Lanes): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
performance.mark(`--layout-effects-start-${formatLanes(lanes)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markLayoutEffectsStopped(): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
performance.mark('--layout-effects-stop');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markPassiveEffectsStarted(lanes: Lanes): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
performance.mark(`--passive-effects-start-${formatLanes(lanes)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markPassiveEffectsStopped(): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
performance.mark('--passive-effects-stop');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markRenderStarted(lanes: Lanes): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
performance.mark(`--render-start-${formatLanes(lanes)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markRenderYielded(): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
performance.mark('--render-yield');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markRenderStopped(): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
performance.mark('--render-stop');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markRenderScheduled(lane: Lane): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
performance.mark(`--schedule-render-${formatLanes(lane)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markForceUpdateScheduled(fiber: Fiber, lane: Lane): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
const componentName = getComponentName(fiber.type) || 'Unknown';
|
||||||
|
const componentStack = cacheFirstGetComponentStackByFiber(fiber);
|
||||||
|
performance.mark(
|
||||||
|
`--schedule-forced-update-${formatLanes(
|
||||||
|
lane,
|
||||||
|
)}-${componentName}-${componentStack}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markStateUpdateScheduled(fiber: Fiber, lane: Lane): void {
|
||||||
|
if (enableSchedulingProfiler) {
|
||||||
|
if (supportsUserTiming) {
|
||||||
|
const componentName = getComponentName(fiber.type) || 'Unknown';
|
||||||
|
const componentStack = cacheFirstGetComponentStackByFiber(fiber);
|
||||||
|
performance.mark(
|
||||||
|
`--schedule-state-update-${formatLanes(
|
||||||
|
lane,
|
||||||
|
)}-${componentName}-${componentStack}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,468 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @emails react-core
|
||||||
|
* @jest-environment node
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function normalizeCodeLocInfo(str) {
|
||||||
|
return (
|
||||||
|
str &&
|
||||||
|
str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function(m, name) {
|
||||||
|
return '\n in ' + name + ' (at **)';
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('SchedulingProfiler', () => {
|
||||||
|
let React;
|
||||||
|
let ReactTestRenderer;
|
||||||
|
let ReactNoop;
|
||||||
|
let Scheduler;
|
||||||
|
|
||||||
|
let marks;
|
||||||
|
|
||||||
|
function createUserTimingPolyfill() {
|
||||||
|
// This is not a true polyfill, but it gives us enough to capture marks.
|
||||||
|
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API
|
||||||
|
return {
|
||||||
|
mark(markName) {
|
||||||
|
marks.push(markName);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules();
|
||||||
|
global.performance = createUserTimingPolyfill();
|
||||||
|
|
||||||
|
React = require('react');
|
||||||
|
|
||||||
|
// ReactNoop must be imported after ReactTestRenderer!
|
||||||
|
ReactTestRenderer = require('react-test-renderer');
|
||||||
|
ReactNoop = require('react-noop-renderer');
|
||||||
|
|
||||||
|
Scheduler = require('scheduler');
|
||||||
|
|
||||||
|
marks = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
delete global.performance;
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate !enableSchedulingProfiler
|
||||||
|
it('should not mark if enableSchedulingProfiler is false', () => {
|
||||||
|
ReactTestRenderer.create(<div />);
|
||||||
|
expect(marks).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark sync render without suspends or state updates', () => {
|
||||||
|
ReactTestRenderer.create(<div />);
|
||||||
|
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--schedule-render-1',
|
||||||
|
'--render-start-1',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-1',
|
||||||
|
'--layout-effects-start-1',
|
||||||
|
'--layout-effects-stop',
|
||||||
|
'--commit-stop',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark concurrent render without suspends or state updates', () => {
|
||||||
|
ReactTestRenderer.create(<div />, {unstable_isConcurrent: true});
|
||||||
|
|
||||||
|
expect(marks).toEqual(['--schedule-render-512']);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||||
|
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--render-start-512',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-512',
|
||||||
|
'--layout-effects-start-512',
|
||||||
|
'--layout-effects-stop',
|
||||||
|
'--commit-stop',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark render yields', async () => {
|
||||||
|
function Bar() {
|
||||||
|
Scheduler.unstable_yieldValue('Bar');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Foo() {
|
||||||
|
Scheduler.unstable_yieldValue('Foo');
|
||||||
|
return <Bar />;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactNoop.render(<Foo />);
|
||||||
|
// Do one step of work.
|
||||||
|
expect(ReactNoop.flushNextYield()).toEqual(['Foo']);
|
||||||
|
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--schedule-render-512',
|
||||||
|
'--render-start-512',
|
||||||
|
'--render-yield',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark sync render with suspense that resolves', async () => {
|
||||||
|
const fakeSuspensePromise = Promise.resolve(true);
|
||||||
|
function Example() {
|
||||||
|
throw fakeSuspensePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.create(
|
||||||
|
<React.Suspense fallback={null}>
|
||||||
|
<Example />
|
||||||
|
</React.Suspense>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--schedule-render-1',
|
||||||
|
'--render-start-1',
|
||||||
|
'--suspense-suspend-0-Example-\n at Example\n at Suspense',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-1',
|
||||||
|
'--layout-effects-start-1',
|
||||||
|
'--layout-effects-stop',
|
||||||
|
'--commit-stop',
|
||||||
|
]);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
await fakeSuspensePromise;
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--suspense-resolved-0-Example-\n at Example\n at Suspense',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark sync render with suspense that rejects', async () => {
|
||||||
|
const fakeSuspensePromise = Promise.reject(new Error('error'));
|
||||||
|
function Example() {
|
||||||
|
throw fakeSuspensePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.create(
|
||||||
|
<React.Suspense fallback={null}>
|
||||||
|
<Example />
|
||||||
|
</React.Suspense>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--schedule-render-1',
|
||||||
|
'--render-start-1',
|
||||||
|
'--suspense-suspend-0-Example-\n at Example\n at Suspense',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-1',
|
||||||
|
'--layout-effects-start-1',
|
||||||
|
'--layout-effects-stop',
|
||||||
|
'--commit-stop',
|
||||||
|
]);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
await expect(fakeSuspensePromise).rejects.toThrow();
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--suspense-rejected-0-Example-\n at Example\n at Suspense',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark concurrent render with suspense that resolves', async () => {
|
||||||
|
const fakeSuspensePromise = Promise.resolve(true);
|
||||||
|
function Example() {
|
||||||
|
throw fakeSuspensePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.create(
|
||||||
|
<React.Suspense fallback={null}>
|
||||||
|
<Example />
|
||||||
|
</React.Suspense>,
|
||||||
|
{unstable_isConcurrent: true},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(marks).toEqual(['--schedule-render-512']);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||||
|
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--render-start-512',
|
||||||
|
'--suspense-suspend-0-Example-\n at Example\n at Suspense',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-512',
|
||||||
|
'--layout-effects-start-512',
|
||||||
|
'--layout-effects-stop',
|
||||||
|
'--commit-stop',
|
||||||
|
]);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
await fakeSuspensePromise;
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--suspense-resolved-0-Example-\n at Example\n at Suspense',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark concurrent render with suspense that rejects', async () => {
|
||||||
|
const fakeSuspensePromise = Promise.reject(new Error('error'));
|
||||||
|
function Example() {
|
||||||
|
throw fakeSuspensePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.create(
|
||||||
|
<React.Suspense fallback={null}>
|
||||||
|
<Example />
|
||||||
|
</React.Suspense>,
|
||||||
|
{unstable_isConcurrent: true},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(marks).toEqual(['--schedule-render-512']);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||||
|
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--render-start-512',
|
||||||
|
'--suspense-suspend-0-Example-\n at Example\n at Suspense',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-512',
|
||||||
|
'--layout-effects-start-512',
|
||||||
|
'--layout-effects-stop',
|
||||||
|
'--commit-stop',
|
||||||
|
]);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
await expect(fakeSuspensePromise).rejects.toThrow();
|
||||||
|
expect(marks).toEqual([
|
||||||
|
'--suspense-rejected-0-Example-\n at Example\n at Suspense',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark cascading class component state updates', () => {
|
||||||
|
class Example extends React.Component {
|
||||||
|
state = {didMount: false};
|
||||||
|
componentDidMount() {
|
||||||
|
this.setState({didMount: true});
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.create(<Example />, {unstable_isConcurrent: true});
|
||||||
|
|
||||||
|
expect(marks).toEqual(['--schedule-render-512']);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||||
|
|
||||||
|
expect(marks.map(normalizeCodeLocInfo)).toEqual([
|
||||||
|
'--render-start-512',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-512',
|
||||||
|
'--layout-effects-start-512',
|
||||||
|
'--schedule-state-update-1-Example-\n in Example (at **)',
|
||||||
|
'--layout-effects-stop',
|
||||||
|
'--render-start-1',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-1',
|
||||||
|
'--commit-stop',
|
||||||
|
'--commit-stop',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark cascading class component force updates', () => {
|
||||||
|
class Example extends React.Component {
|
||||||
|
componentDidMount() {
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.create(<Example />, {unstable_isConcurrent: true});
|
||||||
|
|
||||||
|
expect(marks).toEqual(['--schedule-render-512']);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||||
|
|
||||||
|
expect(marks.map(normalizeCodeLocInfo)).toEqual([
|
||||||
|
'--render-start-512',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-512',
|
||||||
|
'--layout-effects-start-512',
|
||||||
|
'--schedule-forced-update-1-Example-\n in Example (at **)',
|
||||||
|
'--layout-effects-stop',
|
||||||
|
'--render-start-1',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-1',
|
||||||
|
'--commit-stop',
|
||||||
|
'--commit-stop',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark render phase state updates for class component', () => {
|
||||||
|
class Example extends React.Component {
|
||||||
|
state = {didRender: false};
|
||||||
|
render() {
|
||||||
|
if (this.state.didRender === false) {
|
||||||
|
this.setState({didRender: true});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.create(<Example />, {unstable_isConcurrent: true});
|
||||||
|
|
||||||
|
expect(marks).toEqual(['--schedule-render-512']);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
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 enableSchedulingProfiler
|
||||||
|
it('should mark render phase force updates for class component', () => {
|
||||||
|
class Example extends React.Component {
|
||||||
|
state = {didRender: false};
|
||||||
|
render() {
|
||||||
|
if (this.state.didRender === false) {
|
||||||
|
this.forceUpdate(() => this.setState({didRender: true}));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.create(<Example />, {unstable_isConcurrent: true});
|
||||||
|
|
||||||
|
expect(marks).toEqual(['--schedule-render-512']);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
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 enableSchedulingProfiler
|
||||||
|
it('should mark cascading layout updates', () => {
|
||||||
|
function Example() {
|
||||||
|
const [didMount, setDidMount] = React.useState(false);
|
||||||
|
React.useLayoutEffect(() => {
|
||||||
|
setDidMount(true);
|
||||||
|
}, []);
|
||||||
|
return didMount;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.create(<Example />, {unstable_isConcurrent: true});
|
||||||
|
|
||||||
|
expect(marks).toEqual(['--schedule-render-512']);
|
||||||
|
|
||||||
|
marks.splice(0);
|
||||||
|
|
||||||
|
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||||
|
|
||||||
|
expect(marks.map(normalizeCodeLocInfo)).toEqual([
|
||||||
|
'--render-start-512',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-512',
|
||||||
|
'--layout-effects-start-512',
|
||||||
|
'--schedule-state-update-1-Example-\n in Example (at **)',
|
||||||
|
'--layout-effects-stop',
|
||||||
|
'--render-start-1',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-1',
|
||||||
|
'--commit-stop',
|
||||||
|
'--commit-stop',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark cascading passive updates', () => {
|
||||||
|
function Example() {
|
||||||
|
const [didMount, setDidMount] = React.useState(false);
|
||||||
|
React.useEffect(() => {
|
||||||
|
setDidMount(true);
|
||||||
|
}, []);
|
||||||
|
return didMount;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.act(() => {
|
||||||
|
ReactTestRenderer.create(<Example />, {unstable_isConcurrent: true});
|
||||||
|
});
|
||||||
|
expect(marks.map(normalizeCodeLocInfo)).toEqual([
|
||||||
|
'--schedule-render-512',
|
||||||
|
'--render-start-512',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-512',
|
||||||
|
'--layout-effects-start-512',
|
||||||
|
'--layout-effects-stop',
|
||||||
|
'--commit-stop',
|
||||||
|
'--passive-effects-start-512',
|
||||||
|
'--schedule-state-update-1024-Example-\n in Example (at **)',
|
||||||
|
'--passive-effects-stop',
|
||||||
|
'--render-start-1024',
|
||||||
|
'--render-stop',
|
||||||
|
'--commit-start-1024',
|
||||||
|
'--commit-stop',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @gate enableSchedulingProfiler
|
||||||
|
it('should mark render phase updates', () => {
|
||||||
|
function Example() {
|
||||||
|
const [didRender, setDidRender] = React.useState(false);
|
||||||
|
if (!didRender) {
|
||||||
|
setDidRender(true);
|
||||||
|
}
|
||||||
|
return didRender;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactTestRenderer.act(() => {
|
||||||
|
ReactTestRenderer.create(<Example />, {unstable_isConcurrent: true});
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(marks.map(normalizeCodeLocInfo)).toContain(
|
||||||
|
'--schedule-state-update-1024-Example-\n in Example (at **)',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -15,6 +15,10 @@ export const enableFilterEmptyStringAttributesDOM = false;
|
||||||
// Intended to enable React core members to more easily debug scheduling issues in DEV builds.
|
// Intended to enable React core members to more easily debug scheduling issues in DEV builds.
|
||||||
export const enableDebugTracing = false;
|
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;
|
||||||
|
|
||||||
// Helps identify side effects in render-phase lifecycle hooks and setState
|
// Helps identify side effects in render-phase lifecycle hooks and setState
|
||||||
// reducers by double invoking them in Strict Mode.
|
// reducers by double invoking them in Strict Mode.
|
||||||
export const debugRenderPhaseSideEffectsForStrictMode = __DEV__;
|
export const debugRenderPhaseSideEffectsForStrictMode = __DEV__;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.native-fb';
|
||||||
|
|
||||||
// The rest of the flags are static for better dead code elimination.
|
// The rest of the flags are static for better dead code elimination.
|
||||||
export const enableDebugTracing = false;
|
export const enableDebugTracing = false;
|
||||||
|
export const enableSchedulingProfiler = false;
|
||||||
export const enableProfilerTimer = __PROFILE__;
|
export const enableProfilerTimer = __PROFILE__;
|
||||||
export const enableProfilerCommitHooks = false;
|
export const enableProfilerCommitHooks = false;
|
||||||
export const enableSchedulerTracing = __PROFILE__;
|
export const enableSchedulerTracing = __PROFILE__;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.native-oss';
|
||||||
|
|
||||||
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
||||||
export const enableDebugTracing = false;
|
export const enableDebugTracing = false;
|
||||||
|
export const enableSchedulingProfiler = false;
|
||||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
||||||
export const warnAboutDeprecatedLifecycles = true;
|
export const warnAboutDeprecatedLifecycles = true;
|
||||||
export const enableProfilerTimer = __PROFILE__;
|
export const enableProfilerTimer = __PROFILE__;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.test-renderer';
|
||||||
|
|
||||||
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
||||||
export const enableDebugTracing = false;
|
export const enableDebugTracing = false;
|
||||||
|
export const enableSchedulingProfiler = false;
|
||||||
export const warnAboutDeprecatedLifecycles = true;
|
export const warnAboutDeprecatedLifecycles = true;
|
||||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||||
export const enableProfilerTimer = __PROFILE__;
|
export const enableProfilerTimer = __PROFILE__;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.test-renderer.www';
|
||||||
|
|
||||||
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
||||||
export const enableDebugTracing = false;
|
export const enableDebugTracing = false;
|
||||||
|
export const enableSchedulingProfiler = false;
|
||||||
export const warnAboutDeprecatedLifecycles = true;
|
export const warnAboutDeprecatedLifecycles = true;
|
||||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||||
export const enableProfilerTimer = __PROFILE__;
|
export const enableProfilerTimer = __PROFILE__;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.testing';
|
||||||
|
|
||||||
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
||||||
export const enableDebugTracing = false;
|
export const enableDebugTracing = false;
|
||||||
|
export const enableSchedulingProfiler = false;
|
||||||
export const warnAboutDeprecatedLifecycles = true;
|
export const warnAboutDeprecatedLifecycles = true;
|
||||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||||
export const enableProfilerTimer = __PROFILE__;
|
export const enableProfilerTimer = __PROFILE__;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.testing.www';
|
||||||
|
|
||||||
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
||||||
export const enableDebugTracing = false;
|
export const enableDebugTracing = false;
|
||||||
|
export const enableSchedulingProfiler = false;
|
||||||
export const warnAboutDeprecatedLifecycles = true;
|
export const warnAboutDeprecatedLifecycles = true;
|
||||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||||
export const enableProfilerTimer = false;
|
export const enableProfilerTimer = false;
|
||||||
|
|
|
@ -17,9 +17,12 @@ export const warnAboutSpreadingKeyToJSX = __VARIANT__;
|
||||||
export const disableInputAttributeSyncing = __VARIANT__;
|
export const disableInputAttributeSyncing = __VARIANT__;
|
||||||
export const enableFilterEmptyStringAttributesDOM = __VARIANT__;
|
export const enableFilterEmptyStringAttributesDOM = __VARIANT__;
|
||||||
export const enableLegacyFBSupport = __VARIANT__;
|
export const enableLegacyFBSupport = __VARIANT__;
|
||||||
export const enableDebugTracing = !__VARIANT__;
|
|
||||||
export const decoupleUpdatePriorityFromScheduler = __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__;
|
||||||
|
|
||||||
// This only has an effect in the new reconciler. But also, the new reconciler
|
// 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
|
// is only enabled when __VARIANT__ is true. So this is set to the opposite of
|
||||||
// __VARIANT__ so that it's `false` when running against the new reconciler.
|
// __VARIANT__ so that it's `false` when running against the new reconciler.
|
||||||
|
|
|
@ -25,6 +25,8 @@ export const {
|
||||||
enableLegacyFBSupport,
|
enableLegacyFBSupport,
|
||||||
deferRenderPhaseUpdateToNextBatch,
|
deferRenderPhaseUpdateToNextBatch,
|
||||||
decoupleUpdatePriorityFromScheduler,
|
decoupleUpdatePriorityFromScheduler,
|
||||||
|
enableDebugTracing,
|
||||||
|
enableSchedulingProfiler,
|
||||||
} = dynamicFeatureFlags;
|
} = dynamicFeatureFlags;
|
||||||
|
|
||||||
// On WWW, __EXPERIMENTAL__ is used for a new modern build.
|
// On WWW, __EXPERIMENTAL__ is used for a new modern build.
|
||||||
|
@ -77,9 +79,6 @@ export const warnUnstableRenderSubtreeIntoContainer = false;
|
||||||
// to the correct value.
|
// to the correct value.
|
||||||
export const enableNewReconciler = __VARIANT__;
|
export const enableNewReconciler = __VARIANT__;
|
||||||
|
|
||||||
// TODO: This does not currently exist in the new reconciler fork.
|
|
||||||
export const enableDebugTracing = !__VARIANT__;
|
|
||||||
|
|
||||||
// Flow magic to verify the exports of this file match the original version.
|
// Flow magic to verify the exports of this file match the original version.
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
type Check<_X, Y: _X, X: Y = _X> = null;
|
type Check<_X, Y: _X, X: Y = _X> = null;
|
||||||
|
|
Loading…
Reference in New Issue