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.