@samitouri / QOS-React-2 / commits / 51267c4ac9

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

Brian Vaughn committed Jul 17, 2020 at 11:24 UTC 51267c4ac9de8bf190505fbb7670ed99aae325f7
17 files changed +382 -46
packages/react-reconciler/src/ReactFiberClassComponent.new.js
+47 -1
@@ -16,6 +16,8 @@ import {Update, Snapshot} from './ReactSideEffectTags';
16 import {
17 debugRenderPhaseSideEffectsForStrictMode,
18 disableLegacyContext,
19 + enableDebugTracing,
20 + enableSchedulingProfiler,
21 warnAboutDeprecatedLifecycles,
22 } from 'shared/ReactFeatureFlags';
23 import ReactStrictModeWarnings from './ReactStrictModeWarnings.new';
@@ -27,7 +29,7 @@ import invariant from 'shared/invariant';
29 import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
30
31 import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
30 -import {StrictMode} from './ReactTypeOfMode';
32 +import {DebugTracingMode, StrictMode} from './ReactTypeOfMode';
33
34 import {
35 enqueueUpdate,
@@ -55,8 +57,13 @@ import {
57 scheduleUpdateOnFiber,
58 } from './ReactFiberWorkLoop.new';
59 import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';
60 +import {logForceUpdateScheduled, logStateUpdateScheduled} from './DebugTracing';
61
62 import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
63 +import {
64 + markForceUpdateScheduled,
65 + markStateUpdateScheduled,
66 +} from './SchedulingProfiler';
67
68 const fakeInternalInstance = {};
69 const isArray = Array.isArray;
@@ -203,6 +210,19 @@ const classComponentUpdater = {
210
211 enqueueUpdate(fiber, update);
212 scheduleUpdateOnFiber(fiber, lane, eventTime);
213 +
214 + if (__DEV__) {
215 + if (enableDebugTracing) {
216 + if (fiber.mode & DebugTracingMode) {
217 + const name = getComponentName(fiber.type) || 'Unknown';
218 + logStateUpdateScheduled(name, lane, payload);
219 + }
220 + }
221 + }
222 +
223 + if (enableSchedulingProfiler) {
224 + markStateUpdateScheduled(fiber, lane);
225 + }
226 },
227 enqueueReplaceState(inst, payload, callback) {
228 const fiber = getInstance(inst);
@@ -223,6 +243,19 @@ const classComponentUpdater = {
243
244 enqueueUpdate(fiber, update);
245 scheduleUpdateOnFiber(fiber, lane, eventTime);
246 +
247 + if (__DEV__) {
248 + if (enableDebugTracing) {
249 + if (fiber.mode & DebugTracingMode) {
250 + const name = getComponentName(fiber.type) || 'Unknown';
251 + logStateUpdateScheduled(name, lane, payload);
252 + }
253 + }
254 + }
255 +
256 + if (enableSchedulingProfiler) {
257 + markStateUpdateScheduled(fiber, lane);
258 + }
259 },
260 enqueueForceUpdate(inst, callback) {
261 const fiber = getInstance(inst);
@@ -242,6 +275,19 @@ const classComponentUpdater = {
275
276 enqueueUpdate(fiber, update);
277 scheduleUpdateOnFiber(fiber, lane, eventTime);
278 +
279 + if (__DEV__) {
280 + if (enableDebugTracing) {
281 + if (fiber.mode & DebugTracingMode) {
282 + const name = getComponentName(fiber.type) || 'Unknown';
283 + logForceUpdateScheduled(name, lane);
284 + }
285 + }
286 + }
287 +
288 + if (enableSchedulingProfiler) {
289 + markForceUpdateScheduled(fiber, lane);
290 + }
291 },
292 };
293
packages/react-reconciler/src/ReactFiberHooks.new.js
+21 -2
@@ -24,9 +24,13 @@ import type {FiberRoot} from './ReactInternalTypes';
24 import type {OpaqueIDType} from './ReactFiberHostConfig';
25
26 import ReactSharedInternals from 'shared/ReactSharedInternals';
27 -import {enableNewReconciler} from 'shared/ReactFeatureFlags';
27 +import {
28 + enableDebugTracing,
29 + enableSchedulingProfiler,
30 + enableNewReconciler,
31 +} from 'shared/ReactFeatureFlags';
32
29 -import {NoMode, BlockingMode} from './ReactTypeOfMode';
33 +import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
34 import {
35 NoLane,
36 NoLanes,
@@ -88,6 +92,8 @@ import {
92 warnAboutMultipleRenderersDEV,
93 } from './ReactMutableSource.new';
94 import {getIsRendering} from './ReactCurrentFiber';
95 +import {logStateUpdateScheduled} from './DebugTracing';
96 +import {markStateUpdateScheduled} from './SchedulingProfiler';
97
98 const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
99
@@ -1751,6 +1757,19 @@ function dispatchAction<S, A>(
1757 }
1758 scheduleUpdateOnFiber(fiber, lane, eventTime);
1759 }
1760 +
1761 + if (__DEV__) {
1762 + if (enableDebugTracing) {
1763 + if (fiber.mode & DebugTracingMode) {
1764 + const name = getComponentName(fiber.type) || 'Unknown';
1765 + logStateUpdateScheduled(name, lane, action);
1766 + }
1767 + }
1768 + }
1769 +
1770 + if (enableSchedulingProfiler) {
1771 + markStateUpdateScheduled(fiber, lane);
1772 + }
1773 }
1774
1775 export const ContextOnlyDispatcher: Dispatcher = {
packages/react-reconciler/src/ReactFiberReconciler.new.js
+6
@@ -39,6 +39,7 @@ import {
39 } from './ReactWorkTags';
40 import getComponentName from 'shared/getComponentName';
41 import invariant from 'shared/invariant';
42 +import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
43 import ReactSharedInternals from 'shared/ReactSharedInternals';
44 import {getPublicInstance} from './ReactFiberHostConfig';
45 import {
@@ -95,6 +96,7 @@ import {
96 setRefreshHandler,
97 findHostInstancesForRefresh,
98 } from './ReactFiberHotReloading.new';
99 +import {markRenderScheduled} from './SchedulingProfiler';
100
101 export {registerMutableSourceForHydration} from './ReactMutableSource.new';
102 export {createPortal} from './ReactPortal';
@@ -273,6 +275,10 @@ export function updateContainer(
275 const suspenseConfig = requestCurrentSuspenseConfig();
276 const lane = requestUpdateLane(current, suspenseConfig);
277
278 + if (enableSchedulingProfiler) {
279 + markRenderScheduled(lane);
280 + }
281 +
282 const context = getContextForSubtree(parentComponent);
283 if (container.context === null) {
284 container.context = context;
packages/react-reconciler/src/ReactFiberThrow.new.js
+20 -1
@@ -31,7 +31,11 @@ import {
31 ForceUpdateForLegacySuspense,
32 } from './ReactSideEffectTags';
33 import {shouldCaptureSuspense} from './ReactFiberSuspenseComponent.new';
34 -import {NoMode, BlockingMode} from './ReactTypeOfMode';
34 +import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
35 +import {
36 + enableDebugTracing,
37 + enableSchedulingProfiler,
38 +} from 'shared/ReactFeatureFlags';
39 import {createCapturedValue} from './ReactCapturedValue';
40 import {
41 enqueueCapturedUpdate,
@@ -54,6 +58,8 @@ import {
58 pingSuspendedRoot,
59 } from './ReactFiberWorkLoop.new';
60 import {logCapturedError} from './ReactFiberErrorLogger';
61 +import {logComponentSuspended} from './DebugTracing';
62 +import {markComponentSuspended} from './SchedulingProfiler';
63
64 import {
65 SyncLane,
@@ -190,6 +196,19 @@ function throwException(
196 // This is a wakeable.
197 const wakeable: Wakeable = (value: any);
198
199 + if (__DEV__) {
200 + if (enableDebugTracing) {
201 + if (sourceFiber.mode & DebugTracingMode) {
202 + const name = getComponentName(sourceFiber.type) || 'Unknown';
203 + logComponentSuspended(name, wakeable);
204 + }
205 + }
206 + }
207 +
208 + if (enableSchedulingProfiler) {
209 + markComponentSuspended(sourceFiber, wakeable);
210 + }
211 +
212 if ((sourceFiber.mode & BlockingMode) === NoMode) {
213 // Reset the memoizedState to what it was before we attempted
214 // to render it.
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+146
@@ -27,6 +27,8 @@ import {
27 warnAboutUnmockedScheduler,
28 deferRenderPhaseUpdateToNextBatch,
29 decoupleUpdatePriorityFromScheduler,
30 + enableDebugTracing,
31 + enableSchedulingProfiler,
32 enableScopeAPI,
33 } from 'shared/ReactFeatureFlags';
34 import ReactSharedInternals from 'shared/ReactSharedInternals';
@@ -47,6 +49,27 @@ import {
49 flushSyncCallbackQueue,
50 scheduleSyncCallback,
51 } from './SchedulerWithReactIntegration.new';
52 +import {
53 + logCommitStarted,
54 + logCommitStopped,
55 + logLayoutEffectsStarted,
56 + logLayoutEffectsStopped,
57 + logPassiveEffectsStarted,
58 + logPassiveEffectsStopped,
59 + logRenderStarted,
60 + logRenderStopped,
61 +} from './DebugTracing';
62 +import {
63 + markCommitStarted,
64 + markCommitStopped,
65 + markLayoutEffectsStarted,
66 + markLayoutEffectsStopped,
67 + markPassiveEffectsStarted,
68 + markPassiveEffectsStopped,
69 + markRenderStarted,
70 + markRenderYielded,
71 + markRenderStopped,
72 +} from './SchedulingProfiler';
73
74 // The scheduler is imported here *only* to detect whether it's been mocked
75 import * as Scheduler from 'scheduler';
@@ -1516,6 +1539,16 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1539
1540 const prevInteractions = pushInteractions(root);
1541
1542 + if (__DEV__) {
1543 + if (enableDebugTracing) {
1544 + logRenderStarted(lanes);
1545 + }
1546 + }
1547 +
1548 + if (enableSchedulingProfiler) {
1549 + markRenderStarted(lanes);
1550 + }
1551 +
1552 do {
1553 try {
1554 workLoopSync();
@@ -1541,6 +1574,16 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1574 );
1575 }
1576
1577 + if (__DEV__) {
1578 + if (enableDebugTracing) {
1579 + logRenderStopped();
1580 + }
1581 + }
1582 +
1583 + if (enableSchedulingProfiler) {
1584 + markRenderStopped();
1585 + }
1586 +
1587 // Set this to null to indicate there's no in-progress render.
1588 workInProgressRoot = null;
1589 workInProgressRootRenderLanes = NoLanes;
@@ -1571,6 +1614,16 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
1614
1615 const prevInteractions = pushInteractions(root);
1616
1617 + if (__DEV__) {
1618 + if (enableDebugTracing) {
1619 + logRenderStarted(lanes);
1620 + }
1621 + }
1622 +
1623 + if (enableSchedulingProfiler) {
1624 + markRenderStarted(lanes);
1625 + }
1626 +
1627 do {
1628 try {
1629 workLoopConcurrent();
@@ -1587,12 +1640,25 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
1640 popDispatcher(prevDispatcher);
1641 executionContext = prevExecutionContext;
1642
1643 + if (__DEV__) {
1644 + if (enableDebugTracing) {
1645 + logRenderStopped();
1646 + }
1647 + }
1648 +
1649 // Check if the tree has completed.
1650 if (workInProgress !== null) {
1651 // Still work remaining.
1652 + if (enableSchedulingProfiler) {
1653 + markRenderYielded();
1654 + }
1655 return RootIncomplete;
1656 } else {
1657 // Completed the tree.
1658 + if (enableSchedulingProfiler) {
1659 + markRenderStopped();
1660 + }
1661 +
1662 // Set this to null to indicate there's no in-progress render.
1663 workInProgressRoot = null;
1664 workInProgressRootRenderLanes = NoLanes;
@@ -1950,7 +2016,28 @@ function commitRootImpl(root, renderPriorityLevel) {
2016
2017 const finishedWork = root.finishedWork;
2018 const lanes = root.finishedLanes;
2019 +
2020 + if (__DEV__) {
2021 + if (enableDebugTracing) {
2022 + logCommitStarted(lanes);
2023 + }
2024 + }
2025 +
2026 + if (enableSchedulingProfiler) {
2027 + markCommitStarted(lanes);
2028 + }
2029 +
2030 if (finishedWork === null) {
2031 + if (__DEV__) {
2032 + if (enableDebugTracing) {
2033 + logCommitStopped();
2034 + }
2035 + }
2036 +
2037 + if (enableSchedulingProfiler) {
2038 + markCommitStopped();
2039 + }
2040 +
2041 return null;
2042 }
2043 root.finishedWork = null;
@@ -2062,8 +2149,27 @@ function commitRootImpl(root, renderPriorityLevel) {
2149 // The next phase is the layout phase, where we call effects that read
2150 // the host tree after it's been mutated. The idiomatic use case for this is
2151 // layout, but class component lifecycles also fire here for legacy reasons.
2152 +
2153 + if (__DEV__) {
2154 + if (enableDebugTracing) {
2155 + logLayoutEffectsStarted(lanes);
2156 + }
2157 + }
2158 + if (enableSchedulingProfiler) {
2159 + markLayoutEffectsStarted(lanes);
2160 + }
2161 +
2162 commitLayoutEffects(finishedWork, root, lanes);
2163
2164 + if (__DEV__) {
2165 + if (enableDebugTracing) {
2166 + logLayoutEffectsStopped();
2167 + }
2168 + }
2169 + if (enableSchedulingProfiler) {
2170 + markLayoutEffectsStopped();
2171 + }
2172 +
2173 // Tell Scheduler to yield at the end of the frame, so the browser has an
2174 // opportunity to paint.
2175 requestPaint();
@@ -2165,6 +2271,16 @@ function commitRootImpl(root, renderPriorityLevel) {
2271 }
2272
2273 if ((executionContext & LegacyUnbatchedContext) !== NoContext) {
2274 + if (__DEV__) {
2275 + if (enableDebugTracing) {
2276 + logCommitStopped();
2277 + }
2278 + }
2279 +
2280 + if (enableSchedulingProfiler) {
2281 + markCommitStopped();
2282 + }
2283 +
2284 // This is a legacy edge case. We just committed the initial mount of
2285 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired
2286 // synchronously, but layout updates should be deferred until the end
@@ -2175,6 +2291,16 @@ function commitRootImpl(root, renderPriorityLevel) {
2291 // If layout work was scheduled, flush it now.
2292 flushSyncCallbackQueue();
2293
2294 + if (__DEV__) {
2295 + if (enableDebugTracing) {
2296 + logCommitStopped();
2297 + }
2298 + }
2299 +
2300 + if (enableSchedulingProfiler) {
2301 + markCommitStopped();
2302 + }
2303 +
2304 return null;
2305 }
2306
@@ -2572,6 +2698,16 @@ function flushPassiveEffectsImpl() {
2698 'Cannot flush passive effects while already rendering.',
2699 );
2700
2701 + if (__DEV__) {
2702 + if (enableDebugTracing) {
2703 + logPassiveEffectsStarted(lanes);
2704 + }
2705 + }
2706 +
2707 + if (enableSchedulingProfiler) {
2708 + markPassiveEffectsStarted(lanes);
2709 + }
2710 +
2711 if (__DEV__) {
2712 isFlushingPassiveEffects = true;
2713 }
@@ -2716,6 +2852,16 @@ function flushPassiveEffectsImpl() {
2852 isFlushingPassiveEffects = false;
2853 }
2854
2855 + if (__DEV__) {
2856 + if (enableDebugTracing) {
2857 + logPassiveEffectsStopped();
2858 + }
2859 + }
2860 +
2861 + if (enableSchedulingProfiler) {
2862 + markPassiveEffectsStopped();
2863 + }
2864 +
2865 executionContext = prevExecutionContext;
2866
2867 flushSyncCallbackQueue();
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+1 -1
@@ -1361,7 +1361,7 @@ function handleError(root, thrownValue): void {
1361 // sibling, or the parent if there are no siblings. But since the root
1362 // has no siblings nor a parent, we set it to null. Usually this is
1363 // handled by `completeUnitOfWork` or `unwindWork`, but since we're
1364 - // interntionally not calling those, we need set it here.
1364 + // intentionally not calling those, we need set it here.
1365 // TODO: Consider calling `unwindWork` to pop the contexts.
1366 workInProgress = null;
1367 return;
packages/react-reconciler/src/SchedulingProfiler.js
+31 -18
@@ -11,7 +11,10 @@ import type {Lane, Lanes} from './ReactFiberLane';
11 import type {Fiber} from './ReactInternalTypes';
12 import type {Wakeable} from 'shared/ReactTypes';
13
14 -import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
14 +import {
15 + enableSchedulingProfiler,
16 + enableSchedulingProfilerComponentStacks,
17 +} from 'shared/ReactFeatureFlags';
18 import getComponentName from 'shared/getComponentName';
19 import {getStackByFiberInDevAndProd} from './ReactFiberComponentStack';
20
@@ -54,21 +57,31 @@ function getWakeableID(wakeable: Wakeable): number {
57 return ((wakeableIDs.get(wakeable): any): number);
58 }
59
57 -// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
58 -const cachedFiberStacks: WeakMap<Fiber, string> = new PossiblyWeakMap();
59 -function cacheFirstGetComponentStackByFiber(fiber: Fiber): string {
60 - if (cachedFiberStacks.has(fiber)) {
61 - return ((cachedFiberStacks.get(fiber): any): string);
62 - } else {
63 - const alternate = fiber.alternate;
64 - if (alternate !== null && cachedFiberStacks.has(alternate)) {
65 - return ((cachedFiberStacks.get(alternate): any): string);
60 +let getComponentStackByFiber = function getComponentStackByFiberDisabled(
61 + fiber: Fiber,
62 +): string {
63 + return '';
64 +};
65 +
66 +if (enableSchedulingProfilerComponentStacks) {
67 + // $FlowFixMe: Flow cannot handle polymorphic WeakMaps
68 + const cachedFiberStacks: WeakMap<Fiber, string> = new PossiblyWeakMap();
69 + getComponentStackByFiber = function cacheFirstGetComponentStackByFiber(
70 + fiber: Fiber,
71 + ): string {
72 + if (cachedFiberStacks.has(fiber)) {
73 + return ((cachedFiberStacks.get(fiber): any): string);
74 + } else {
75 + const alternate = fiber.alternate;
76 + if (alternate !== null && cachedFiberStacks.has(alternate)) {
77 + return ((cachedFiberStacks.get(alternate): any): string);
78 + }
79 }
67 - }
68 - // TODO (brian) Generate and store temporary ID so DevTools can match up a component stack later.
69 - const componentStack = getStackByFiberInDevAndProd(fiber) || '';
70 - cachedFiberStacks.set(fiber, componentStack);
71 - return componentStack;
80 + // TODO (brian) Generate and store temporary ID so DevTools can match up a component stack later.
81 + const componentStack = getStackByFiberInDevAndProd(fiber) || '';
82 + cachedFiberStacks.set(fiber, componentStack);
83 + return componentStack;
84 + };
85 }
86
87 export function markComponentSuspended(fiber: Fiber, wakeable: Wakeable): void {
@@ -76,7 +89,7 @@ export function markComponentSuspended(fiber: Fiber, wakeable: Wakeable): void {
89 if (supportsUserTiming) {
90 const id = getWakeableID(wakeable);
91 const componentName = getComponentName(fiber.type) || 'Unknown';
79 - const componentStack = cacheFirstGetComponentStackByFiber(fiber);
92 + const componentStack = getComponentStackByFiber(fiber);
93 performance.mark(
94 `--suspense-suspend-${id}-${componentName}-${componentStack}`,
95 );
@@ -162,7 +175,7 @@ export function markForceUpdateScheduled(fiber: Fiber, lane: Lane): void {
175 if (enableSchedulingProfiler) {
176 if (supportsUserTiming) {
177 const componentName = getComponentName(fiber.type) || 'Unknown';
165 - const componentStack = cacheFirstGetComponentStackByFiber(fiber);
178 + const componentStack = getComponentStackByFiber(fiber);
179 performance.mark(
180 `--schedule-forced-update-${formatLanes(
181 lane,
@@ -176,7 +189,7 @@ export function markStateUpdateScheduled(fiber: Fiber, lane: Lane): void {
189 if (enableSchedulingProfiler) {
190 if (supportsUserTiming) {
191 const componentName = getComponentName(fiber.type) || 'Unknown';
179 - const componentStack = cacheFirstGetComponentStackByFiber(fiber);
192 + const componentStack = getComponentStackByFiber(fiber);
193 performance.mark(
194 `--schedule-state-update-${formatLanes(
195 lane,
packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js
+87 -18
@@ -19,6 +19,20 @@ function normalizeCodeLocInfo(str) {
19 );
20 }
21
22 +// TODO (enableSchedulingProfilerComponentStacks) Clean this up once the feature flag has been removed.
23 +function toggleComponentStacks(mark) {
24 + let expectedMark = mark;
25 + gate(({enableSchedulingProfilerComponentStacks}) => {
26 + if (!enableSchedulingProfilerComponentStacks) {
27 + const index = mark.indexOf('\n ');
28 + if (index >= 0) {
29 + expectedMark = mark.substr(0, index);
30 + }
31 + }
32 + });
33 + return expectedMark;
34 +}
35 +
36 describe('SchedulingProfiler', () => {
37 let React;
38 let ReactTestRenderer;
@@ -136,7 +150,9 @@ describe('SchedulingProfiler', () => {
150 expect(marks).toEqual([
151 '--schedule-render-1',
152 '--render-start-1',
139 - '--suspense-suspend-0-Example-\n at Example\n at Suspense',
153 + toggleComponentStacks(
154 + '--suspense-suspend-0-Example-\n at Example\n at Suspense',
155 + ),
156 '--render-stop',
157 '--commit-start-1',
158 '--layout-effects-start-1',
@@ -148,7 +164,9 @@ describe('SchedulingProfiler', () => {
164
165 await fakeSuspensePromise;
166 expect(marks).toEqual([
151 - '--suspense-resolved-0-Example-\n at Example\n at Suspense',
167 + toggleComponentStacks(
168 + '--suspense-resolved-0-Example-\n at Example\n at Suspense',
169 + ),
170 ]);
171 });
172
@@ -168,7 +186,9 @@ describe('SchedulingProfiler', () => {
186 expect(marks).toEqual([
187 '--schedule-render-1',
188 '--render-start-1',
171 - '--suspense-suspend-0-Example-\n at Example\n at Suspense',
189 + toggleComponentStacks(
190 + '--suspense-suspend-0-Example-\n at Example\n at Suspense',
191 + ),
192 '--render-stop',
193 '--commit-start-1',
194 '--layout-effects-start-1',
@@ -180,7 +200,9 @@ describe('SchedulingProfiler', () => {
200
201 await expect(fakeSuspensePromise).rejects.toThrow();
202 expect(marks).toEqual([
183 - '--suspense-rejected-0-Example-\n at Example\n at Suspense',
203 + toggleComponentStacks(
204 + '--suspense-rejected-0-Example-\n at Example\n at Suspense',
205 + ),
206 ]);
207 });
208
@@ -206,7 +228,9 @@ describe('SchedulingProfiler', () => {
228
229 expect(marks).toEqual([
230 '--render-start-512',
209 - '--suspense-suspend-0-Example-\n at Example\n at Suspense',
231 + toggleComponentStacks(
232 + '--suspense-suspend-0-Example-\n at Example\n at Suspense',
233 + ),
234 '--render-stop',
235 '--commit-start-512',
236 '--layout-effects-start-512',
@@ -218,7 +242,9 @@ describe('SchedulingProfiler', () => {
242
243 await fakeSuspensePromise;
244 expect(marks).toEqual([
221 - '--suspense-resolved-0-Example-\n at Example\n at Suspense',
245 + toggleComponentStacks(
246 + '--suspense-resolved-0-Example-\n at Example\n at Suspense',
247 + ),
248 ]);
249 });
250
@@ -244,7 +270,9 @@ describe('SchedulingProfiler', () => {
270
271 expect(marks).toEqual([
272 '--render-start-512',
247 - '--suspense-suspend-0-Example-\n at Example\n at Suspense',
273 + toggleComponentStacks(
274 + '--suspense-suspend-0-Example-\n at Example\n at Suspense',
275 + ),
276 '--render-stop',
277 '--commit-start-512',
278 '--layout-effects-start-512',
@@ -256,7 +284,9 @@ describe('SchedulingProfiler', () => {
284
285 await expect(fakeSuspensePromise).rejects.toThrow();
286 expect(marks).toEqual([
259 - '--suspense-rejected-0-Example-\n at Example\n at Suspense',
287 + toggleComponentStacks(
288 + '--suspense-rejected-0-Example-\n at Example\n at Suspense',
289 + ),
290 ]);
291 });
292
@@ -285,7 +315,9 @@ describe('SchedulingProfiler', () => {
315 '--render-stop',
316 '--commit-start-512',
317 '--layout-effects-start-512',
288 - '--schedule-state-update-1-Example-\n in Example (at **)',
318 + toggleComponentStacks(
319 + '--schedule-state-update-1-Example-\n in Example (at **)',
320 + ),
321 '--layout-effects-stop',
322 '--render-start-1',
323 '--render-stop',
@@ -319,7 +351,9 @@ describe('SchedulingProfiler', () => {
351 '--render-stop',
352 '--commit-start-512',
353 '--layout-effects-start-512',
322 - '--schedule-forced-update-1-Example-\n in Example (at **)',
354 + toggleComponentStacks(
355 + '--schedule-forced-update-1-Example-\n in Example (at **)',
356 + ),
357 '--layout-effects-stop',
358 '--render-start-1',
359 '--render-stop',
@@ -351,8 +385,18 @@ describe('SchedulingProfiler', () => {
385 expect(Scheduler).toFlushUntilNextPaint([]);
386 }).toErrorDev('Cannot update during an existing state transition');
387
354 - expect(marks.map(normalizeCodeLocInfo)).toContain(
355 - '--schedule-state-update-1024-Example-\n in Example (at **)',
388 + gate(({old}) =>
389 + old
390 + ? expect(marks.map(normalizeCodeLocInfo)).toContain(
391 + toggleComponentStacks(
392 + '--schedule-state-update-1024-Example-\n in Example (at **)',
393 + ),
394 + )
395 + : expect(marks.map(normalizeCodeLocInfo)).toContain(
396 + toggleComponentStacks(
397 + '--schedule-state-update-512-Example-\n in Example (at **)',
398 + ),
399 + ),
400 );
401 });
402
@@ -378,8 +422,18 @@ describe('SchedulingProfiler', () => {
422 expect(Scheduler).toFlushUntilNextPaint([]);
423 }).toErrorDev('Cannot update during an existing state transition');
424
381 - expect(marks.map(normalizeCodeLocInfo)).toContain(
382 - '--schedule-forced-update-1024-Example-\n in Example (at **)',
425 + gate(({old}) =>
426 + old
427 + ? expect(marks.map(normalizeCodeLocInfo)).toContain(
428 + toggleComponentStacks(
429 + '--schedule-forced-update-1024-Example-\n in Example (at **)',
430 + ),
431 + )
432 + : expect(marks.map(normalizeCodeLocInfo)).toContain(
433 + toggleComponentStacks(
434 + '--schedule-forced-update-512-Example-\n in Example (at **)',
435 + ),
436 + ),
437 );
438 });
439
@@ -406,7 +460,9 @@ describe('SchedulingProfiler', () => {
460 '--render-stop',
461 '--commit-start-512',
462 '--layout-effects-start-512',
409 - '--schedule-state-update-1-Example-\n in Example (at **)',
463 + toggleComponentStacks(
464 + '--schedule-state-update-1-Example-\n in Example (at **)',
465 + ),
466 '--layout-effects-stop',
467 '--render-start-1',
468 '--render-stop',
@@ -429,6 +485,7 @@ describe('SchedulingProfiler', () => {
485 ReactTestRenderer.act(() => {
486 ReactTestRenderer.create(<Example />, {unstable_isConcurrent: true});
487 });
488 +
489 expect(marks.map(normalizeCodeLocInfo)).toEqual([
490 '--schedule-render-512',
491 '--render-start-512',
@@ -438,7 +495,9 @@ describe('SchedulingProfiler', () => {
495 '--layout-effects-stop',
496 '--commit-stop',
497 '--passive-effects-start-512',
441 - '--schedule-state-update-1024-Example-\n in Example (at **)',
498 + toggleComponentStacks(
499 + '--schedule-state-update-1024-Example-\n in Example (at **)',
500 + ),
501 '--passive-effects-stop',
502 '--render-start-1024',
503 '--render-stop',
@@ -461,8 +520,18 @@ describe('SchedulingProfiler', () => {
520 ReactTestRenderer.create(<Example />, {unstable_isConcurrent: true});
521 });
522
464 - expect(marks.map(normalizeCodeLocInfo)).toContain(
465 - '--schedule-state-update-1024-Example-\n in Example (at **)',
523 + gate(({old}) =>
524 + old
525 + ? expect(marks.map(normalizeCodeLocInfo)).toContain(
526 + toggleComponentStacks(
527 + '--schedule-state-update-1024-Example-\n in Example (at **)',
528 + ),
529 + )
530 + : expect(marks.map(normalizeCodeLocInfo)).toContain(
531 + toggleComponentStacks(
532 + '--schedule-state-update-512-Example-\n in Example (at **)',
533 + ),
534 + ),
535 );
536 });
537 });
packages/shared/ReactFeatureFlags.js
+2 -1
@@ -17,7 +17,8 @@ export const enableDebugTracing = false;
17
18 // Adds user timing marks for e.g. state updates, suspense, and work loop stuff,
19 // for an experimental scheduling profiler tool.
20 -export const enableSchedulingProfiler = false;
20 +export const enableSchedulingProfiler = __PROFILE__ && __EXPERIMENTAL__;
21 +export const enableSchedulingProfilerComponentStacks = false;
22
23 // Helps identify side effects in render-phase lifecycle hooks and setState
24 // reducers by double invoking them in Strict Mode.
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.native-fb';
13 // The rest of the flags are static for better dead code elimination.
14 export const enableDebugTracing = false;
15 export const enableSchedulingProfiler = false;
16 +export const enableSchedulingProfilerComponentStacks = false;
17 export const enableProfilerTimer = __PROFILE__;
18 export const enableProfilerCommitHooks = false;
19 export const enableSchedulerTracing = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.native-oss';
13 export const debugRenderPhaseSideEffectsForStrictMode = false;
14 export const enableDebugTracing = false;
15 export const enableSchedulingProfiler = false;
16 +export const enableSchedulingProfilerComponentStacks = false;
17 export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
18 export const warnAboutDeprecatedLifecycles = true;
19 export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.test-renderer';
13 export const debugRenderPhaseSideEffectsForStrictMode = false;
14 export const enableDebugTracing = false;
15 export const enableSchedulingProfiler = false;
16 +export const enableSchedulingProfilerComponentStacks = false;
17 export const warnAboutDeprecatedLifecycles = true;
18 export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
19 export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.test-renderer.www';
13 export const debugRenderPhaseSideEffectsForStrictMode = false;
14 export const enableDebugTracing = false;
15 export const enableSchedulingProfiler = false;
16 +export const enableSchedulingProfilerComponentStacks = false;
17 export const warnAboutDeprecatedLifecycles = true;
18 export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
19 export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.testing.js
+1
@@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.testing';
13 export const debugRenderPhaseSideEffectsForStrictMode = false;
14 export const enableDebugTracing = false;
15 export const enableSchedulingProfiler = false;
16 +export const enableSchedulingProfilerComponentStacks = false;
17 export const warnAboutDeprecatedLifecycles = true;
18 export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
19 export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.testing.www.js
+1
@@ -13,6 +13,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.testing.www';
13 export const debugRenderPhaseSideEffectsForStrictMode = false;
14 export const enableDebugTracing = false;
15 export const enableSchedulingProfiler = false;
16 +export const enableSchedulingProfilerComponentStacks = false;
17 export const warnAboutDeprecatedLifecycles = true;
18 export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
19 export const enableProfilerTimer = false;
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+11 -3
@@ -19,9 +19,17 @@ export const enableFilterEmptyStringAttributesDOM = __VARIANT__;
19 export const enableLegacyFBSupport = __VARIANT__;
20 export const decoupleUpdatePriorityFromScheduler = __VARIANT__;
21
22 -// TODO: These features do not currently exist in the new reconciler fork.
23 -export const enableDebugTracing = !__VARIANT__;
24 -export const enableSchedulingProfiler = !__VARIANT__ && __PROFILE__;
22 +// Enable this flag to help with concurrent mode debugging.
23 +// It logs information to the console about React scheduling, rendering, and commit phases.
24 +//
25 +// NOTE: This feature will only work in DEV mode; all callsights are wrapped with __DEV__.
26 +export const enableDebugTracing = false;
27 +
28 +// TODO: getStackByFiberInDevAndProd() causes errors when synced to www.
29 +// This flag can be used to disable component stacks for the profiler marks,
30 +// so that the feature can be synced for others,
31 +// while still enabling investigation into the underlying source of the errors.
32 +export const enableSchedulingProfilerComponentStacks = false;
33
34 // This only has an effect in the new reconciler. But also, the new reconciler
35 // is only enabled when __VARIANT__ is true. So this is set to the opposite of
packages/shared/forks/ReactFeatureFlags.www.js
+4 -1
@@ -26,7 +26,7 @@ export const {
26 deferRenderPhaseUpdateToNextBatch,
27 decoupleUpdatePriorityFromScheduler,
28 enableDebugTracing,
29 - enableSchedulingProfiler,
29 + enableSchedulingProfilerComponentStacks,
30 } = dynamicFeatureFlags;
31
32 // On WWW, __EXPERIMENTAL__ is used for a new modern build.
@@ -35,6 +35,9 @@ export const {
35 export const enableProfilerTimer = __PROFILE__;
36 export const enableProfilerCommitHooks = __PROFILE__;
37
38 +// Logs additional User Timing API marks for use with an experimental profiling tool.
39 +export const enableSchedulingProfiler = __PROFILE__;
40 +
41 // Note: we'll want to remove this when we to userland implementation.
42 // For now, we'll turn it on for everyone because it's *already* on for everyone in practice.
43 // At least this will let us stop shipping <Profiler> implementation to all users.