diff --git a/packages/react-devtools-scheduling-profiler/src/constants.js b/packages/react-devtools-scheduling-profiler/src/constants.js
index 2e463ae632d95..e29b8901d81fd 100644
--- a/packages/react-devtools-scheduling-profiler/src/constants.js
+++ b/packages/react-devtools-scheduling-profiler/src/constants.js
@@ -13,3 +13,6 @@ export {
} from 'react-devtools-shared/src/constants.js';
export const REACT_TOTAL_NUM_LANES = 31;
+
+// Increment this number any time a backwards breaking change is made to the profiler metadata.
+export const SCHEDULING_PROFILER_VERSION = 1;
diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/ComponentMeasuresView.js b/packages/react-devtools-scheduling-profiler/src/content-views/ComponentMeasuresView.js
index 4d9ecfa5d17ee..c7de8f8897c5e 100644
--- a/packages/react-devtools-scheduling-profiler/src/content-views/ComponentMeasuresView.js
+++ b/packages/react-devtools-scheduling-profiler/src/content-views/ComponentMeasuresView.js
@@ -161,8 +161,7 @@ export class ComponentMeasuresView extends View {
context,
visibleArea,
visibleArea,
- 'center',
- COLORS.TEXT_DIM_COLOR,
+ {fillStyle: COLORS.TEXT_DIM_COLOR, textAlign: 'center'},
);
}
diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/ReactMeasuresView.js b/packages/react-devtools-scheduling-profiler/src/content-views/ReactMeasuresView.js
index f8e4cb99b94c1..976e37ebb9d39 100644
--- a/packages/react-devtools-scheduling-profiler/src/content-views/ReactMeasuresView.js
+++ b/packages/react-devtools-scheduling-profiler/src/content-views/ReactMeasuresView.js
@@ -16,6 +16,8 @@ import type {
ViewRefs,
} from '../view-base';
+import {formatDuration} from '../utils/formatting';
+import {drawText} from './utils/text';
import {
durationToWidth,
positioningScaleFactor,
@@ -102,17 +104,19 @@ export class ReactMeasuresView extends View {
context: CanvasRenderingContext2D,
rect: Rect,
measure: ReactMeasure,
+ nextMeasure: ReactMeasure | null,
baseY: number,
scaleFactor: number,
showGroupHighlight: boolean,
showHoverHighlight: boolean,
) {
- const {frame} = this;
+ const {frame, visibleArea} = this;
const {timestamp, type, duration} = measure;
let fillStyle = null;
let hoveredFillStyle = null;
let groupSelectedFillStyle = null;
+ let textFillStyle = null;
// We could change the max to 0 and just skip over rendering anything that small,
// but this has the effect of making the chart look very empty when zoomed out.
@@ -131,11 +135,29 @@ export class ReactMeasuresView extends View {
return; // Not in view
}
+ const drawableRect = intersectionOfRects(measureRect, rect);
+ let textRect = measureRect;
+
switch (type) {
case 'commit':
fillStyle = COLORS.REACT_COMMIT;
hoveredFillStyle = COLORS.REACT_COMMIT_HOVER;
groupSelectedFillStyle = COLORS.REACT_COMMIT_HOVER;
+ textFillStyle = COLORS.REACT_COMMIT_TEXT;
+
+ // Commit phase rects are overlapped by layout and passive rects,
+ // and it looks bad if text flows underneath/behind these overlayed rects.
+ if (nextMeasure != null) {
+ textRect = {
+ ...measureRect,
+ size: {
+ width:
+ timestampToPosition(nextMeasure.timestamp, scaleFactor, frame) -
+ x,
+ height: REACT_MEASURE_HEIGHT,
+ },
+ };
+ }
break;
case 'render-idle':
// We could render idle time as diagonal hashes.
@@ -149,22 +171,24 @@ export class ReactMeasuresView extends View {
fillStyle = COLORS.REACT_RENDER;
hoveredFillStyle = COLORS.REACT_RENDER_HOVER;
groupSelectedFillStyle = COLORS.REACT_RENDER_HOVER;
+ textFillStyle = COLORS.REACT_RENDER_TEXT;
break;
case 'layout-effects':
fillStyle = COLORS.REACT_LAYOUT_EFFECTS;
hoveredFillStyle = COLORS.REACT_LAYOUT_EFFECTS_HOVER;
groupSelectedFillStyle = COLORS.REACT_LAYOUT_EFFECTS_HOVER;
+ textFillStyle = COLORS.REACT_LAYOUT_EFFECTS_TEXT;
break;
case 'passive-effects':
fillStyle = COLORS.REACT_PASSIVE_EFFECTS;
hoveredFillStyle = COLORS.REACT_PASSIVE_EFFECTS_HOVER;
groupSelectedFillStyle = COLORS.REACT_PASSIVE_EFFECTS_HOVER;
+ textFillStyle = COLORS.REACT_PASSIVE_EFFECTS_TEXT;
break;
default:
throw new Error(`Unexpected measure type "${type}"`);
}
- const drawableRect = intersectionOfRects(measureRect, rect);
context.fillStyle = showHoverHighlight
? hoveredFillStyle
: showGroupHighlight
@@ -176,6 +200,12 @@ export class ReactMeasuresView extends View {
drawableRect.size.width,
drawableRect.size.height,
);
+
+ if (textFillStyle !== null) {
+ drawText(formatDuration(duration), context, textRect, visibleArea, {
+ fillStyle: textFillStyle,
+ });
+ }
}
draw(context: CanvasRenderingContext2D) {
@@ -211,6 +241,27 @@ export class ReactMeasuresView extends View {
);
}
+ // Render lane labels
+ const label = this._profilerData.laneToLabelMap.get(lane);
+ if (label == null) {
+ console.warn(`Could not find label for lane ${lane}.`);
+ } else {
+ const labelRect = {
+ origin: {
+ x: visibleArea.origin.x,
+ y: baseY,
+ },
+ size: {
+ width: visibleArea.size.width,
+ height: REACT_LANE_HEIGHT,
+ },
+ };
+
+ drawText(label, context, labelRect, visibleArea, {
+ fillStyle: COLORS.TEXT_DIM_COLOR,
+ });
+ }
+
// Draw measures
for (let j = 0; j < measuresForLane.length; j++) {
const measure = measuresForLane[j];
@@ -222,6 +273,7 @@ export class ReactMeasuresView extends View {
context,
visibleArea,
measure,
+ measuresForLane[j + 1] || null,
baseY,
scaleFactor,
showGroupHighlight,
diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/constants.js b/packages/react-devtools-scheduling-profiler/src/content-views/constants.js
index ec8fec8d1e461..6cda416ad8bb3 100644
--- a/packages/react-devtools-scheduling-profiler/src/content-views/constants.js
+++ b/packages/react-devtools-scheduling-profiler/src/content-views/constants.js
@@ -19,7 +19,7 @@ export const SUSPENSE_EVENT_HEIGHT = 14;
export const PENDING_SUSPENSE_EVENT_SIZE = 8;
export const REACT_EVENT_DIAMETER = 6;
export const USER_TIMING_MARK_SIZE = 8;
-export const REACT_MEASURE_HEIGHT = 9;
+export const REACT_MEASURE_HEIGHT = 14;
export const BORDER_SIZE = 1;
export const FLAMECHART_FRAME_HEIGHT = 14;
export const TEXT_PADDING = 3;
@@ -56,12 +56,16 @@ export let COLORS = {
REACT_IDLE_HOVER: '',
REACT_RENDER: '',
REACT_RENDER_HOVER: '',
+ REACT_RENDER_TEXT: '',
REACT_COMMIT: '',
REACT_COMMIT_HOVER: '',
+ REACT_COMMIT_TEXT: '',
REACT_LAYOUT_EFFECTS: '',
REACT_LAYOUT_EFFECTS_HOVER: '',
+ REACT_LAYOUT_EFFECTS_TEXT: '',
REACT_PASSIVE_EFFECTS: '',
REACT_PASSIVE_EFFECTS_HOVER: '',
+ REACT_PASSIVE_EFFECTS_TEXT: '',
REACT_RESIZE_BAR: '',
REACT_RESIZE_BAR_ACTIVE: '',
REACT_RESIZE_BAR_BORDER: '',
@@ -132,24 +136,36 @@ export function updateColorsToMatchTheme(element: Element): boolean {
REACT_RENDER_HOVER: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-render-hover',
),
+ REACT_RENDER_TEXT: computedStyle.getPropertyValue(
+ '--color-scheduling-profiler-react-render-text',
+ ),
REACT_COMMIT: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-commit',
),
REACT_COMMIT_HOVER: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-commit-hover',
),
+ REACT_COMMIT_TEXT: computedStyle.getPropertyValue(
+ '--color-scheduling-profiler-react-commit-text',
+ ),
REACT_LAYOUT_EFFECTS: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-layout-effects',
),
REACT_LAYOUT_EFFECTS_HOVER: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-layout-effects-hover',
),
+ REACT_LAYOUT_EFFECTS_TEXT: computedStyle.getPropertyValue(
+ '--color-scheduling-profiler-react-layout-effects-text',
+ ),
REACT_PASSIVE_EFFECTS: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-passive-effects',
),
REACT_PASSIVE_EFFECTS_HOVER: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-passive-effects-hover',
),
+ REACT_PASSIVE_EFFECTS_TEXT: computedStyle.getPropertyValue(
+ '--color-scheduling-profiler-react-passive-effects-text',
+ ),
REACT_RESIZE_BAR: computedStyle.getPropertyValue('--color-resize-bar'),
REACT_RESIZE_BAR_ACTIVE: computedStyle.getPropertyValue(
'--color-resize-bar-active',
diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/utils/text.js b/packages/react-devtools-scheduling-profiler/src/content-views/utils/text.js
index 229b9f50f8dcd..4cd7d94821589 100644
--- a/packages/react-devtools-scheduling-profiler/src/content-views/utils/text.js
+++ b/packages/react-devtools-scheduling-profiler/src/content-views/utils/text.js
@@ -14,6 +14,19 @@ import {COLORS, FONT_SIZE, TEXT_PADDING} from '../constants';
const cachedTextWidths = new Map();
+export function getTextWidth(
+ context: CanvasRenderingContext2D,
+ text: string,
+): number {
+ let measuredWidth = cachedTextWidths.get(text);
+ if (measuredWidth == null) {
+ measuredWidth = context.measureText(text).width;
+ cachedTextWidths.set(text, measuredWidth);
+ }
+
+ return ((measuredWidth: any): number);
+}
+
export function trimText(
context: CanvasRenderingContext2D,
text: string,
@@ -21,14 +34,7 @@ export function trimText(
): string | null {
for (let i = text.length - 1; i >= 0; i--) {
const trimmedText = i === text.length - 1 ? text : text.substr(0, i) + '…';
-
- let measuredWidth = cachedTextWidths.get(trimmedText);
- if (measuredWidth == null) {
- measuredWidth = context.measureText(trimmedText).width;
- cachedTextWidths.set(trimmedText, measuredWidth);
- }
-
- if (measuredWidth <= width) {
+ if (getTextWidth(context, trimmedText) <= width) {
return trimmedText;
}
}
@@ -36,18 +42,29 @@ export function trimText(
return null;
}
+type TextConfig = {|
+ fillStyle?: string,
+ fontSize?: number,
+ textAlign?: 'left' | 'center',
+|};
+
export function drawText(
text: string,
context: CanvasRenderingContext2D,
fullRect: Rect,
drawableRect: Rect,
- textAlign: 'left' | 'center' = 'left',
- fillStyle: string = COLORS.TEXT_COLOR,
+ config?: TextConfig,
): void {
+ const {
+ fillStyle = COLORS.TEXT_COLOR,
+ fontSize = FONT_SIZE,
+ textAlign = 'left',
+ } = config || {};
+
if (fullRect.size.width > TEXT_PADDING * 2) {
context.textAlign = textAlign;
context.textBaseline = 'middle';
- context.font = `${FONT_SIZE}px sans-serif`;
+ context.font = `${fontSize}px sans-serif`;
const {x, y} = fullRect.origin;
diff --git a/packages/react-devtools-scheduling-profiler/src/import-worker/__tests__/preprocessData-test.internal.js b/packages/react-devtools-scheduling-profiler/src/import-worker/__tests__/preprocessData-test.internal.js
index f47200c6f0e21..f08dcd2512b1e 100644
--- a/packages/react-devtools-scheduling-profiler/src/import-worker/__tests__/preprocessData-test.internal.js
+++ b/packages/react-devtools-scheduling-profiler/src/import-worker/__tests__/preprocessData-test.internal.js
@@ -7,10 +7,15 @@
'use strict';
+import {getLaneLabels} from 'react-reconciler/src/SchedulingProfiler';
import preprocessData, {
getLanesFromTransportDecimalBitmask,
} from '../preprocessData';
-import {REACT_TOTAL_NUM_LANES} from '../../constants';
+import {
+ REACT_TOTAL_NUM_LANES,
+ SCHEDULING_PROFILER_VERSION,
+} from '../../constants';
+import REACT_VERSION from 'shared/ReactVersion';
describe(getLanesFromTransportDecimalBitmask, () => {
it('should return array of lane numbers from bitmask string', () => {
@@ -109,6 +114,35 @@ describe(preprocessData, () => {
};
}
+ function createProfilerVersionEntry() {
+ return createUserTimingEntry({
+ cat: 'blink.user_timing',
+ name: '--profiler-version-' + SCHEDULING_PROFILER_VERSION,
+ });
+ }
+
+ function createReactVersionEntry() {
+ return createUserTimingEntry({
+ cat: 'blink.user_timing',
+ name: '--react-version-' + REACT_VERSION,
+ });
+ }
+
+ function createLaneLabelsEntry() {
+ return createUserTimingEntry({
+ cat: 'blink.user_timing',
+ name: '--react-lane-labels-' + getLaneLabels().join(','),
+ });
+ }
+
+ function createBoilerplateEntries() {
+ return [
+ createProfilerVersionEntry(),
+ createReactVersionEntry(),
+ createLaneLabelsEntry(),
+ ];
+ }
+
function createUserTimingData(sampleMarks) {
const cpuProfilerSample = createUserTimingEntry({
args: {data: {startTime: ++startTime}},
@@ -192,7 +226,6 @@ describe(preprocessData, () => {
name: 'Profile',
ph: 'P',
});
-
const randomSample = createUserTimingEntry({
dur: 100,
tdur: 200,
@@ -202,20 +235,62 @@ describe(preprocessData, () => {
args: {},
});
- expect(preprocessData([cpuProfilerSample, randomSample])).toStrictEqual({
- componentMeasures: [],
- duration: 0.002,
- flamechart: [],
- measures: [],
- nativeEvents: [],
- otherUserTimingMarks: [],
- schedulingEvents: [],
- startTime: 1,
- suspenseEvents: [],
- });
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ const data = preprocessData([
+ ...createBoilerplateEntries(),
+ cpuProfilerSample,
+ randomSample,
+ ]);
+ expect(data).toMatchInlineSnapshot(`
+ Object {
+ "componentMeasures": Array [],
+ "duration": 0.005,
+ "flamechart": Array [],
+ "laneToLabelMap": Map {
+ 0 => "Sync",
+ 1 => "InputContinuousHydration",
+ 2 => "InputContinuous",
+ 3 => "DefaultHydration",
+ 4 => "Default",
+ 5 => "TransitionHydration",
+ 6 => "Transition",
+ 7 => "Transition",
+ 8 => "Transition",
+ 9 => "Transition",
+ 10 => "Transition",
+ 11 => "Transition",
+ 12 => "Transition",
+ 13 => "Transition",
+ 14 => "Transition",
+ 15 => "Transition",
+ 16 => "Transition",
+ 17 => "Transition",
+ 18 => "Transition",
+ 19 => "Transition",
+ 20 => "Transition",
+ 21 => "Transition",
+ 22 => "Retry",
+ 23 => "Retry",
+ 24 => "Retry",
+ 25 => "Retry",
+ 26 => "Retry",
+ 27 => "SelectiveHydration",
+ 28 => "IdleHydration",
+ 29 => "Idle",
+ 30 => "Offscreen",
+ },
+ "measures": Array [],
+ "nativeEvents": Array [],
+ "otherUserTimingMarks": Array [],
+ "reactVersion": "17.0.3",
+ "schedulingEvents": Array [],
+ "startTime": 1,
+ "suspenseEvents": Array [],
+ }
+ `);
+ }
});
- // NOTE This test doesn't have to be gated because it has hard-coded profiler samples.
it('should process legacy data format (before lane labels were added)', () => {
const cpuProfilerSample = createUserTimingEntry({
args: {data: {startTime: ++startTime}},
@@ -225,10 +300,11 @@ describe(preprocessData, () => {
ph: 'P',
});
- expect(
+ if (gate(flags => flags.enableSchedulingProfiler)) {
// Data below is hard-coded based on an older profile sample.
// Should be fine since this is explicitly a legacy-format test.
- preprocessData([
+ const data = preprocessData([
+ ...createBoilerplateEntries(),
cpuProfilerSample,
createUserTimingEntry({
cat: 'blink.user_timing',
@@ -258,140 +334,220 @@ describe(preprocessData, () => {
cat: 'blink.user_timing',
name: '--commit-stop',
}),
- ]),
- ).toStrictEqual({
- componentMeasures: [],
- duration: 0.008,
- flamechart: [],
- measures: [
- {
- batchUID: 0,
- depth: 0,
- duration: 0.005,
- laneLabels: [],
- lanes: [9],
- timestamp: 0.003,
- type: 'render-idle',
- },
- {
- batchUID: 0,
- depth: 0,
- duration: 0.001,
- laneLabels: [],
- lanes: [9],
- timestamp: 0.003,
- type: 'render',
- },
- {
- batchUID: 0,
- depth: 0,
- duration: 0.003,
- laneLabels: [],
- lanes: [9],
- timestamp: 0.005,
- type: 'commit',
+ ]);
+ expect(data).toMatchInlineSnapshot(`
+ Object {
+ "componentMeasures": Array [],
+ "duration": 0.011,
+ "flamechart": Array [],
+ "laneToLabelMap": Map {
+ 0 => "Sync",
+ 1 => "InputContinuousHydration",
+ 2 => "InputContinuous",
+ 3 => "DefaultHydration",
+ 4 => "Default",
+ 5 => "TransitionHydration",
+ 6 => "Transition",
+ 7 => "Transition",
+ 8 => "Transition",
+ 9 => "Transition",
+ 10 => "Transition",
+ 11 => "Transition",
+ 12 => "Transition",
+ 13 => "Transition",
+ 14 => "Transition",
+ 15 => "Transition",
+ 16 => "Transition",
+ 17 => "Transition",
+ 18 => "Transition",
+ 19 => "Transition",
+ 20 => "Transition",
+ 21 => "Transition",
+ 22 => "Retry",
+ 23 => "Retry",
+ 24 => "Retry",
+ 25 => "Retry",
+ 26 => "Retry",
+ 27 => "SelectiveHydration",
+ 28 => "IdleHydration",
+ 29 => "Idle",
+ 30 => "Offscreen",
},
- {
- batchUID: 0,
- depth: 1,
- duration: 0.001,
- laneLabels: [],
- lanes: [9],
- timestamp: 0.006,
- type: 'layout-effects',
- },
- ],
- nativeEvents: [],
- otherUserTimingMarks: [],
- schedulingEvents: [
- {
- laneLabels: [],
- lanes: [9],
- timestamp: 0.002,
- type: 'schedule-render',
- warning: null,
- },
- ],
- startTime: 1,
- suspenseEvents: [],
- });
+ "measures": Array [
+ Object {
+ "batchUID": 0,
+ "depth": 0,
+ "duration": 0.004999999999999999,
+ "lanes": Array [
+ 9,
+ ],
+ "timestamp": 0.006,
+ "type": "render-idle",
+ },
+ Object {
+ "batchUID": 0,
+ "depth": 0,
+ "duration": 0.001,
+ "lanes": Array [
+ 9,
+ ],
+ "timestamp": 0.006,
+ "type": "render",
+ },
+ Object {
+ "batchUID": 0,
+ "depth": 0,
+ "duration": 0.002999999999999999,
+ "lanes": Array [
+ 9,
+ ],
+ "timestamp": 0.008,
+ "type": "commit",
+ },
+ Object {
+ "batchUID": 0,
+ "depth": 1,
+ "duration": 0.0010000000000000009,
+ "lanes": Array [
+ 9,
+ ],
+ "timestamp": 0.009,
+ "type": "layout-effects",
+ },
+ ],
+ "nativeEvents": Array [],
+ "otherUserTimingMarks": Array [],
+ "reactVersion": "17.0.3",
+ "schedulingEvents": Array [
+ Object {
+ "lanes": Array [
+ 9,
+ ],
+ "timestamp": 0.005,
+ "type": "schedule-render",
+ "warning": null,
+ },
+ ],
+ "startTime": 1,
+ "suspenseEvents": Array [],
+ }
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should process a sample legacy render sequence', () => {
ReactDOM.render(
, document.createElement('div'));
- const reactVersion = require('shared/ReactVersion').default;
-
- const userTimingData = createUserTimingData(clearedMarks);
- expect(preprocessData(userTimingData)).toStrictEqual({
- componentMeasures: [],
- duration: 0.011,
- flamechart: [],
- measures: [
- {
- batchUID: 0,
- depth: 0,
- duration: 0.004999999999999999,
- laneLabels: ['Sync'],
- lanes: [0],
- timestamp: 0.006,
- type: 'render-idle',
- },
- {
- batchUID: 0,
- depth: 0,
- duration: 0.001,
- laneLabels: ['Sync'],
- lanes: [0],
- timestamp: 0.006,
- type: 'render',
- },
- {
- batchUID: 0,
- depth: 0,
- duration: 0.002999999999999999,
- laneLabels: ['Sync'],
- lanes: [0],
- timestamp: 0.008,
- type: 'commit',
- },
- {
- batchUID: 0,
- depth: 1,
- duration: 0.0010000000000000009,
- laneLabels: ['Sync'],
- lanes: [0],
- timestamp: 0.009,
- type: 'layout-effects',
- },
- ],
- nativeEvents: [],
- otherUserTimingMarks: [
- {
- name: '__v3',
- timestamp: 0.003,
- },
- {
- name: `--react-init-${reactVersion}`,
- timestamp: 0.004,
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ const data = preprocessData([
+ ...createBoilerplateEntries(),
+ ...createUserTimingData(clearedMarks),
+ ]);
+ expect(data).toMatchInlineSnapshot(`
+ Object {
+ "componentMeasures": Array [],
+ "duration": 0.013,
+ "flamechart": Array [],
+ "laneToLabelMap": Map {
+ 0 => "Sync",
+ 1 => "InputContinuousHydration",
+ 2 => "InputContinuous",
+ 3 => "DefaultHydration",
+ 4 => "Default",
+ 5 => "TransitionHydration",
+ 6 => "Transition",
+ 7 => "Transition",
+ 8 => "Transition",
+ 9 => "Transition",
+ 10 => "Transition",
+ 11 => "Transition",
+ 12 => "Transition",
+ 13 => "Transition",
+ 14 => "Transition",
+ 15 => "Transition",
+ 16 => "Transition",
+ 17 => "Transition",
+ 18 => "Transition",
+ 19 => "Transition",
+ 20 => "Transition",
+ 21 => "Transition",
+ 22 => "Retry",
+ 23 => "Retry",
+ 24 => "Retry",
+ 25 => "Retry",
+ 26 => "Retry",
+ 27 => "SelectiveHydration",
+ 28 => "IdleHydration",
+ 29 => "Idle",
+ 30 => "Offscreen",
},
- ],
- schedulingEvents: [
- {
- laneLabels: ['Sync'],
- lanes: [0],
- timestamp: 0.005,
- type: 'schedule-render',
- warning: null,
- },
- ],
- startTime: 1,
- suspenseEvents: [],
- });
+ "measures": Array [
+ Object {
+ "batchUID": 0,
+ "depth": 0,
+ "duration": 0.008,
+ "lanes": Array [
+ 0,
+ ],
+ "timestamp": 0.005,
+ "type": "render-idle",
+ },
+ Object {
+ "batchUID": 0,
+ "depth": 0,
+ "duration": 0.001,
+ "lanes": Array [
+ 0,
+ ],
+ "timestamp": 0.005,
+ "type": "render",
+ },
+ Object {
+ "batchUID": 0,
+ "depth": 0,
+ "duration": 0.005999999999999999,
+ "lanes": Array [
+ 0,
+ ],
+ "timestamp": 0.007,
+ "type": "commit",
+ },
+ Object {
+ "batchUID": 0,
+ "depth": 1,
+ "duration": 0.0010000000000000009,
+ "lanes": Array [
+ 0,
+ ],
+ "timestamp": 0.011,
+ "type": "layout-effects",
+ },
+ ],
+ "nativeEvents": Array [],
+ "otherUserTimingMarks": Array [
+ Object {
+ "name": "__v3",
+ "timestamp": 0.003,
+ },
+ ],
+ "reactVersion": "17.0.3",
+ "schedulingEvents": Array [
+ Object {
+ "lanes": Array [
+ 0,
+ ],
+ "timestamp": 0.004,
+ "type": "schedule-render",
+ "warning": null,
+ },
+ ],
+ "startTime": 4,
+ "suspenseEvents": Array [],
+ }
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should process a sample createRoot render sequence', () => {
function App() {
const [didMount, setDidMount] = React.useState(false);
@@ -403,150 +559,199 @@ describe(preprocessData, () => {
return true;
}
- const root = ReactDOM.createRoot(document.createElement('div'));
- act(() => root.render(
));
-
- const userTimingData = createUserTimingData(clearedMarks);
- expect(preprocessData(userTimingData)).toStrictEqual({
- componentMeasures: [
- {
- componentName: 'App',
- duration: 0.001,
- timestamp: 0.007,
- warning: null,
- },
- {
- componentName: 'App',
- duration: 0.0010000000000000009,
- timestamp: 0.018,
- warning: null,
- },
- ],
- duration: 0.026,
- flamechart: [],
- measures: [
- {
- batchUID: 0,
- depth: 0,
- duration: 0.006999999999999999,
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.006,
- type: 'render-idle',
- },
- {
- batchUID: 0,
- depth: 0,
- duration: 0.002999999999999999,
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.006,
- type: 'render',
- },
- {
- batchUID: 0,
- depth: 0,
- duration: 0.002999999999999999,
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.01,
- type: 'commit',
- },
- {
- batchUID: 0,
- depth: 1,
- duration: 0.0010000000000000009,
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.011,
- type: 'layout-effects',
- },
- {
- batchUID: 0,
- depth: 0,
- duration: 0.002,
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.014,
- type: 'passive-effects',
- },
- {
- batchUID: 1,
- depth: 0,
- duration: 0.006999999999999999,
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.017,
- type: 'render-idle',
- },
- {
- batchUID: 1,
- depth: 0,
- duration: 0.002999999999999999,
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.017,
- type: 'render',
- },
- {
- batchUID: 1,
- depth: 0,
- duration: 0.002999999999999999,
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.021,
- type: 'commit',
- },
- {
- batchUID: 1,
- depth: 1,
- duration: 0.0010000000000000009,
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.022,
- type: 'layout-effects',
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ const root = ReactDOM.createRoot(document.createElement('div'));
+ act(() => root.render(
));
+
+ const data = preprocessData([
+ ...createBoilerplateEntries(),
+ ...createUserTimingData(clearedMarks),
+ ]);
+ expect(data).toMatchInlineSnapshot(`
+ Object {
+ "componentMeasures": Array [
+ Object {
+ "componentName": "App",
+ "duration": 0.001,
+ "timestamp": 0.006,
+ "warning": null,
+ },
+ Object {
+ "componentName": "App",
+ "duration": 0.0010000000000000009,
+ "timestamp": 0.02,
+ "warning": null,
+ },
+ ],
+ "duration": 0.031,
+ "flamechart": Array [],
+ "laneToLabelMap": Map {
+ 0 => "Sync",
+ 1 => "InputContinuousHydration",
+ 2 => "InputContinuous",
+ 3 => "DefaultHydration",
+ 4 => "Default",
+ 5 => "TransitionHydration",
+ 6 => "Transition",
+ 7 => "Transition",
+ 8 => "Transition",
+ 9 => "Transition",
+ 10 => "Transition",
+ 11 => "Transition",
+ 12 => "Transition",
+ 13 => "Transition",
+ 14 => "Transition",
+ 15 => "Transition",
+ 16 => "Transition",
+ 17 => "Transition",
+ 18 => "Transition",
+ 19 => "Transition",
+ 20 => "Transition",
+ 21 => "Transition",
+ 22 => "Retry",
+ 23 => "Retry",
+ 24 => "Retry",
+ 25 => "Retry",
+ 26 => "Retry",
+ 27 => "SelectiveHydration",
+ 28 => "IdleHydration",
+ 29 => "Idle",
+ 30 => "Offscreen",
},
- {
- batchUID: 1,
- depth: 0,
- duration: 0.0009999999999999974,
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.025,
- type: 'passive-effects',
- },
- ],
- nativeEvents: [],
- otherUserTimingMarks: [
- {
- name: '__v3',
- timestamp: 0.003,
- },
- {
- name: '--react-init-17.0.3',
- timestamp: 0.004,
- },
- ],
- schedulingEvents: [
- {
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.005,
- type: 'schedule-render',
- warning: null,
- },
- {
- componentName: 'App',
- laneLabels: ['Default'],
- lanes: [4],
- timestamp: 0.015,
- type: 'schedule-state-update',
- warning: null,
- },
- ],
- startTime: 1,
- suspenseEvents: [],
- });
+ "measures": Array [
+ Object {
+ "batchUID": 0,
+ "depth": 0,
+ "duration": 0.009999999999999998,
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.005,
+ "type": "render-idle",
+ },
+ Object {
+ "batchUID": 0,
+ "depth": 0,
+ "duration": 0.003,
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.005,
+ "type": "render",
+ },
+ Object {
+ "batchUID": 0,
+ "depth": 0,
+ "duration": 0.006,
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.009,
+ "type": "commit",
+ },
+ Object {
+ "batchUID": 0,
+ "depth": 1,
+ "duration": 0.0010000000000000009,
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.013,
+ "type": "layout-effects",
+ },
+ Object {
+ "batchUID": 0,
+ "depth": 0,
+ "duration": 0.0019999999999999983,
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.016,
+ "type": "passive-effects",
+ },
+ Object {
+ "batchUID": 1,
+ "depth": 0,
+ "duration": 0.010000000000000002,
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.019,
+ "type": "render-idle",
+ },
+ Object {
+ "batchUID": 1,
+ "depth": 0,
+ "duration": 0.002999999999999999,
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.019,
+ "type": "render",
+ },
+ Object {
+ "batchUID": 1,
+ "depth": 0,
+ "duration": 0.006000000000000002,
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.023,
+ "type": "commit",
+ },
+ Object {
+ "batchUID": 1,
+ "depth": 1,
+ "duration": 0.0010000000000000009,
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.027,
+ "type": "layout-effects",
+ },
+ Object {
+ "batchUID": 1,
+ "depth": 0,
+ "duration": 0.0010000000000000009,
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.03,
+ "type": "passive-effects",
+ },
+ ],
+ "nativeEvents": Array [],
+ "otherUserTimingMarks": Array [
+ Object {
+ "name": "__v3",
+ "timestamp": 0.003,
+ },
+ ],
+ "reactVersion": "17.0.3",
+ "schedulingEvents": Array [
+ Object {
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.004,
+ "type": "schedule-render",
+ "warning": null,
+ },
+ Object {
+ "componentName": "App",
+ "lanes": Array [
+ 4,
+ ],
+ "timestamp": 0.017,
+ "type": "schedule-state-update",
+ "warning": null,
+ },
+ ],
+ "startTime": 4,
+ "suspenseEvents": Array [],
+ }
+ `);
+ }
});
// @gate enableSchedulingProfiler
@@ -560,7 +765,7 @@ describe(preprocessData, () => {
const invalidUserTimingData = createUserTimingData(invalidMarks);
const error = spyOnDevAndProd(console, 'error');
- preprocessData(invalidUserTimingData);
+ preprocessData([...createBoilerplateEntries(), ...invalidUserTimingData]);
expect(error).toHaveBeenCalled();
});
@@ -575,7 +780,7 @@ describe(preprocessData, () => {
const invalidUserTimingData = createUserTimingData(invalidMarks);
const error = spyOnDevAndProd(console, 'error');
- preprocessData(invalidUserTimingData);
+ preprocessData([...createBoilerplateEntries(), ...invalidUserTimingData]);
expect(error).toHaveBeenCalled();
});
@@ -606,20 +811,26 @@ describe(preprocessData, () => {
}),
);
- expect(preprocessData(userTimingData).otherUserTimingMarks).toStrictEqual([
- {
- name: 'VCWithoutImage: root',
- timestamp: 0.003,
- },
- {
- name: '--a-mark-that-looks-like-one-of-ours',
- timestamp: 0.004,
- },
- {
- name: 'Some other mark',
- timestamp: 0.005,
- },
+ const data = preprocessData([
+ ...createBoilerplateEntries(),
+ ...userTimingData,
]);
+ expect(data.otherUserTimingMarks).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "name": "VCWithoutImage: root",
+ "timestamp": 0.003,
+ },
+ Object {
+ "name": "--a-mark-that-looks-like-one-of-ours",
+ "timestamp": 0.004,
+ },
+ Object {
+ "name": "Some other mark",
+ "timestamp": 0.005,
+ },
+ ]
+ `);
});
// TODO: Add test for flamechart parsing
diff --git a/packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js b/packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js
index d3d47e111bcaa..36beebac0b137 100644
--- a/packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js
+++ b/packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js
@@ -17,6 +17,7 @@ import type {
BatchUID,
Flamechart,
NativeEvent,
+ Phase,
ReactLane,
ReactComponentMeasure,
ReactMeasureType,
@@ -24,7 +25,7 @@ import type {
SuspenseEvent,
} from '../types';
-import {REACT_TOTAL_NUM_LANES} from '../constants';
+import {REACT_TOTAL_NUM_LANES, SCHEDULING_PROFILER_VERSION} from '../constants';
import InvalidProfileError from './InvalidProfileError';
type MeasureStackElement = {|
@@ -52,7 +53,7 @@ const WARNING_STRINGS = {
'An event handler scheduled a big update with React. Consider using the Transition API to defer some of this work.',
NESTED_UPDATE:
'A nested update was scheduled during layout. These updates require React to re-render synchronously before the browser can paint.',
- SUSPENDD_DURING_UPATE:
+ SUSPEND_DURING_UPATE:
'A component suspended during an update which caused a fallback to be shown. ' +
"Consider using the Transition API to avoid hiding components after they've been mounted.",
};
@@ -80,6 +81,23 @@ export function getLanesFromTransportDecimalBitmask(
return lanes;
}
+const laneToLabelMap: Map
= new Map();
+function updateLaneToLabelMap(laneLabelTuplesString: string): void {
+ // These marks appear multiple times in the data;
+ // We only need to extact them once.
+ if (laneToLabelMap.size === 0) {
+ const laneLabelTuples = laneLabelTuplesString.split(',');
+ for (let laneIndex = 0; laneIndex < laneLabelTuples.length; laneIndex++) {
+ // The numeric lane value (e.g. 64) isn't important.
+ // The profiler parses and stores the lane's position within the bitmap,
+ // (e.g. lane 1 is index 0, lane 16 is index 4).
+ laneToLabelMap.set(laneIndex, laneLabelTuples[laneIndex]);
+ }
+ }
+}
+
+let profilerVersion = null;
+
function getLastType(stack: $PropertyType) {
if (stack.length > 0) {
const {type} = stack[stack.length - 1];
@@ -100,7 +118,6 @@ function markWorkStarted(
type: ReactMeasureType,
startTime: Milliseconds,
lanes: ReactLane[],
- laneLabels: Array,
currentProfilerData: ReactProfilerData,
state: ProcessorState,
) {
@@ -115,7 +132,6 @@ function markWorkStarted(
batchUID,
depth,
lanes,
- laneLabels,
timestamp: startTime,
duration: 0,
});
@@ -242,7 +258,21 @@ function processTimelineEvent(
case 'blink.user_timing':
const startTime = (ts - currentProfilerData.startTime) / 1000;
- if (name.startsWith('--component-render-start-')) {
+ if (name.startsWith('--react-version-')) {
+ const [reactVersion] = name.substr(16).split('-');
+ currentProfilerData.reactVersion = reactVersion;
+ } else if (name.startsWith('--profiler-version-')) {
+ const [versionString] = name.substr(19).split('-');
+ profilerVersion = parseInt(versionString, 10);
+ if (profilerVersion !== SCHEDULING_PROFILER_VERSION) {
+ throw new InvalidProfileError(
+ `This version of profiling data (${versionString}) is not supported by the current profiler.`,
+ );
+ }
+ } else if (name.startsWith('--react-lane-labels-')) {
+ const [laneLabelTuplesString] = name.substr(20).split('-');
+ updateLaneToLabelMap(laneLabelTuplesString);
+ } else if (name.startsWith('--component-render-start-')) {
const [componentName] = name.substr(25).split('-');
if (state.currentReactComponentMeasure !== null) {
@@ -268,48 +298,44 @@ function processTimelineEvent(
currentProfilerData.componentMeasures.push(componentMeasure);
}
} else if (name.startsWith('--schedule-render-')) {
- const [laneBitmaskString, laneLabels] = name.substr(18).split('-');
+ const [laneBitmaskString] = name.substr(18).split('-');
+
currentProfilerData.schedulingEvents.push({
type: 'schedule-render',
lanes: getLanesFromTransportDecimalBitmask(laneBitmaskString),
- laneLabels: laneLabels ? laneLabels.split(',') : [],
timestamp: startTime,
warning: null,
});
} else if (name.startsWith('--schedule-forced-update-')) {
- const [laneBitmaskString, laneLabels, componentName] = name
- .substr(25)
- .split('-');
+ const [laneBitmaskString, componentName] = name.substr(25).split('-');
let warning = null;
if (state.measureStack.find(({type}) => type === 'commit')) {
// TODO (scheduling profiler) Only warn if the subsequent update is longer than some threshold.
+ // This might be easier to do if we separated warnings into a second pass.
warning = WARNING_STRINGS.NESTED_UPDATE;
}
currentProfilerData.schedulingEvents.push({
type: 'schedule-force-update',
lanes: getLanesFromTransportDecimalBitmask(laneBitmaskString),
- laneLabels: laneLabels ? laneLabels.split(',') : [],
componentName,
timestamp: startTime,
warning,
});
} else if (name.startsWith('--schedule-state-update-')) {
- const [laneBitmaskString, laneLabels, componentName] = name
- .substr(24)
- .split('-');
+ const [laneBitmaskString, componentName] = name.substr(24).split('-');
let warning = null;
if (state.measureStack.find(({type}) => type === 'commit')) {
// TODO (scheduling profiler) Only warn if the subsequent update is longer than some threshold.
+ // This might be easier to do if we separated warnings into a second pass.
warning = WARNING_STRINGS.NESTED_UPDATE;
}
currentProfilerData.schedulingEvents.push({
type: 'schedule-state-update',
lanes: getLanesFromTransportDecimalBitmask(laneBitmaskString),
- laneLabels: laneLabels ? laneLabels.split(',') : [],
componentName,
timestamp: startTime,
warning,
@@ -318,25 +344,18 @@ function processTimelineEvent(
// React Events - suspense
else if (name.startsWith('--suspense-suspend-')) {
- const [id, componentName, ...rest] = name.substr(19).split('-');
+ const [id, componentName, phase, laneBitmaskString] = name
+ .substr(19)
+ .split('-');
+ const lanes = getLanesFromTransportDecimalBitmask(laneBitmaskString);
- // Older versions of the scheduling profiler data didn't contain phase or lane values.
- let phase = null;
+ // TODO It's possible we don't have lane-to-label mapping yet (since it's logged during commit phase)
+ // We may need to do this sort of error checking in a separate pass.
let warning = null;
- if (rest.length === 3) {
- switch (rest[0]) {
- case 'mount':
- case 'update':
- phase = rest[0];
- break;
- }
-
- if (phase === 'update') {
- const laneLabels = rest[2];
- // HACK This is a bit gross but the numeric lane value might change between render versions.
- if (!laneLabels.includes('Transition')) {
- warning = WARNING_STRINGS.SUSPENDD_DURING_UPATE;
- }
+ if (phase === 'update') {
+ // HACK This is a bit gross but the numeric lane value might change between render versions.
+ if (lanes.some(lane => laneToLabelMap.get(lane) === 'Transition')) {
+ warning = WARNING_STRINGS.SUSPEND_DURING_UPATE;
}
}
@@ -365,7 +384,7 @@ function processTimelineEvent(
depth,
duration: null,
id,
- phase,
+ phase: ((phase: any): Phase),
resolution: 'unresolved',
resuspendTimestamps: null,
timestamp: startTime,
@@ -411,15 +430,14 @@ function processTimelineEvent(
state.nextRenderShouldGenerateNewBatchID = false;
state.batchUID = ((state.uidCounter++: any): BatchUID);
}
- const [laneBitmaskString, laneLabels] = name.substr(15).split('-');
- const lanes = getLanesFromTransportDecimalBitmask(laneBitmaskString);
+ const [laneBitmaskString] = name.substr(15).split('-');
+
throwIfIncomplete('render', state.measureStack);
if (getLastType(state.measureStack) !== 'render-idle') {
markWorkStarted(
'render-idle',
startTime,
- lanes,
- laneLabels ? laneLabels.split(',') : [],
+ getLanesFromTransportDecimalBitmask(laneBitmaskString),
currentProfilerData,
state,
);
@@ -427,8 +445,7 @@ function processTimelineEvent(
markWorkStarted(
'render',
startTime,
- lanes,
- laneLabels ? laneLabels.split(',') : [],
+ getLanesFromTransportDecimalBitmask(laneBitmaskString),
currentProfilerData,
state,
);
@@ -472,13 +489,12 @@ function processTimelineEvent(
// React Measures - commits
else if (name.startsWith('--commit-start-')) {
state.nextRenderShouldGenerateNewBatchID = true;
- const [laneBitmaskString, laneLabels] = name.substr(15).split('-');
- const lanes = getLanesFromTransportDecimalBitmask(laneBitmaskString);
+ const [laneBitmaskString] = name.substr(15).split('-');
+
markWorkStarted(
'commit',
startTime,
- lanes,
- laneLabels ? laneLabels.split(',') : [],
+ getLanesFromTransportDecimalBitmask(laneBitmaskString),
currentProfilerData,
state,
);
@@ -499,13 +515,12 @@ function processTimelineEvent(
// React Measures - layout effects
else if (name.startsWith('--layout-effects-start-')) {
- const [laneBitmaskString, laneLabels] = name.substr(23).split('-');
- const lanes = getLanesFromTransportDecimalBitmask(laneBitmaskString);
+ const [laneBitmaskString] = name.substr(23).split('-');
+
markWorkStarted(
'layout-effects',
startTime,
- lanes,
- laneLabels ? laneLabels.split(',') : [],
+ getLanesFromTransportDecimalBitmask(laneBitmaskString),
currentProfilerData,
state,
);
@@ -520,13 +535,12 @@ function processTimelineEvent(
// React Measures - passive effects
else if (name.startsWith('--passive-effects-start-')) {
- const [laneBitmaskString, laneLabels] = name.substr(24).split('-');
- const lanes = getLanesFromTransportDecimalBitmask(laneBitmaskString);
+ const [laneBitmaskString] = name.substr(24).split('-');
+
markWorkStarted(
'passive-effects',
startTime,
- lanes,
- laneLabels ? laneLabels.split(',') : [],
+ getLanesFromTransportDecimalBitmask(laneBitmaskString),
currentProfilerData,
state,
);
@@ -610,9 +624,11 @@ export default function preprocessData(
componentMeasures: [],
duration: 0,
flamechart,
+ laneToLabelMap,
measures: [],
nativeEvents: [],
otherUserTimingMarks: [],
+ reactVersion: null,
schedulingEvents: [],
startTime: 0,
suspenseEvents: [],
@@ -656,6 +672,12 @@ export default function preprocessData(
timeline.forEach(event => processTimelineEvent(event, profilerData, state));
+ if (profilerVersion === null) {
+ throw new InvalidProfileError(
+ `This version of profiling data is not supported by the current profiler.`,
+ );
+ }
+
// Validate that all events and measures are complete
const {measureStack} = state;
if (measureStack.length > 0) {
diff --git a/packages/react-devtools-scheduling-profiler/src/types.js b/packages/react-devtools-scheduling-profiler/src/types.js
index 106e4048363a5..edd683043a56e 100644
--- a/packages/react-devtools-scheduling-profiler/src/types.js
+++ b/packages/react-devtools-scheduling-profiler/src/types.js
@@ -38,7 +38,6 @@ type BaseReactEvent = {|
type BaseReactScheduleEvent = {|
...BaseReactEvent,
+lanes: ReactLane[],
- +laneLabels: string[],
|};
export type ReactScheduleRenderEvent = {|
...BaseReactScheduleEvent,
@@ -53,12 +52,14 @@ export type ReactScheduleForceUpdateEvent = {|
+type: 'schedule-force-update',
|};
+export type Phase = 'mount' | 'update';
+
export type SuspenseEvent = {|
...BaseReactEvent,
depth: number,
duration: number | null,
+id: string,
- +phase: 'mount' | 'update' | null,
+ +phase: Phase | null,
resolution: 'rejected' | 'resolved' | 'unresolved',
resuspendTimestamps: Array | null,
+type: 'suspense',
@@ -84,7 +85,6 @@ export type BatchUID = number;
export type ReactMeasure = {|
+type: ReactMeasureType,
+lanes: ReactLane[],
- +laneLabels: string[],
+timestamp: Milliseconds,
+duration: Milliseconds,
+batchUID: BatchUID,
@@ -127,9 +127,11 @@ export type ReactProfilerData = {|
componentMeasures: ReactComponentMeasure[],
duration: number,
flamechart: Flamechart,
+ laneToLabelMap: Map,
measures: ReactMeasure[],
nativeEvents: NativeEvent[],
otherUserTimingMarks: UserTimingMark[],
+ reactVersion: string | null,
schedulingEvents: SchedulingEvent[],
startTime: number,
suspenseEvents: SuspenseEvent[],
diff --git a/packages/react-devtools-scheduling-profiler/src/view-base/ResizableView.js b/packages/react-devtools-scheduling-profiler/src/view-base/ResizableView.js
index 1b99d20a54c3a..8394bcf105770 100644
--- a/packages/react-devtools-scheduling-profiler/src/view-base/ResizableView.js
+++ b/packages/react-devtools-scheduling-profiler/src/view-base/ResizableView.js
@@ -105,14 +105,10 @@ class ResizeBar extends View {
const drawableRect = intersectionOfRects(labelRect, this.visibleArea);
- drawText(
- this._label,
- context,
- labelRect,
- drawableRect,
- 'center',
- COLORS.REACT_RESIZE_BAR_DOT,
- );
+ drawText(this._label, context, labelRect, drawableRect, {
+ fillStyle: COLORS.REACT_RESIZE_BAR_DOT,
+ textAlign: 'center',
+ });
} else {
// Otherwise draw horizontally centered resize bar dots
context.beginPath();
diff --git a/packages/react-devtools-shared/src/constants.js b/packages/react-devtools-shared/src/constants.js
index 8afcf85d41519..2476ce32839d3 100644
--- a/packages/react-devtools-shared/src/constants.js
+++ b/packages/react-devtools-shared/src/constants.js
@@ -149,12 +149,16 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
'--color-scheduling-profiler-react-idle-hover': '#c3d9ef',
'--color-scheduling-profiler-react-render': '#9fc3f3',
'--color-scheduling-profiler-react-render-hover': '#83afe9',
+ '--color-scheduling-profiler-react-render-text': '#11365e',
'--color-scheduling-profiler-react-commit': '#c88ff0',
'--color-scheduling-profiler-react-commit-hover': '#b281d6',
+ '--color-scheduling-profiler-react-commit-text': '#3e2c4a',
'--color-scheduling-profiler-react-layout-effects': '#b281d6',
'--color-scheduling-profiler-react-layout-effects-hover': '#9d71bd',
+ '--color-scheduling-profiler-react-layout-effects-text': '#3e2c4a',
'--color-scheduling-profiler-react-passive-effects': '#b281d6',
'--color-scheduling-profiler-react-passive-effects-hover': '#9d71bd',
+ '--color-scheduling-profiler-react-passive-effects-text': '#3e2c4a',
'--color-scheduling-profiler-react-schedule': '#9fc3f3',
'--color-scheduling-profiler-react-schedule-hover': '#2683E2',
'--color-scheduling-profiler-react-suspense-rejected': '#f1cc14',
@@ -280,12 +284,16 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
'--color-scheduling-profiler-react-idle-hover': '#465269',
'--color-scheduling-profiler-react-render': '#2683E2',
'--color-scheduling-profiler-react-render-hover': '#1a76d4',
+ '--color-scheduling-profiler-react-render-text': '#11365e',
'--color-scheduling-profiler-react-commit': '#731fad',
'--color-scheduling-profiler-react-commit-hover': '#611b94',
+ '--color-scheduling-profiler-react-commit-text': '#e5c1ff',
'--color-scheduling-profiler-react-layout-effects': '#611b94',
'--color-scheduling-profiler-react-layout-effects-hover': '#51167a',
+ '--color-scheduling-profiler-react-layout-effects-text': '#e5c1ff',
'--color-scheduling-profiler-react-passive-effects': '#611b94',
'--color-scheduling-profiler-react-passive-effects-hover': '#51167a',
+ '--color-scheduling-profiler-react-passive-effects-text': '#e5c1ff',
'--color-scheduling-profiler-react-schedule': '#2683E2',
'--color-scheduling-profiler-react-schedule-hover': '#1a76d4',
'--color-scheduling-profiler-react-suspense-rejected': '#f1cc14',
diff --git a/packages/react-reconciler/src/ReactFiberLane.new.js b/packages/react-reconciler/src/ReactFiberLane.new.js
index 59e04b1865d4d..2ad6b6a69a495 100644
--- a/packages/react-reconciler/src/ReactFiberLane.new.js
+++ b/packages/react-reconciler/src/ReactFiberLane.new.js
@@ -25,7 +25,7 @@ import {
import {isDevToolsPresent} from './ReactFiberDevToolsHook.new';
import {ConcurrentUpdatesByDefaultMode, NoMode} from './ReactTypeOfMode';
-// Lane values below should be kept in sync with getLabelsForLanes(), used by react-devtools-scheduling-profiler.
+// Lane values below should be kept in sync with getLabelForLane(), used by react-devtools-scheduling-profiler.
// If those values are changed that package should be rebuilt and redeployed.
export const TotalLanes = 31;
@@ -80,46 +80,44 @@ export const OffscreenLane: Lane = /* */ 0b1000000000000000000
// This function is used for the experimental scheduling profiler (react-devtools-scheduling-profiler)
// It should be kept in sync with the Lanes values above.
-export function getLabelsForLanes(lanes: Lanes): Array | void {
+export function getLabelForLane(lane: Lane): string | void {
if (enableSchedulingProfiler) {
- const labels = [];
- if (lanes & SyncLane) {
- labels.push('Sync');
+ if (lane & SyncLane) {
+ return 'Sync';
}
- if (lanes & InputContinuousHydrationLane) {
- labels.push('InputContinuousHydration');
+ if (lane & InputContinuousHydrationLane) {
+ return 'InputContinuousHydration';
}
- if (lanes & InputContinuousLane) {
- labels.push('InputContinuous');
+ if (lane & InputContinuousLane) {
+ return 'InputContinuous';
}
- if (lanes & DefaultHydrationLane) {
- labels.push('DefaultHydration');
+ if (lane & DefaultHydrationLane) {
+ return 'DefaultHydration';
}
- if (lanes & DefaultLane) {
- labels.push('Default');
+ if (lane & DefaultLane) {
+ return 'Default';
}
- if (lanes & TransitionHydrationLane) {
- labels.push('TransitionHydration');
+ if (lane & TransitionHydrationLane) {
+ return 'TransitionHydration';
}
- if (lanes & TransitionLanes) {
- labels.push('Transition(s)');
+ if (lane & TransitionLanes) {
+ return 'Transition';
}
- if (lanes & RetryLanes) {
- labels.push('Retry(s)');
+ if (lane & RetryLanes) {
+ return 'Retry';
}
- if (lanes & SelectiveHydrationLane) {
- labels.push('SelectiveHydration');
+ if (lane & SelectiveHydrationLane) {
+ return 'SelectiveHydration';
}
- if (lanes & IdleHydrationLane) {
- labels.push('IdleHydration');
+ if (lane & IdleHydrationLane) {
+ return 'IdleHydration';
}
- if (lanes & IdleLane) {
- labels.push('Idle');
+ if (lane & IdleLane) {
+ return 'Idle';
}
- if (lanes & OffscreenLane) {
- labels.push('Offscreen');
+ if (lane & OffscreenLane) {
+ return 'Offscreen';
}
- return labels;
}
}
diff --git a/packages/react-reconciler/src/ReactFiberLane.old.js b/packages/react-reconciler/src/ReactFiberLane.old.js
index 458d3ec6290ec..3e704f54e6761 100644
--- a/packages/react-reconciler/src/ReactFiberLane.old.js
+++ b/packages/react-reconciler/src/ReactFiberLane.old.js
@@ -25,7 +25,7 @@ import {
import {isDevToolsPresent} from './ReactFiberDevToolsHook.old';
import {ConcurrentUpdatesByDefaultMode, NoMode} from './ReactTypeOfMode';
-// Lane values below should be kept in sync with getLabelsForLanes(), used by react-devtools-scheduling-profiler.
+// Lane values below should be kept in sync with getLabelForLane(), used by react-devtools-scheduling-profiler.
// If those values are changed that package should be rebuilt and redeployed.
export const TotalLanes = 31;
@@ -80,46 +80,44 @@ export const OffscreenLane: Lane = /* */ 0b1000000000000000000
// This function is used for the experimental scheduling profiler (react-devtools-scheduling-profiler)
// It should be kept in sync with the Lanes values above.
-export function getLabelsForLanes(lanes: Lanes): Array | void {
+export function getLabelForLane(lane: Lane): string | void {
if (enableSchedulingProfiler) {
- const labels = [];
- if (lanes & SyncLane) {
- labels.push('Sync');
+ if (lane & SyncLane) {
+ return 'Sync';
}
- if (lanes & InputContinuousHydrationLane) {
- labels.push('InputContinuousHydration');
+ if (lane & InputContinuousHydrationLane) {
+ return 'InputContinuousHydration';
}
- if (lanes & InputContinuousLane) {
- labels.push('InputContinuous');
+ if (lane & InputContinuousLane) {
+ return 'InputContinuous';
}
- if (lanes & DefaultHydrationLane) {
- labels.push('DefaultHydration');
+ if (lane & DefaultHydrationLane) {
+ return 'DefaultHydration';
}
- if (lanes & DefaultLane) {
- labels.push('Default');
+ if (lane & DefaultLane) {
+ return 'Default';
}
- if (lanes & TransitionHydrationLane) {
- labels.push('TransitionHydration');
+ if (lane & TransitionHydrationLane) {
+ return 'TransitionHydration';
}
- if (lanes & TransitionLanes) {
- labels.push('Transition(s)');
+ if (lane & TransitionLanes) {
+ return 'Transition';
}
- if (lanes & RetryLanes) {
- labels.push('Retry(s)');
+ if (lane & RetryLanes) {
+ return 'Retry';
}
- if (lanes & SelectiveHydrationLane) {
- labels.push('SelectiveHydration');
+ if (lane & SelectiveHydrationLane) {
+ return 'SelectiveHydration';
}
- if (lanes & IdleHydrationLane) {
- labels.push('IdleHydration');
+ if (lane & IdleHydrationLane) {
+ return 'IdleHydration';
}
- if (lanes & IdleLane) {
- labels.push('Idle');
+ if (lane & IdleLane) {
+ return 'Idle';
}
- if (lanes & OffscreenLane) {
- labels.push('Offscreen');
+ if (lane & OffscreenLane) {
+ return 'Offscreen';
}
- return labels;
}
}
diff --git a/packages/react-reconciler/src/SchedulingProfiler.js b/packages/react-reconciler/src/SchedulingProfiler.js
index fc7a7499c6014..b4e7f83d3a157 100644
--- a/packages/react-reconciler/src/SchedulingProfiler.js
+++ b/packages/react-reconciler/src/SchedulingProfiler.js
@@ -17,13 +17,22 @@ import {
} from 'shared/ReactFeatureFlags';
import ReactVersion from 'shared/ReactVersion';
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
+import {SCHEDULING_PROFILER_VERSION} from 'react-devtools-scheduling-profiler/src/constants';
-import {getLabelsForLanes as getLabelsForLanes_old} from 'react-reconciler/src/ReactFiberLane.old';
-import {getLabelsForLanes as getLabelsForLanes_new} from 'react-reconciler/src/ReactFiberLane.new';
+import {
+ getLabelForLane as getLabelForLane_old,
+ TotalLanes as TotalLanes_old,
+} from 'react-reconciler/src/ReactFiberLane.old';
+import {
+ getLabelForLane as getLabelForLane_new,
+ TotalLanes as TotalLanes_new,
+} from 'react-reconciler/src/ReactFiberLane.new';
-const getLabelsForLanes = enableNewReconciler
- ? getLabelsForLanes_new
- : getLabelsForLanes_old;
+const getLabelForLane = enableNewReconciler
+ ? getLabelForLane_new
+ : getLabelForLane_old;
+
+const TotalLanes = enableNewReconciler ? TotalLanes_new : TotalLanes_old;
/**
* If performance exists and supports the subset of the User Timing API that we
@@ -59,14 +68,24 @@ if (enableSchedulingProfiler) {
}
}
-export function formatLanes(laneOrLanes: Lane | Lanes): string {
- let labels = getLabelsForLanes(laneOrLanes);
- if (labels != null) {
- labels = labels.sort().join(',');
- } else {
- labels = '';
+const laneLabels: Array = [];
+
+export function getLaneLabels(): Array {
+ if (laneLabels.length === 0) {
+ let lane = 1;
+ for (let index = 0; index < TotalLanes; index++) {
+ laneLabels.push(((getLabelForLane(lane): any): string));
+
+ lane *= 2;
+ }
}
- return `${laneOrLanes}-${labels}`;
+ return laneLabels;
+}
+
+function markLaneToLabelMetadata() {
+ getLaneLabels();
+
+ markAndClear(`--react-lane-labels-${laneLabels.join(',')}`);
}
function markAndClear(name) {
@@ -74,17 +93,27 @@ function markAndClear(name) {
performance.clearMarks(name);
}
-// Create a mark on React initialization
-if (enableSchedulingProfiler) {
- if (supportsUserTimingV3) {
- markAndClear(`--react-init-${ReactVersion}`);
- }
+function markVersionMetadata() {
+ markAndClear(`--react-version-${ReactVersion}`);
+ markAndClear(`--profiler-version-${SCHEDULING_PROFILER_VERSION}`);
}
export function markCommitStarted(lanes: Lanes): void {
if (enableSchedulingProfiler) {
if (supportsUserTimingV3) {
- markAndClear(`--commit-start-${formatLanes(lanes)}`);
+ markAndClear(`--commit-start-${lanes}`);
+
+ // Certain types of metadata should be logged infrequently.
+ // Normally we would log this during module init,
+ // but there's no guarantee a user is profiling at that time.
+ // Commits happen infrequently (less than renders or state updates)
+ // so we log this extra information along with a commit.
+ // It will likely be logged more than once but that's okay.
+ //
+ // TODO Once DevTools supports starting/stopping the profiler,
+ // we can log this data only once (when started) and remove the per-commit logging.
+ markVersionMetadata();
+ markLaneToLabelMetadata();
}
}
}
@@ -101,6 +130,7 @@ export function markComponentRenderStarted(fiber: Fiber): void {
if (enableSchedulingProfiler) {
if (supportsUserTimingV3) {
const componentName = getComponentNameFromFiber(fiber) || 'Unknown';
+ // TODO (scheduling profiler) Add component stack id
markAndClear(`--component-render-start-${componentName}`);
}
}
@@ -137,11 +167,9 @@ export function markComponentSuspended(
const id = getWakeableID(wakeable);
const componentName = getComponentNameFromFiber(fiber) || 'Unknown';
const phase = fiber.alternate === null ? 'mount' : 'update';
- // TODO (scheduling profiler) Add component stack id if we re-add component stack info.
+ // TODO (scheduling profiler) Add component stack id
markAndClear(
- `--suspense-${eventType}-${id}-${componentName}-${phase}-${formatLanes(
- lanes,
- )}`,
+ `--suspense-${eventType}-${id}-${componentName}-${phase}-${lanes}`,
);
wakeable.then(
() => markAndClear(`--suspense-resolved-${id}-${componentName}`),
@@ -154,7 +182,7 @@ export function markComponentSuspended(
export function markLayoutEffectsStarted(lanes: Lanes): void {
if (enableSchedulingProfiler) {
if (supportsUserTimingV3) {
- markAndClear(`--layout-effects-start-${formatLanes(lanes)}`);
+ markAndClear(`--layout-effects-start-${lanes}`);
}
}
}
@@ -170,7 +198,7 @@ export function markLayoutEffectsStopped(): void {
export function markPassiveEffectsStarted(lanes: Lanes): void {
if (enableSchedulingProfiler) {
if (supportsUserTimingV3) {
- markAndClear(`--passive-effects-start-${formatLanes(lanes)}`);
+ markAndClear(`--passive-effects-start-${lanes}`);
}
}
}
@@ -186,7 +214,7 @@ export function markPassiveEffectsStopped(): void {
export function markRenderStarted(lanes: Lanes): void {
if (enableSchedulingProfiler) {
if (supportsUserTimingV3) {
- markAndClear(`--render-start-${formatLanes(lanes)}`);
+ markAndClear(`--render-start-${lanes}`);
}
}
}
@@ -210,7 +238,7 @@ export function markRenderStopped(): void {
export function markRenderScheduled(lane: Lane): void {
if (enableSchedulingProfiler) {
if (supportsUserTimingV3) {
- markAndClear(`--schedule-render-${formatLanes(lane)}`);
+ markAndClear(`--schedule-render-${lane}`);
}
}
}
@@ -219,10 +247,8 @@ export function markForceUpdateScheduled(fiber: Fiber, lane: Lane): void {
if (enableSchedulingProfiler) {
if (supportsUserTimingV3) {
const componentName = getComponentNameFromFiber(fiber) || 'Unknown';
- // TODO Add component stack id
- markAndClear(
- `--schedule-forced-update-${formatLanes(lane)}-${componentName}`,
- );
+ // TODO (scheduling profiler) Add component stack id
+ markAndClear(`--schedule-forced-update-${lane}-${componentName}`);
}
}
}
@@ -231,10 +257,8 @@ export function markStateUpdateScheduled(fiber: Fiber, lane: Lane): void {
if (enableSchedulingProfiler) {
if (supportsUserTimingV3) {
const componentName = getComponentNameFromFiber(fiber) || 'Unknown';
- // TODO Add component stack id
- markAndClear(
- `--schedule-state-update-${formatLanes(lane)}-${componentName}`,
- );
+ // TODO (scheduling profiler) Add component stack id
+ markAndClear(`--schedule-state-update-${lane}-${componentName}`);
}
}
}
diff --git a/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js b/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js
index 4b496b732a21d..8680d19b710bf 100644
--- a/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js
@@ -10,17 +10,6 @@
'use strict';
-// This test is *.internal so that it can import this shared file.
-import ReactVersion from 'shared/ReactVersion';
-
-// Hard-coding because importing will not work with bundle tests and to
-// avoid leaking exports for lanes that are only imported in this test.
-const ReactFiberLane = {
- SyncLane: /* */ 0b0000000000000000000000000000001,
- DefaultLane: /* */ 0b0000000000000000000000000010000,
- TransitionLane1: /* */ 0b0000000000000000000000001000000,
-};
-
describe('SchedulingProfiler', () => {
let React;
let ReactTestRenderer;
@@ -30,7 +19,6 @@ describe('SchedulingProfiler', () => {
let clearedMarks;
let featureDetectionMarkName = null;
- let formatLanes;
let marks;
function createUserTimingPolyfill() {
@@ -63,16 +51,10 @@ describe('SchedulingProfiler', () => {
clearedMarks.splice(0);
}
- function expectMarksToContain(expectedMarks) {
- expect(clearedMarks).toContain(expectedMarks);
- }
-
- function expectMarksToEqual(expectedMarks) {
- expect(
- clearedMarks[0] === featureDetectionMarkName
- ? clearedMarks.slice(1)
- : clearedMarks,
- ).toEqual(expectedMarks);
+ function getMarks() {
+ return clearedMarks[0] === featureDetectionMarkName
+ ? clearedMarks.slice(1)
+ : clearedMarks;
}
beforeEach(() => {
@@ -88,9 +70,6 @@ describe('SchedulingProfiler', () => {
Scheduler = require('scheduler');
act = require('jest-react').act;
-
- const SchedulingProfiler = require('react-reconciler/src/SchedulingProfiler');
- formatLanes = SchedulingProfiler.formatLanes;
});
afterEach(() => {
@@ -103,54 +82,62 @@ describe('SchedulingProfiler', () => {
// @gate !enableSchedulingProfiler
it('should not mark if enableSchedulingProfiler is false', () => {
ReactTestRenderer.create();
- expectMarksToEqual([]);
+ expect(getMarks()).toEqual([]);
});
- // @gate enableSchedulingProfiler
- it('should log React version on initialization', () => {
- expectMarksToEqual([`--react-init-${ReactVersion}`]);
- });
-
- // @gate enableSchedulingProfiler
it('should mark sync render without suspends or state updates', () => {
ReactTestRenderer.create();
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`,
- `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- `--layout-effects-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--layout-effects-stop',
- '--commit-stop',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-1",
+ "--render-start-1",
+ "--render-stop",
+ "--commit-start-1",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-1",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark concurrent render without suspends or state updates', () => {
ReactTestRenderer.create(, {unstable_isConcurrent: true});
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-16",
+ ]
+ `);
+ }
clearPendingMarks();
expect(Scheduler).toFlushUntilNextPaint([]);
- expectMarksToEqual([
- `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--layout-effects-stop',
- '--commit-stop',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--render-start-16",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-16",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark render yields', async () => {
function Bar() {
Scheduler.unstable_yieldValue('Bar');
@@ -170,30 +157,31 @@ describe('SchedulingProfiler', () => {
// Do one step of work.
expect(ReactNoop.flushNextYield()).toEqual(['Foo']);
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.TransitionLane1)}`,
- `--render-start-${formatLanes(ReactFiberLane.TransitionLane1)}`,
- '--component-render-start-Foo',
- '--component-render-stop',
- '--render-yield',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-64",
+ "--render-start-64",
+ "--component-render-start-Foo",
+ "--component-render-stop",
+ "--render-yield",
+ ]
+ `);
+ }
} else {
ReactNoop.render();
// Do one step of work.
expect(ReactNoop.flushNextYield()).toEqual(['Foo']);
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--render-yield',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array []
+ `);
+ }
}
});
- // @gate enableSchedulingProfiler
it('should mark sync render with suspense that resolves', async () => {
const fakeSuspensePromise = Promise.resolve(true);
function Example() {
@@ -206,27 +194,38 @@ describe('SchedulingProfiler', () => {
,
);
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`,
- `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--suspense-suspend-0-Example-mount-1-Sync',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- `--layout-effects-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--layout-effects-stop',
- '--commit-stop',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-1",
+ "--render-start-1",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--suspense-suspend-0-Example-mount-1",
+ "--render-stop",
+ "--commit-start-1",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-1",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
clearPendingMarks();
await fakeSuspensePromise;
- expectMarksToEqual(['--suspense-resolved-0-Example']);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--suspense-resolved-0-Example",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark sync render with suspense that rejects', async () => {
const fakeSuspensePromise = Promise.reject(new Error('error'));
function Example() {
@@ -239,27 +238,38 @@ describe('SchedulingProfiler', () => {
,
);
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`,
- `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--suspense-suspend-0-Example-mount-1-Sync',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- `--layout-effects-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--layout-effects-stop',
- '--commit-stop',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-1",
+ "--render-start-1",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--suspense-suspend-0-Example-mount-1",
+ "--render-stop",
+ "--commit-start-1",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-1",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
clearPendingMarks();
await expect(fakeSuspensePromise).rejects.toThrow();
- expectMarksToEqual(['--suspense-rejected-0-Example']);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--suspense-rejected-0-Example",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark concurrent render with suspense that resolves', async () => {
const fakeSuspensePromise = Promise.resolve(true);
function Example() {
@@ -273,34 +283,49 @@ describe('SchedulingProfiler', () => {
{unstable_isConcurrent: true},
);
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-16",
+ ]
+ `);
+ }
clearPendingMarks();
expect(Scheduler).toFlushUntilNextPaint([]);
- expectMarksToEqual([
- `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--suspense-suspend-0-Example-mount-16-Default',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--layout-effects-stop',
- '--commit-stop',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--render-start-16",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--suspense-suspend-0-Example-mount-16",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-16",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
clearPendingMarks();
await fakeSuspensePromise;
- expectMarksToEqual(['--suspense-resolved-0-Example']);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--suspense-resolved-0-Example",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark concurrent render with suspense that rejects', async () => {
const fakeSuspensePromise = Promise.reject(new Error('error'));
function Example() {
@@ -314,34 +339,49 @@ describe('SchedulingProfiler', () => {
{unstable_isConcurrent: true},
);
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-16",
+ ]
+ `);
+ }
clearPendingMarks();
expect(Scheduler).toFlushUntilNextPaint([]);
- expectMarksToEqual([
- `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--suspense-suspend-0-Example-mount-16-Default',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--layout-effects-stop',
- '--commit-stop',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--render-start-16",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--suspense-suspend-0-Example-mount-16",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-16",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
clearPendingMarks();
await expect(fakeSuspensePromise).rejects.toThrow();
- expectMarksToEqual(['--suspense-rejected-0-Example']);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--suspense-rejected-0-Example",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark cascading class component state updates', () => {
class Example extends React.Component {
state = {didMount: false};
@@ -355,35 +395,47 @@ describe('SchedulingProfiler', () => {
ReactTestRenderer.create(, {unstable_isConcurrent: true});
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-16",
+ ]
+ `);
+ }
clearPendingMarks();
expect(Scheduler).toFlushUntilNextPaint([]);
- expectMarksToEqual([
- `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--schedule-state-update-${formatLanes(ReactFiberLane.SyncLane)}-Example`,
- '--layout-effects-stop',
- `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--commit-stop',
- '--commit-stop',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--render-start-16",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-16",
+ "--schedule-state-update-1-Example",
+ "--layout-effects-stop",
+ "--render-start-1",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-1",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--commit-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark cascading class component force updates', () => {
class Example extends React.Component {
componentDidMount() {
@@ -396,37 +448,47 @@ describe('SchedulingProfiler', () => {
ReactTestRenderer.create(, {unstable_isConcurrent: true});
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-16",
+ ]
+ `);
+ }
clearPendingMarks();
expect(Scheduler).toFlushUntilNextPaint([]);
- expectMarksToEqual([
- `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--schedule-forced-update-${formatLanes(
- ReactFiberLane.SyncLane,
- )}-Example`,
- '--layout-effects-stop',
- `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--commit-stop',
- '--commit-stop',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--render-start-16",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-16",
+ "--schedule-forced-update-1-Example",
+ "--layout-effects-stop",
+ "--render-start-1",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-1",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--commit-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark render phase state updates for class component', () => {
class Example extends React.Component {
state = {didRender: false};
@@ -440,10 +502,13 @@ describe('SchedulingProfiler', () => {
ReactTestRenderer.create(, {unstable_isConcurrent: true});
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-16",
+ ]
+ `);
+ }
clearPendingMarks();
@@ -451,14 +516,26 @@ describe('SchedulingProfiler', () => {
expect(Scheduler).toFlushUntilNextPaint([]);
}).toErrorDev('Cannot update during an existing state transition');
- expectMarksToContain(
- `--schedule-state-update-${formatLanes(
- ReactFiberLane.DefaultLane,
- )}-Example`,
- );
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--render-start-16",
+ "--component-render-start-Example",
+ "--schedule-state-update-16-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-16",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark render phase force updates for class component', () => {
class Example extends React.Component {
state = {didRender: false};
@@ -472,10 +549,13 @@ describe('SchedulingProfiler', () => {
ReactTestRenderer.create(, {unstable_isConcurrent: true});
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-16",
+ ]
+ `);
+ }
clearPendingMarks();
@@ -483,14 +563,26 @@ describe('SchedulingProfiler', () => {
expect(Scheduler).toFlushUntilNextPaint([]);
}).toErrorDev('Cannot update during an existing state transition');
- expectMarksToContain(
- `--schedule-forced-update-${formatLanes(
- ReactFiberLane.DefaultLane,
- )}-Example`,
- );
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--render-start-16",
+ "--component-render-start-Example",
+ "--schedule-forced-update-16-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-16",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark cascading layout updates', () => {
function Example() {
const [didMount, setDidMount] = React.useState(false);
@@ -502,37 +594,49 @@ describe('SchedulingProfiler', () => {
ReactTestRenderer.create(, {unstable_isConcurrent: true});
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-16",
+ ]
+ `);
+ }
clearPendingMarks();
expect(Scheduler).toFlushUntilNextPaint([]);
- expectMarksToEqual([
- `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--schedule-state-update-${formatLanes(ReactFiberLane.SyncLane)}-Example`,
- '--layout-effects-stop',
- `--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
- '--commit-stop',
- '--commit-stop',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--render-start-16",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-16",
+ "--schedule-state-update-1-Example",
+ "--layout-effects-stop",
+ "--render-start-1",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-1",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--commit-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
// This test is coupled to lane implementation details, so I'm disabling it in
// the new fork until it stabilizes so we don't have to repeatedly update it.
- // @gate enableSchedulingProfiler
it('should mark cascading passive updates', () => {
function Example() {
const [didMount, setDidMount] = React.useState(false);
@@ -546,32 +650,38 @@ describe('SchedulingProfiler', () => {
ReactTestRenderer.create(, {unstable_isConcurrent: true});
});
- expectMarksToEqual([
- `--react-init-${ReactVersion}`,
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--layout-effects-stop',
- '--commit-stop',
- `--passive-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- `--schedule-state-update-${formatLanes(
- ReactFiberLane.DefaultLane,
- )}-Example`,
- '--passive-effects-stop',
- `--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--component-render-start-Example',
- '--component-render-stop',
- '--render-stop',
- `--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
- '--commit-stop',
- ]);
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-16",
+ "--render-start-16",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-16",
+ "--layout-effects-stop",
+ "--commit-stop",
+ "--passive-effects-start-16",
+ "--schedule-state-update-16-Example",
+ "--passive-effects-stop",
+ "--render-start-16",
+ "--component-render-start-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--commit-stop",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('should mark render phase updates', () => {
function Example() {
const [didRender, setDidRender] = React.useState(false);
@@ -585,10 +695,24 @@ describe('SchedulingProfiler', () => {
ReactTestRenderer.create(, {unstable_isConcurrent: true});
});
- expectMarksToContain(
- `--schedule-state-update-${formatLanes(
- ReactFiberLane.DefaultLane,
- )}-Example`,
- );
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(getMarks()).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-render-16",
+ "--render-start-16",
+ "--component-render-start-Example",
+ "--schedule-state-update-16-Example",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-16",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-16",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
});
diff --git a/packages/react-reconciler/src/__tests__/SchedulingProfilerLabels-test.internal.js b/packages/react-reconciler/src/__tests__/SchedulingProfilerLabels-test.internal.js
index 226607eaed2e2..09fdb95b01e6c 100644
--- a/packages/react-reconciler/src/__tests__/SchedulingProfilerLabels-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/SchedulingProfilerLabels-test.internal.js
@@ -15,12 +15,10 @@
describe('SchedulingProfiler labels', () => {
let React;
let ReactDOM;
- let ReactFiberLane;
let act;
let clearedMarks;
let featureDetectionMarkName = null;
- let formatLanes;
let marks;
function polyfillJSDomUserTiming() {
@@ -75,14 +73,6 @@ describe('SchedulingProfiler labels', () => {
const TestUtils = require('react-dom/test-utils');
act = TestUtils.act;
-
- 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(() => {
@@ -92,29 +82,45 @@ describe('SchedulingProfiler labels', () => {
delete global.performance;
});
- // @gate enableSchedulingProfiler
it('regression test SyncLane', () => {
ReactDOM.render(, document.createElement('div'));
- expect(clearedMarks).toContain(
- `--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`,
- );
+
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ expect(clearedMarks).toMatchInlineSnapshot(`
+ Array [
+ "__v3",
+ "--schedule-render-1",
+ "--render-start-1",
+ "--render-stop",
+ "--commit-start-1",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-1",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('regression test DefaultLane', () => {
- const container = document.createElement('div');
- const root = ReactDOM.createRoot(container);
-
- act(() => {
- root.render();
- expect(clearedMarks).toContain(
- `--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
- );
- });
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ act(() => {
+ const container = document.createElement('div');
+ const root = ReactDOM.createRoot(container);
+
+ root.render();
+ expect(clearedMarks).toMatchInlineSnapshot(`
+ Array [
+ "__v3",
+ "--schedule-render-16",
+ ]
+ `);
+ });
+ }
});
- // @gate enableSchedulingProfiler
- // @gate !enableLegacyFBSupport
it('regression test InputDiscreteLane', () => {
const container = document.createElement('div');
const root = ReactDOM.createRoot(container);
@@ -128,24 +134,41 @@ describe('SchedulingProfiler labels', () => {
return ;
}
- act(() => {
- root.render();
- });
-
- clearedMarks.splice(0);
-
- act(() => {
- targetRef.current.click();
- });
- expect(clearedMarks).toContain(
- `--schedule-state-update-${formatLanes(ReactFiberLane.SyncLane)}-App`,
- );
+ if (
+ gate(
+ flags => flags.enableSchedulingProfiler && !flags.enableLegacyFBSupport,
+ )
+ ) {
+ act(() => {
+ root.render();
+ });
+
+ clearedMarks.splice(0);
+
+ act(() => {
+ targetRef.current.click();
+ });
+
+ expect(clearedMarks).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-state-update-1-App",
+ "--render-start-1",
+ "--component-render-start-App",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-1",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-1",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
- // @gate enableSchedulingProfiler
it('regression test InputContinuousLane', () => {
- const container = document.createElement('div');
- const root = ReactDOM.createRoot(container);
const targetRef = React.createRef(null);
function App() {
@@ -154,21 +177,38 @@ describe('SchedulingProfiler labels', () => {
return ;
}
- act(() => {
- root.render();
- });
-
- clearedMarks.splice(0);
-
- act(() => {
- const event = document.createEvent('MouseEvents');
- event.initEvent('mouseover', true, true);
- dispatchAndSetCurrentEvent(targetRef.current, event);
- });
- expect(clearedMarks).toContain(
- `--schedule-state-update-${formatLanes(
- ReactFiberLane.InputContinuousLane,
- )}-App`,
- );
+ if (gate(flags => flags.enableSchedulingProfiler)) {
+ const container = document.createElement('div');
+ const root = ReactDOM.createRoot(container);
+
+ act(() => {
+ root.render();
+ });
+
+ clearedMarks.splice(0);
+
+ act(() => {
+ const event = document.createEvent('MouseEvents');
+ event.initEvent('mouseover', true, true);
+ dispatchAndSetCurrentEvent(targetRef.current, event);
+ });
+
+ expect(clearedMarks).toMatchInlineSnapshot(`
+ Array [
+ "--schedule-state-update-4-App",
+ "--render-start-4",
+ "--component-render-start-App",
+ "--component-render-stop",
+ "--render-stop",
+ "--commit-start-4",
+ "--react-version-17.0.3",
+ "--profiler-version-1",
+ "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen",
+ "--layout-effects-start-4",
+ "--layout-effects-stop",
+ "--commit-stop",
+ ]
+ `);
+ }
});
});