From 227a65a8f04c92a0c63425dd383cb0b5e8917bd0 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 11 Feb 2021 18:02:24 -0500 Subject: [PATCH] Add Lane labels to scheduling profiler marks This changes profiler marks from a format like "--schedule-render-1" like "--schedule-render-1-Sync" which will enable the profiler itself to show more meaningful labels for updates and render work. There are a couple of downsides to this proposed change: 1. It reqiures maintaining a map of bitmask range to string/label in ReactFiberLanel. Hopefully not a huge burden but something we would need to remember to keep in sync. 2. It increases the size of User Timing data logged by the profiler during render. (I don't think there's a way around this since there's no initialization event where we can log a standalone mapping). Perhaps we could log a mapping with each commit or something but that doesn't seem like an obvious improvement. --- .../src/ReactFiberLane.new.js | 47 ++++++ .../src/ReactFiberLane.old.js | 47 ++++++ .../src/SchedulingProfiler.js | 22 ++- ...internal.js => SchedulingProfiler-test.js} | 149 ++++++++++-------- 4 files changed, 199 insertions(+), 66 deletions(-) rename packages/react-reconciler/src/__tests__/{SchedulingProfiler-test.internal.js => SchedulingProfiler-test.js} (70%) diff --git a/packages/react-reconciler/src/ReactFiberLane.new.js b/packages/react-reconciler/src/ReactFiberLane.new.js index e4280582e80cd..a1119043ea915 100644 --- a/packages/react-reconciler/src/ReactFiberLane.new.js +++ b/packages/react-reconciler/src/ReactFiberLane.new.js @@ -39,6 +39,7 @@ import invariant from 'shared/invariant'; import { enableCache, enableNonInterruptingNormalPri, + enableSchedulingProfiler, } from 'shared/ReactFeatureFlags'; import { @@ -127,6 +128,52 @@ const IdleLane: Lanes = /* */ 0b0100000000000000000 export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000; +export function getLabelsForLanes(lanes: Lanes): Array | void { + if (enableSchedulingProfiler) { + const labels = []; + if (lanes & SyncLane) { + labels.push('Sync'); + } + if (lanes & SyncBatchedLane) { + labels.push('SyncBatched'); + } + if (lanes & InputDiscreteHydrationLane) { + labels.push('InputDiscreteHydration'); + } + if (lanes & InputDiscreteLane) { + labels.push('InputDiscrete'); + } + if (lanes & DefaultHydrationLane) { + labels.push('DefaultHydration'); + } + if (lanes & DefaultLane) { + labels.push('Default'); + } + if (lanes & TransitionHydrationLane) { + labels.push('TransitionHydration'); + } + if (lanes & TransitionLanes) { + labels.push('Transition(s)'); + } + if (lanes & RetryLanes) { + labels.push('Retry(s)'); + } + if (lanes & SelectiveHydrationLane) { + labels.push('SelectiveHydration'); + } + if (lanes & IdleHydrationLane) { + labels.push('IdleHydration'); + } + if (lanes & IdleLane) { + labels.push('Idle'); + } + if (lanes & OffscreenLane) { + labels.push('Offscreen'); + } + return labels; + } +} + export const NoTimestamp = -1; let currentUpdateLanePriority: LanePriority = NoLanePriority; diff --git a/packages/react-reconciler/src/ReactFiberLane.old.js b/packages/react-reconciler/src/ReactFiberLane.old.js index fe7d6eb7508c8..a661ede429a38 100644 --- a/packages/react-reconciler/src/ReactFiberLane.old.js +++ b/packages/react-reconciler/src/ReactFiberLane.old.js @@ -39,6 +39,7 @@ import invariant from 'shared/invariant'; import { enableCache, enableNonInterruptingNormalPri, + enableSchedulingProfiler, } from 'shared/ReactFeatureFlags'; import { @@ -127,6 +128,52 @@ const IdleLane: Lanes = /* */ 0b0100000000000000000 export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000; +export function getLabelsForLanes(lanes: Lanes): Array | void { + if (enableSchedulingProfiler) { + const labels = []; + if (lanes & SyncLane) { + labels.push('Sync'); + } + if (lanes & SyncBatchedLane) { + labels.push('SyncBatched'); + } + if (lanes & InputDiscreteHydrationLane) { + labels.push('InputDiscreteHydration'); + } + if (lanes & InputDiscreteLane) { + labels.push('InputDiscrete'); + } + if (lanes & DefaultHydrationLane) { + labels.push('DefaultHydration'); + } + if (lanes & DefaultLane) { + labels.push('Default'); + } + if (lanes & TransitionHydrationLane) { + labels.push('TransitionHydration'); + } + if (lanes & TransitionLanes) { + labels.push('Transition(s)'); + } + if (lanes & RetryLanes) { + labels.push('Retry(s)'); + } + if (lanes & SelectiveHydrationLane) { + labels.push('SelectiveHydration'); + } + if (lanes & IdleHydrationLane) { + labels.push('IdleHydration'); + } + if (lanes & IdleLane) { + labels.push('Idle'); + } + if (lanes & OffscreenLane) { + labels.push('Offscreen'); + } + return labels; + } +} + export const NoTimestamp = -1; let currentUpdateLanePriority: LanePriority = NoLanePriority; diff --git a/packages/react-reconciler/src/SchedulingProfiler.js b/packages/react-reconciler/src/SchedulingProfiler.js index 0779b870a274b..ccc41f9854876 100644 --- a/packages/react-reconciler/src/SchedulingProfiler.js +++ b/packages/react-reconciler/src/SchedulingProfiler.js @@ -11,10 +11,20 @@ import type {Lane, Lanes} from './ReactFiberLane.old'; import type {Fiber} from './ReactInternalTypes'; import type {Wakeable} from 'shared/ReactTypes'; -import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags'; +import { + enableNewReconciler, + enableSchedulingProfiler, +} from 'shared/ReactFeatureFlags'; import ReactVersion from 'shared/ReactVersion'; import getComponentName from 'shared/getComponentName'; +import {getLabelsForLanes as getLabelsForLanes_old} from 'react-reconciler/src/ReactFiberLane.old'; +import {getLabelsForLanes as getLabelsForLanes_new} from 'react-reconciler/src/ReactFiberLane.new'; + +const getLabelsForLanes = enableNewReconciler + ? getLabelsForLanes_new + : getLabelsForLanes_old; + /** * If performance exists and supports the subset of the User Timing API that we * require. @@ -49,8 +59,14 @@ if (enableSchedulingProfiler) { } } -function formatLanes(laneOrLanes: Lane | Lanes): string { - return ((laneOrLanes: any): number).toString(); +export function formatLanes(laneOrLanes: Lane | Lanes): string { + let labels = getLabelsForLanes(laneOrLanes); + if (labels != null) { + labels = labels.sort().join(','); + } else { + labels = ''; + } + return `${laneOrLanes}-${labels}`; } function markAndClear(name) { diff --git a/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js b/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.js similarity index 70% rename from packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js rename to packages/react-reconciler/src/__tests__/SchedulingProfiler-test.js index dd3528da50fe1..10415906ba698 100644 --- a/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js +++ b/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.js @@ -17,9 +17,11 @@ describe('SchedulingProfiler', () => { let ReactTestRenderer; let ReactNoop; let Scheduler; + let ReactFiberLane; let clearedMarks; let featureDetectionMarkName = null; + let formatLanes; let marks; function createUserTimingPolyfill() { @@ -76,6 +78,14 @@ describe('SchedulingProfiler', () => { ReactNoop = require('react-noop-renderer'); Scheduler = require('scheduler'); + + const SchedulingProfiler = require('react-reconciler/src/SchedulingProfiler'); + formatLanes = SchedulingProfiler.formatLanes; + + const ReactFeatureFlags = require('shared/ReactFeatureFlags'); + ReactFiberLane = ReactFeatureFlags.enableNewReconciler + ? require('react-reconciler/src/ReactFiberLane.new') + : require('react-reconciler/src/ReactFiberLane.old'); }); afterEach(() => { @@ -85,9 +95,6 @@ describe('SchedulingProfiler', () => { delete global.performance; }); - // This is coupled to implementation - const DEFAULT_LANE = 128; - // @gate !enableSchedulingProfiler it('should not mark if enableSchedulingProfiler is false', () => { ReactTestRenderer.create(
); @@ -105,11 +112,11 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - '--schedule-render-1', - '--render-start-1', + `--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`, + `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--render-stop', - '--commit-start-1', - '--layout-effects-start-1', + `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`, + `--layout-effects-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--layout-effects-stop', '--commit-stop', ]); @@ -121,7 +128,7 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - `--schedule-render-${DEFAULT_LANE}`, + `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`, ]); clearPendingMarks(); @@ -129,10 +136,10 @@ describe('SchedulingProfiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); expectMarksToEqual([ - `--render-start-${DEFAULT_LANE}`, + `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--render-stop', - `--commit-start-${DEFAULT_LANE}`, - `--layout-effects-start-${DEFAULT_LANE}`, + `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--layout-effects-stop', '--commit-stop', ]); @@ -156,8 +163,8 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - `--schedule-render-${DEFAULT_LANE}`, - `--render-start-${DEFAULT_LANE}`, + `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--render-yield', ]); }); @@ -177,12 +184,12 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - '--schedule-render-1', - '--render-start-1', + `--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`, + `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--suspense-suspend-0-Example', '--render-stop', - '--commit-start-1', - '--layout-effects-start-1', + `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`, + `--layout-effects-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--layout-effects-stop', '--commit-stop', ]); @@ -208,12 +215,12 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - '--schedule-render-1', - '--render-start-1', + `--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`, + `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--suspense-suspend-0-Example', '--render-stop', - '--commit-start-1', - '--layout-effects-start-1', + `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`, + `--layout-effects-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--layout-effects-stop', '--commit-stop', ]); @@ -240,7 +247,7 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - `--schedule-render-${DEFAULT_LANE}`, + `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`, ]); clearPendingMarks(); @@ -248,11 +255,11 @@ describe('SchedulingProfiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); expectMarksToEqual([ - `--render-start-${DEFAULT_LANE}`, + `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--suspense-suspend-0-Example', '--render-stop', - `--commit-start-${DEFAULT_LANE}`, - `--layout-effects-start-${DEFAULT_LANE}`, + `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--layout-effects-stop', '--commit-stop', ]); @@ -279,7 +286,7 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - `--schedule-render-${DEFAULT_LANE}`, + `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`, ]); clearPendingMarks(); @@ -287,11 +294,11 @@ describe('SchedulingProfiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); expectMarksToEqual([ - `--render-start-${DEFAULT_LANE}`, + `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--suspense-suspend-0-Example', '--render-stop', - `--commit-start-${DEFAULT_LANE}`, - `--layout-effects-start-${DEFAULT_LANE}`, + `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--layout-effects-stop', '--commit-stop', ]); @@ -318,7 +325,7 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - `--schedule-render-${DEFAULT_LANE}`, + `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`, ]); clearPendingMarks(); @@ -326,15 +333,15 @@ describe('SchedulingProfiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); expectMarksToEqual([ - `--render-start-${DEFAULT_LANE}`, + `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--render-stop', - `--commit-start-${DEFAULT_LANE}`, - `--layout-effects-start-${DEFAULT_LANE}`, - '--schedule-state-update-1-Example', + `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--schedule-state-update-${formatLanes(ReactFiberLane.SyncLane)}-Example`, '--layout-effects-stop', - '--render-start-1', + `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--render-stop', - '--commit-start-1', + `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--commit-stop', '--commit-stop', ]); @@ -355,7 +362,7 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - `--schedule-render-${DEFAULT_LANE}`, + `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`, ]); clearPendingMarks(); @@ -363,15 +370,17 @@ describe('SchedulingProfiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); expectMarksToEqual([ - `--render-start-${DEFAULT_LANE}`, + `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--render-stop', - `--commit-start-${DEFAULT_LANE}`, - `--layout-effects-start-${DEFAULT_LANE}`, - '--schedule-forced-update-1-Example', + `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--schedule-forced-update-${formatLanes( + ReactFiberLane.SyncLane, + )}-Example`, '--layout-effects-stop', - '--render-start-1', + `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--render-stop', - '--commit-start-1', + `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--commit-stop', '--commit-stop', ]); @@ -393,7 +402,7 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - `--schedule-render-${DEFAULT_LANE}`, + `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`, ]); clearPendingMarks(); @@ -402,7 +411,11 @@ describe('SchedulingProfiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); }).toErrorDev('Cannot update during an existing state transition'); - expectMarksToContain(`--schedule-state-update-${DEFAULT_LANE}-Example`); + expectMarksToContain( + `--schedule-state-update-${formatLanes( + ReactFiberLane.DefaultLane, + )}-Example`, + ); }); // @gate enableSchedulingProfiler @@ -421,7 +434,7 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - `--schedule-render-${DEFAULT_LANE}`, + `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`, ]); clearPendingMarks(); @@ -430,7 +443,11 @@ describe('SchedulingProfiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); }).toErrorDev('Cannot update during an existing state transition'); - expectMarksToContain(`--schedule-forced-update-${DEFAULT_LANE}-Example`); + expectMarksToContain( + `--schedule-forced-update-${formatLanes( + ReactFiberLane.DefaultLane, + )}-Example`, + ); }); // @gate enableSchedulingProfiler @@ -447,7 +464,7 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - `--schedule-render-${DEFAULT_LANE}`, + `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`, ]); clearPendingMarks(); @@ -455,15 +472,15 @@ describe('SchedulingProfiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); expectMarksToEqual([ - `--render-start-${DEFAULT_LANE}`, + `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--render-stop', - `--commit-start-${DEFAULT_LANE}`, - `--layout-effects-start-${DEFAULT_LANE}`, - '--schedule-state-update-1-Example', + `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--schedule-state-update-${formatLanes(ReactFiberLane.SyncLane)}-Example`, '--layout-effects-stop', - '--render-start-1', + `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--render-stop', - '--commit-start-1', + `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`, '--commit-stop', '--commit-stop', ]); @@ -487,19 +504,21 @@ describe('SchedulingProfiler', () => { expectMarksToEqual([ `--react-init-${ReactVersion}`, - `--schedule-render-${DEFAULT_LANE}`, - `--render-start-${DEFAULT_LANE}`, + `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--render-stop', - `--commit-start-${DEFAULT_LANE}`, - `--layout-effects-start-${DEFAULT_LANE}`, + `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--layout-effects-stop', '--commit-stop', - `--passive-effects-start-${DEFAULT_LANE}`, - `--schedule-state-update-${DEFAULT_LANE}-Example`, + `--passive-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`, + `--schedule-state-update-${formatLanes( + ReactFiberLane.DefaultLane, + )}-Example`, '--passive-effects-stop', - `--render-start-${DEFAULT_LANE}`, + `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--render-stop', - `--commit-start-${DEFAULT_LANE}`, + `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`, '--commit-stop', ]); }); @@ -518,6 +537,10 @@ describe('SchedulingProfiler', () => { ReactTestRenderer.create(, {unstable_isConcurrent: true}); }); - expectMarksToContain(`--schedule-state-update-${DEFAULT_LANE}-Example`); + expectMarksToContain( + `--schedule-state-update-${formatLanes( + ReactFiberLane.DefaultLane, + )}-Example`, + ); }); });